Skip to navigation

Sign

Send a PDF for e-signature and get a sealed document with an audit trail.

View as Markdown

For new product integrations, use the RakeSign API. It adds team documents, scoped integration keys, template management, and revision-bound delegation. POST /v1/sign remains available with its existing contract. See migration guidance before changing an existing integration.

POST /v1/sign creates a signature envelope for one PDF. Each signer verifies their email with a one-time code. Each signer signs by typing their name. When the last signer completes, CloudRaker adds a signature certificate and a tamper-evident cryptographic seal. The sealed document comes back as a new file.

How it works

  1. You send a file and one or more signers (name + email). You can add cc parties (name + email) who only receive the result.
  2. CloudRaker creates the envelope and emails each signer their own signing link. The call returns 202 with status: "needs_input". Signing is never synchronous.
  3. The run stays at needs_input while signatures come in. Poll GET /v1/runs/:id, watch the envelope, or subscribe a webhook.
  4. When all signers complete, CloudRaker seals the document. The run reaches processed with output.file and output.auditUrl. Each cc party receives the sealed PDF by email.

Sign runs are exempt from the run TTL. An envelope must outlive the 7-day maximum, so CloudRaker never purges an open envelope. The expiresAt field is still present on the run body. For a sign run, the API does not enforce it. The envelope has its own expiry, visible on GET /v1/runs/:id/envelope.

Quickstart

curl -X POST https://api.cloudraker.com/v1/sign \
-H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"file": { "id": "a04d6597-4e34-4a99-94ea-964c289a4c68" },
"signers": [{ "name": "Jane Doe", "email": "jane@example.com" }],
"cc": [{ "name": "Marc Renaud", "email": "marc.renaud@example.com" }],
"message": "Please sign the master services agreement.",
"placement": "page"
}'

The TypeScript and Python samples use plain HTTP, so they run with no installed packages. This endpoint is also a top-level method on both SDKs as of 0.3.0: client.sign(…). That page has a full worked example.

Example response

{
"object": "sign_run",
"id": "sgr_01KYD7A0QEMDJ7YKCNMAXFD229",
"status": "needs_input",
"statusUrl": "/v1/runs/sgr_01KYD7A0QEMDJ7YKCNMAXFD229",
"envelopeUrl": "/v1/runs/sgr_01KYD7A0QEMDJ7YKCNMAXFD229/envelope"
}

envelopeUrl appears on every sign response, the 202 and each GET /v1/runs/:id. It points at the envelope, the sender’s view of who has signed. A signing link is a bearer capability that belongs to one signer. CloudRaker emails the link to that signer and never returns it to you.

When every signer completes, GET /v1/runs/:id reaches processed. Then output carries:

FieldWhat it is
output.fileThe sealed PDF: {id, name, url}. Also at GET /v1/runs/:id/output/:name.
output.envelopeIdThe envelope this run produced.
output.signers[]{name, email, signedAt} per signer.
output.cc[]{name, email} per cc party, when the request had any.
output.auditUrlThe machine-readable audit trail, also attached inside the sealed PDF.

Configuration

FieldTypeWhat it does
file{url, name?, processing?} or {id}Required. The PDF to be signed.
signers[]array of {name, email}, 1–50Required. Everyone is invited at once. Nobody waits their turn. Array position sets each signer’s position, and with placement: "tags", which marker they sign.
cc[]array of {name, email}, ≤ 50Informed parties. A cc party never signs, gets no one-time code, and needs no [Signature N] marker. Each cc party receives the sealed PDF when the envelope completes.
messagestring, ≤ 2000 charsShown to signers in the invitation email.
placementpage | tagsWhere the stamps go. See Placing the signatures. page is the default.
languageen | frThe language of everything a signer or cc party sees. See Language. en is the default.
metadataobjectYour own key/values. The API echoes them back on the run body, not on webhook deliveries. Max 10 KB.
webhook{url} or {id}Where to deliver terminal events. See Webhooks.
ttlinteger seconds, 1–604800The API accepts it for uniformity but does not enforce it while the envelope is open.

Placing the signatures

page (the default) appends an Electronic Signatures page and puts every stamp on it. Nothing needs preparing in the document.

tags puts each signer’s stamp on a literal [Signature N] in the document text. signers[0] signs [Signature 1], signers[1] signs [Signature 2], and so on, matched by position, never by name or email. Every signer needs a marker. Only signers count. cc parties never map to a marker. If one is missing the run fails before anyone is invited, with error.code: "signature_tags_missing".

A marker sets where a stamp goes, not how big it is. The stamp is anchored on the marker and takes the free space around it: above and to the right by default, below or to the left when that side is blocked. A full-size stamp is about 210 x 86pt, so leave roughly that much clear of other text and form fields on one side of the marker. A marker with less room gets a smaller stamp, down to a floor of about 120 x 49pt.

All the signature stamps in a document are drawn at one size, the smallest that any of them fits. Two signatures on a page match, instead of one printing larger than the other.

A marker with less room than the floor still prints, at the floor, over whatever is beneath it. You can see that before you send, so it is your call.

Two things a send does refuse, because neither leaves you anything to decide. A signature marker too close to a page edge to hold a stamp at all, where there is nowhere to draw it. And two signature markers so close together that both stamps cannot fit however they are arranged, where neither signature would be readable. Both refusals name the markers and the page.

Signature stamps step around each other: a stamp shrinks or takes a different corner rather than print across one already placed. What a stamp cannot step around is an initials or date, which are measured against the text beside them and print wider than their own marker. A date is the easiest to miss, because it is written in full. Give each marker its own line, or check overlaps_field before you send.

