Reference
The REST API.
Thin wrappers over the same functions the dashboard calls, so a behavior cannot exist in one and
not the other. Base URL https://app.getsendframe.com/api/v1. GET /api/v1 returns this route
table as JSON.
Authentication and scopes
API keys and nothing else: Authorization: Bearer sf_live_…. A dashboard session never works on the
API, and a key never works on the dashboard. Keys are minted at
Settings → API keys and shown once.
read Every GET.
send Every write, including upload and process. A leaked read key cannot consume storage, start conversions, or mail anything to a customer.
Conventions
Requests camelCase. Unknown fields are REFUSED, not ignored — and on the multipart uploads, an unrecognized PART is refused the same way.
Responses camelCase.
Upload is multipart only. A
filepart and an optionaltitle; any other part is refused.processmust run beforeshare: upload leaves an artifactpendingon purpose.notifydefaults to true. A share emails its recipients unless told not to.
Errors
Every refusal is JSON with an error code and, where there is something to do, a
message saying what. Render the message rather than mapping the code to your own copy; it is
kept in step with what the server actually did.
401 unauthorizedNo key, or a revoked one.403 insufficient_scopeThe route needssendand the key hasread.403 account_closedsays the organization is closed and how to reopen it.400 unknown_fieldsNames the fields, the allowed ones, and the spelling you probably meant.4xx integrity_*The artifact is not sent, masked or otherwise: malware, unprocessed, or zero pages. The message names the exact call to make next.429 rate_limitedwith ascope.burstis a short ceiling (100 requests a minute per key; waitretryAfterSecondsand continue).quotais the day's allowance of artifact operations (30 on free, 200 on paid); nothing frees up untilresetsAt, so stop and tell the person who asked.503 database_timeoutTransient, withRetry-After. On a write it also saysretrySafeand, when it cannot be sure,verifyWith: the read that shows whether the send happened.
Retries and idempotency
Both send routes accept an optional idempotencyKey (8–255 characters, yours to choose). The same key replays the original send — 200 with replayed: true instead of 201 — and also finishes a send that was interrupted partway through emailing. Reusing a key for a different request is a 409. KEYS STOP DEDUPLICATING AFTER 24 HOURS.
Artifacts
-
GET /artifacts read
Every artifact, with status and finding counts. Start here.
-
POST /artifacts send
Upload. multipart/form-data with a
filepart; optionaltitle. Those are the only parts accepted — an unrecognized one is REFUSED, not ignored, the same as an unknown JSON field. -
GET /artifacts/{id} read
One artifact and where it stands: its status, its versions with the served one marked, findings by severity, and the shares sent from it. The single call that answers "can I send this yet, and what is in it" — the id-shaped counterpart to GET /artifacts, which every other artifact route already required and this one did not answer.
-
POST /artifacts/{id}/process send
Convert, scan and detect the NEWEST version. REQUIRED before an artifact can be shared. ASYNCHRONOUS: it returns without waiting, the version stays
pending, and you learn the outcome by polling GET /artifacts/{id}/versions forreadyorfailed. Do not treat a response as a finished conversion. -
POST /artifacts/{id}/versions send
Upload a new version of an existing artifact, so that shares already sent can serve it without a new link. multipart/form-data with a
filepart. Like the first upload it leaves the versionpending: call process, poll GET /artifacts/{id}/versions until it isready, then release it. Existing recipients, their verification and the state they wrote all carry over. -
GET /artifacts/{id}/versions read
Every version of one artifact, newest first, saying which one recipients are being served right now.
-
GET /artifacts/{id}/findings read
The safety review, and what is currently redacted.
-
POST /artifacts/{id}/findings/{findingId}/unredact send
Clear one redaction, so the recipient sees it from now on.
-
POST /artifacts/{id}/findings/{findingId}/redact send
Hide one finding again. Takes effect on the next fetch (a viewer with the bytes already open keeps them; page images are cached for five minutes). Answers with an exposure report: every viewing session that overlapped the window it was visible, and whether that session was on its page -- 'opened while it was visible', never 'saw it'.
Shares
Recipients
-
POST /recipients/{id}/revoke send
Kill one recipient's link, leaving the rest live.
-
POST /recipients/{id}/erase send
Erase everything about one named recipient — their address, their link, every view and everything they wrote. REVOKE THEM FIRST: this is refused on a live recipient, deliberately, because it is permanent. It records the request and the deletion happens on the next purge run, so the response says
scheduledrather than pretending it is done. What survives is listed in the response: de-attributed document state, audit records naming an internal id, and the bounce suppression.