> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.cloudraker.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.cloudraker.com/_mcp/server.

# Sign

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

## How it works

1. You send a **file** and one or more **signers** (`name` + `email`).
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](#track-the-envelope), or subscribe a [webhook](/developers/webhooks).
4. Once everyone has signed, the document is sealed and the run reaches `processed` with `output.file` and `output.auditUrl`.

Sign runs are **exempt from the run TTL** — an envelope must outlive the 7-day maximum, so it is never purged while it is open. The `expiresAt` field is still present on the run body; for a sign run it is not enforced. The envelope has its own expiry, visible on `GET /v1/runs/:id/envelope`.

## Quickstart

```bash title="curl"
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" }],
    "message": "Please sign the master services agreement.",
    "placement": "page"
  }'
```

```ts title="TypeScript"
const res = await fetch("https://api.cloudraker.com/v1/sign", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CLOUDRAKER_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    file: { id: "a04d6597-4e34-4a99-94ea-964c289a4c68" },
    signers: [{ name: "Jane Doe", email: "jane@example.com" }],
    message: "Please sign the master services agreement.",
  }),
});

const run = await res.json(); // 202 — status: "needs_input"
console.log(run.id, run.statusUrl);
```

```python title="Python"
import os, requests

res = requests.post(
    "https://api.cloudraker.com/v1/sign",
    headers={"Authorization": f"Bearer {os.environ['CLOUDRAKER_API_KEY']}"},
    json={
        "file": {"id": "a04d6597-4e34-4a99-94ea-964c289a4c68"},
        "signers": [{"name": "Jane Doe", "email": "jane@example.com"}],
        "message": "Please sign the master services agreement.",
    },
)

run = res.json()  # 202 — status: "needs_input"
print(run["id"], run["statusUrl"])
```

The TypeScript and Python samples are plain HTTP so they run with nothing installed. This endpoint is also a top-level method on both [SDKs](/developers/sdks) as of 0.3.0 — `client.sign(…)`; that page has a full worked example.

## Example response

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

`envelopeUrl` is on every sign response — the `202` and each `GET /v1/runs/:id` — and points at [the envelope](#track-the-envelope), the sender's view of who has signed. A sign run never carries a `tasks[]` array the way a [fill review](/capabilities/fill#human-review) does: a signing link is a bearer capability belonging to one signer, so it is emailed to them and never returned to you.

Once every signer has completed, `GET /v1/runs/:id` reaches `processed` and `output` carries:

| Field               | What it is                                                                   |
| ------------------- | ---------------------------------------------------------------------------- |
| `output.file`       | The sealed PDF — `{id, name, url}`. Also at `GET /v1/runs/:id/output/:name`. |
| `output.envelopeId` | The envelope this run produced.                                              |
| `output.signers[]`  | `{name, email, signedAt}` per signer.                                        |
| `output.auditUrl`   | The machine-readable audit trail, also attached inside the sealed PDF.       |

## Configuration

| Field       | Type                                  | What it does                                                                                                                                                                                       |
| ----------- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `file`      | `{url, name?, processing?}` or `{id}` | **Required.** The PDF to be signed.                                                                                                                                                                |
| `signers[]` | array of `{name, email}`, 1–50        | **Required.** Signing order follows the array.                                                                                                                                                     |
| `message`   | string, ≤ 2000 chars                  | Shown to signers in the invitation email.                                                                                                                                                          |
| `placement` | `page` \| `tags`                      | `page` (default) appends a signature certificate page. `tags` puts each signer's stamp where a `[Signature N]` placeholder already sits in the document — every signer needs one or the run fails. |
| `metadata`  | object                                | Your own key/values, echoed back on the run body (not on webhook deliveries). Max 10 KB.                                                                                                           |
| `webhook`   | `{url}` or `{id}`                     | Where to deliver terminal events. See [Webhooks](/developers/webhooks).                                                                                                                            |
| `ttl`       | integer seconds, 1–604800             | Accepted for uniformity, but not enforced while the envelope is open.                                                                                                                              |

## 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.

```json
{
  "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 held only by its signer — 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. `409` if that signer already signed or 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. Optional body `{ "reason": "…" }`. `409` once the envelope is finalizing or completed.

```bash
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`

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

## Sync vs async

Always asynchronous — `POST /v1/sign` answers `202` whatever you pass for `?wait=`, because a sign run cannot reach a terminal status inside the window. With the default wait it holds until the envelope exists and reports `status: "needs_input"`; with `?wait=0` it returns immediately with `status: "queued"`. Poll `GET /v1/runs/:id?wait=…`, watch the envelope, or subscribe `run.completed` on a [webhook endpoint](/developers/webhooks).

## Save as an action

Sign is the one capability with **no `action` reference in its request body**. `POST /v1/sign` and `{"sign": …}` [pipeline](/capabilities/pipeline) steps always run the built-in signing action, and they take their whole configuration inline — send `message` and `placement` on every call.

`POST /v1/actions` does accept `"capability": "sign"`, so a saved sign action can be created and listed, but today no `/v1` request path consumes one. Passing a saved sign action as another verb's `action` — or as a bare `{"action": …}` pipeline step — is rejected with `400 invalid_request`, because only the sign path keeps the run exempt from the TTL purge.

## Next steps

#### [Fill](/capabilities/fill)

Complete the form before you send it out for signature.

#### [Runs](/developers/runs)

Statuses, downloading the sealed document, and keeping the result.

#### [Webhooks](/developers/webhooks)

Be told the moment the last signer completes.

#### [Pipelines](/capabilities/pipeline)

Redact and sign the same document in one call.