[Initials N] and [Date N] are written as text at the marker, not as a stamp. They shrink to clear whatever is to their right, so leave space on that side. Below a readable floor they stop shrinking and print over their neighbour instead. A date needs more room than its marker suggests, because it is written as YYYY-MM-DD.

GET /v1/signing/inspect/document/{id} shows where every stamp will print, before you send. POST /v1/signing/inspect does the same for a PDF you have not stored yet, posted as multipart with a file field. Each marker comes back with the box its output will take and a status:

statuswhat printsstops a send
okfull size, clear of everythingno
shrunksmaller, to clear nearby content or to match the other signaturesno
overlaps_textnothing clears at any corner, so it prints at its smallest over the text beneathno
overlaps_fieldit prints over another marker’s output, because the two sit close enough that both claim the space. A slight clip is not reportedonly when overlapsSignature is true
off_pageno room on the page at any corner, so box is nullyes

overlapsSignature is true on a signature that prints over another signature. Placement has already tried every size and corner for both, so no layout keeps them apart and the send is refused. An initials or date that prints into a signature is only overlaps_field. That is a warning: a date is written in full, and placement does not step around it.

Markers are not case-sensitive, and each one has a short form: [S1], [I1] and [D1] mean the same as [Signature 1], [Initials 1] and [Date 1]. The space after the full word is optional, so [Signature1] works too. The short forms take no space, so [S 1] is text, not a marker. A marker numbered below 1 is refused when you send.

Both placements append the audit pages, so tags gives you in-place signatures and the trail.

Language

language sets the language of everything the recipients see. It applies to every signer and every cc party of the envelope. Only the sender chooses it. en (the default) is English. fr is French.

The setting changes:

  • the invitation and verification-code emails,
  • the signing page,
  • the signature certificate pages in the sealed PDF.

The API surface stays in English. Run bodies, envelope fields, the audit trail JSON, and [Signature N] markers do not change.

curl -X POST https://api.cloudraker.com/v1/sign \
-H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"file": { "id": "a04d6597-4e34-4a99-94ea-964c289a4c68" },
"signers": [{ "name": "Jeanne Tremblay", "email": "jeanne@example.com" }],
"cc": [{ "name": "Marc Renaud", "email": "marc.renaud@example.com" }],
"message": "Veuillez signer le contrat de services.",
"language": "fr"
}'

Track the envelope

Four sub-routes hang off the run id.

GET /v1/runs/:id/envelope

The sender’s view: envelope status, the signer roster with per-signer state, and the event trail. When the request carries cc, the envelope also lists each cc party, labeled CC. A cc party has no signer state: no link, no verification, no resend.

{
"envelope": {
"id": "5f791fda-7120-42f3-bed0-fd6d2589daca",
"docName": "msa.pdf",
"docSha256": "2d420cbb4123dcf1fb82595b2359cfbb5d81f00b9df9d359fcc7af361d093f53",
"docSize": 140815,
"status": "pending",
"signatureMode": "page",
"message": "Please sign the master services agreement.",
"outputFileId": null,
"createdAt": "2026-07-25T18:06:34.000Z",
"completedAt": null,
"expiresAt": "2026-08-01T18:06:34.000Z"
},
"signers": [
{
"id": "a8a1ebf8-79d8-4124-9183-33d2c8f4b61e",
"name": "Jane Doe",
"email": "jane@example.com",
"position": 1,
"status": "pending",
"emailVerifiedAt": null,
"signedAt": null,
"lastInvitedAt": "2026-07-25T18:06:34.000Z"
}
],
"events": [
{ "id": 55, "type": "created", "actor": "system", "occurredAt": "2026-07-25T18:06:34.000Z" },
{ "id": 56, "type": "invited", "actor": "Jane Doe", "signerId": "a8a1ebf8-79d8-4124-9183-33d2c8f4b61e", "occurredAt": "2026-07-25T18:06:37.000Z" }
]
}

The envelope never contains signing links. A signing link is a bearer capability that only its signer holds. To get a signer moving again, re-send their invitation.

POST /v1/runs/:id/signers/:signerId/resend

Emails a pending signer a new signing link. Their previous link stops working immediately. Returns 409 if that signer already signed or if the envelope is no longer pending.

POST /v1/runs/:id/void

Cancels a pending envelope. Every signing link dies at once, and the run terminates as failed. The body accepts an optional { "reason": "…" }. Returns 409 when the envelope is finalizing or completed.

curl -X POST https://api.cloudraker.com/v1/runs/sgr_01KYD7A0QEMDJ7YKCNMAXFD229/void \
-H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "reason": "Superseded by a new draft" }'

GET /v1/runs/:id/audit

Returns the audit trail as JSON: creation, invitations, email verifications, signatures, and the seal. It is available before the envelope completes. The sealed PDF carries the same document as an attachment.

Sync vs async

Sign is always asynchronous. POST /v1/sign answers 202 for any ?wait= value, because a sign run cannot reach a terminal status inside the window. With the default wait, the call holds until the envelope exists and reports status: "needs_input". With ?wait=0, the call returns immediately with status: "queued". Poll GET /v1/runs/:id?wait=…, watch the envelope, or subscribe run.completed on a webhook endpoint.

Save as a config

Sign is the one capability with no config library and no action reference in its request body. POST /v1/sign and {"sign": …} pipeline steps always run the built-in signing action. They take their whole configuration inline. Send message and placement on every call.

There is no /v1/sign/configs. POST /v1/actions accepts "capability": "sign", so you can create and list a saved sign action. But today no /v1 request path consumes one. The API rejects a saved sign action as another verb’s action with 400 invalid_request. The same applies to a bare {"action": …} pipeline step. Only the sign path keeps the run exempt from the TTL purge.

Next steps