---
title: "Send an envelope for signature"
method: POST
path: "/envelopes/{envelopeId}/send"
tags: ["Envelopes"]
---

# Send an envelope for signature

`POST /envelopes/{envelopeId}/send`

## Request

- Base URL: `https://inkseal.net/api/v1`
- URL: `https://inkseal.net/api/v1/envelopes/{envelopeId}/send`
- Auth: HTTP bearer

**Asynchronous.** The 202 says the envelope was queued, not that anyone was emailed.

What the 202 does mean: the envelope exists in this workspace, it was `draft`, it has at least one signer, the workspace balance covered what the envelope costs, the workspace was under its monthly send ceiling, the row was locked, the persisted signers and fields were re-validated against the same envelope contract `POST /envelopes` enforces, readiness was computed from that locked snapshot, the status was moved to `sending`, and a durable `envelope.send` attempt committed. Immediate queue dispatch is best effort; the reconciler retries undispatched attempts. What it does not mean: nothing has been mailed, no signer token has been minted, no page has been rendered, and **nothing has been charged**.

States: `draft` -> `sending` (transient, editing locked) -> `pending` (out for signature: the worker charged the workspace, minted the signer tokens, rendered the pages, and mailed the invitations) -> `completing` -> `completed`. From `pending` an envelope may also reach `declined`, `voided`, or `expired`. If the send job exhausts its retries the envelope is returned to `draft` with a human-readable send error, tokens are reset, and any charge is refunded.

**The credits.** The envelope's price comes off the workspace inside the worker's claim transaction, together with the `draft` -> `pending` transition and a `usage` ledger row: one credit for the envelope, one for each signer who is not the sender, and one more when `emailVerification` is on. `GET /envelopes/{envelopeId}` reports the same number as `creditCost`. The balance check in this request is advisory: it fails fast on a balance that cannot cover the send but reserves nothing, so a send can still fail on credits later and be refunded.

**Validation and readiness.** Persisted envelope-contract validation runs first. Structural failures, including invalid field configuration, invalid signer defaults, and partial signer coverage, answer 422 `invalid_envelope` with contract issues. Readiness then owns missing or invalid sender values and the case where no signer owns any field; these answer 422 `envelope_not_ready` with readiness issues. Either locked refusal changes no status, queues no work, and spends nothing.

**Polling recipe.** Call `GET /envelopes/{envelopeId}` and watch `status`. `sending` means in flight; `pending` with a non-null `sentAt` means delivered to the signers; anything else is terminal. An envelope sitting in `sending` for more than a few minutes is stuck, not slow.

**Webhook alternative.** Subscribe to `envelope.sent` (the invitations went out), `envelope.send_failed` (the job gave up; the payload names the reason, `recoveredStatus: "draft"`, and whether the credits were refunded), and `envelope.completed` (the executed PDF is ready to download).

**Insufficient credits** answers 402 `insufficient_credits`, with `details.required` and `details.available`, and changes nothing. **Re-sending an already-sent envelope** answers 400 `invalid_status`: the endpoint is a `draft`-only transition, and a retry of a send that already succeeded is not a second send. **A workspace send ceiling** answers 429 `workspace_limit_exceeded` with `details.limit`, which is a wait, not a fix to the envelope. **A queue outage after the durable attempt commits** does not change the 202 response or return the envelope to `draft`; reconciliation dispatches the same attempt later.

No request body. Honors `Idempotency-Key`; metered in the `send` rate bucket.

## Headers

- `Idempotency-Key` string

## Response `202`

The envelope was claimed and queued (`sending`), or, on a workspace that requires a person to approve agent sends, parked for that approval (`pending_approval`). A parked send has queued nothing, emailed nobody and spent no credit. A retry of an unchanged draft returns the approval it already has. A retry after the draft was edited opens a new one and emails the owner again, because an approval covers only the revision the owner was shown.

- object
  - `data` EnvelopeSendAccepted, required: What a queued send answers with. `status` describes the transition just made, not the outcome of the delivery: `sending` means the envelope was claimed and queued, and `pending_approval` means the workspace requires a person to release sends made by a credential, so nothing was queued, nobody was emailed and nothing was charged.
    - `id` string, required
    - `status` 'sending' | 'pending_approval', required
    - `approvalId` string: The pending approval a person has to release. Present only with `pending_approval`. The same id appears on the draft's `pendingApproval` block, which is what to poll.
    - `approvalExpiresAt` string, date-time: When the approval request lapses if nobody answers it. Present only with `pending_approval`.
    - `creditCost` integer: What the queued send will spend. Absent while a send waits for a person to release it, and 0 on an unmetered workspace.

## Other responses

- `400`: `invalid_status` when the envelope is not `draft` (it has already been sent), or `no_signers` when it has none. Also the malformed-body and malformed-`Idempotency-Key` cases.
- `401`: The credential is missing, malformed, unknown, expired, revoked, or past its rotation grace window. Code: `unauthorized`. Every authentication failure is deliberately indistinguishable.
- `402`: `insufficient_credits`. The workspace balance does not cover what the envelope costs; `details` carries `required` and `available`. Buy more and retry. Nothing was queued and nothing was charged.
- `403`: The credential is valid but may not do this. Codes: `insufficient_scope` (the route's scopes are not all held; `details` names `requiredScopes` and `missingScopes`), `forbidden` (the capability check on the resolved resource failed), `scope_escalation` (a grant wider than the caller's own was requested).
- `404`: The resource does not exist, was deleted, or belongs to another workspace. Code: `not_found`. The three cases are answered identically on purpose, so a 404 never confirms that an identifier exists elsewhere.
- `409`: `approval_required`: the workspace turned on send approval after this request passed the check, or the draft still has an open send approval that a person has to decide. A credential cannot release a parked send. The same status also covers `idempotency_in_progress`.
- `422`: `envelope_not_ready` for readiness issues, `invalid_envelope` for persisted structural contract issues, or `idempotency_key_reuse` when the key belongs to a different request. A send refusal changes no state and charges no credit. `recipient_denied` refuses a signer or CC whose address or domain is on the workspace's agent deny list; `details.recipients` names each one and the entry that matched. The list is checked again when the send is claimed, so an edit or a list change made during the request is still caught.
- `429`: The `send` rate bucket is exhausted (`rate_limited`), the workspace hit a send ceiling (`workspace_limit_exceeded`, with `details.limit` naming it, for example `monthly_sends`), or the credential reached its own spend limit (`credential_limit_exceeded`, with `details.limit`, `details.window` and `details.resetAt`). All three are waits rather than fixes to the envelope, and none of them sends anything or spends anything.
- `503`: A dependency the request needs is unreachable and nothing was changed. Codes: `queue_unavailable` (a background job could not be enqueued; retry the call), `billing_unavailable` (Stripe would not open a checkout session; nothing was charged, retry the call), `service_unavailable` (the rate-limit store is down, so the request was refused before running).

---

[API](https://skmtc.dev/inkseal/apis/inkseal-api.md) · [All operations](https://skmtc.dev/inkseal/apis/inkseal-api/llms.txt) · [OpenAPI document](https://skmtc.dev/inkseal/apis/inkseal-api/revisions/c440c981d578?raw)
