---
title: "Create an envelope"
method: POST
path: "/envelopes"
tags: ["Envelopes"]
---

# Create an envelope

`POST /envelopes`

## Request

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

Creates an envelope from exactly one source. The document branch accepts a ready unused `documentId` plus direct signers and fields. The template branch accepts `templateId`, exact role-keyed recipients, sender values, and optional field overrides, then snapshots the template's current version. The branches reject each other's keys.

`send: true` on either branch additionally requires `envelopes:send`, validates readiness before any write, and persists a durable send attempt in the creation transaction. Queue transport recovery happens asynchronously.

All shape and semantic failures are answered before partial resources are written. Honors `Idempotency-Key` for exact response replay.

## Headers

- `Idempotency-Key` string

## Request body

- union: Strict union between direct-document creation (flat or stepped) and template snapshot creation.
  - DocumentEnvelopeCreateRequest: A complete ad hoc envelope built directly on an unused document.
    - `documentId` string, required: Public id of a `ready` document in this workspace that does not already have an envelope.
    - `subject` string, required: Subject line of the signing invitation.
    - `message` string: Body text carried in the invitation email.
    - `signingOrder` 'parallel' | 'sequential': `parallel` invites every signer at once. `sequential` invites each only after the one before has signed.
    - `emailVerification` boolean: Require a one-time code emailed to the signer before they can open the document.
    - `expiresInDays` integer: How long the envelope stays signable, counted from creation.
    - `send` boolean: Requires envelopes:send in addition to envelopes:write. When true, readiness and send admission are checked before creation and durable send intent commits with the envelope.
    - `signers` SignerCreateInput[], required
      - `name` string, required
      - `email` string, email, required
      - `role` 'signer' | 'approver' | 'reviewer': What the recipient is asked to do. `signer` executes the document and must be assigned at least one field. `approver` approves or declines and may hold fields. `reviewer` acknowledges having read it and may hold none. Defaults to `signer`.
      - `phone` string: Optional valid international phone number. Common punctuation is accepted and the stored value is normalized to E.164.
      - `key` string: Caller-chosen correlation token so fields in the same request can point at this signer. Request-scoped: never persisted and never returned. Must be unique within the request.
      - `order` integer: 1-based routing step. Optional. Gaps are closed and a repeated value puts those signers in the same step, emailed together; signers with no order follow those that have one, each in a step of its own, in input order. A value below 1 is rejected.
    - `fields` FieldCreateInput[], required: Every signer must own at least one signer-assigned field. Sender fields do not satisfy signer coverage.
      - union: One field on a new envelope. `assignee` is the current assignment shape. Legacy `signerId` remains accepted for one release; provide exactly one of them.
        - object
          - `type` 'signature' | 'initials' | 'text' | 'date' | 'checkbox' | 'number' | 'email' | 'select' | 'radio' | 'textarea', required: How the field is filled and rendered. Values are stored as strings; signature and initials use images.
          - `signerId` string: Legacy signer assignment. Resolved against signer key, email, then 1-based position. Do not send with `assignee`.
          - `assignee` union, required: `sender` for a sender-owned static field, or an object identifying a signer in this request.
            - 'sender'
            - object
              - …
          - `name` string, nullable: Stable field name, unique within the envelope when present.
          - `value` string, nullable: Sender value or editable signer default. Empty string and null are empty values. Textarea values are limited to 10,000 characters; every other scalar field value is limited to 1,000.
          - `readonly` boolean: Only valid for sender-owned fields. Sender fields are always static in signer sessions.
          - `validation` object, nullable
            - `minLength` integer
            - `maxLength` integer
            - `pattern` string: Safe JavaScript regular expression without flags.
            - `min` number
            - `max` number
            - `minDate` string, date
            - `maxDate` string, date
          - `options` object[], nullable: Required for select/radio and forbidden for other types. Option values must be unique.
            - `value` string, required
            - `label` string, required
          - `pageIndex` integer, required: 0-based page, and it must be inside the document's page count.
          - `xRatio` number, required: Left edge as a fraction of page width, from the left.
          - `yRatio` number, required: Top edge as a fraction of page height, from the top.
          - `widthRatio` number, required: Width as a fraction of page width. `xRatio + widthRatio` must stay within the page.
          - `heightRatio` number, required: Height as a fraction of page height. `yRatio + heightRatio` must stay within the page.
          - `required` boolean: Whether the signer must fill this field before the envelope can complete.
          - `label` string: Shown to the signer beside the field. Most useful on `text` and `date`, where the field alone does not say what is wanted.
          - `detectionSource` 'agent' | 'manual' | 'agent-then-manual': Where the placement came from. Pass through what field detection returned so the provenance survives; omit it for hand-placed fields.
          - `detectionConfidence` number, nullable: The detector's confidence, when the placement came from detection.
          - `detectionReason` string, nullable: Why the detector placed the field here.
        - object
          - `type` 'signature' | 'initials' | 'text' | 'date' | 'checkbox' | 'number' | 'email' | 'select' | 'radio' | 'textarea', required: How the field is filled and rendered. Values are stored as strings; signature and initials use images.
          - `signerId` string, required: Legacy signer assignment. Resolved against signer key, email, then 1-based position. Do not send with `assignee`.
          - `assignee` union: `sender` for a sender-owned static field, or an object identifying a signer in this request.
            - 'sender'
            - object
              - …
          - `name` string, nullable: Stable field name, unique within the envelope when present.
          - `value` string, nullable: Sender value or editable signer default. Empty string and null are empty values. Textarea values are limited to 10,000 characters; every other scalar field value is limited to 1,000.
          - `readonly` boolean: Only valid for sender-owned fields. Sender fields are always static in signer sessions.
          - `validation` object, nullable
            - `minLength` integer
            - `maxLength` integer
            - `pattern` string: Safe JavaScript regular expression without flags.
            - `min` number
            - `max` number
            - `minDate` string, date
            - `maxDate` string, date
          - `options` object[], nullable: Required for select/radio and forbidden for other types. Option values must be unique.
            - `value` string, required
            - `label` string, required
          - `pageIndex` integer, required: 0-based page, and it must be inside the document's page count.
          - `xRatio` number, required: Left edge as a fraction of page width, from the left.
          - `yRatio` number, required: Top edge as a fraction of page height, from the top.
          - `widthRatio` number, required: Width as a fraction of page width. `xRatio + widthRatio` must stay within the page.
          - `heightRatio` number, required: Height as a fraction of page height. `yRatio + heightRatio` must stay within the page.
          - `required` boolean: Whether the signer must fill this field before the envelope can complete.
          - `label` string: Shown to the signer beside the field. Most useful on `text` and `date`, where the field alone does not say what is wanted.
          - `detectionSource` 'agent' | 'manual' | 'agent-then-manual': Where the placement came from. Pass through what field detection returned so the provenance survives; omit it for hand-placed fields.
          - `detectionConfidence` number, nullable: The detector's confidence, when the placement came from detection.
          - `detectionReason` string, nullable: Why the detector placed the field here.
    - `cc` CcRecipientInput[]: Copy recipients, delivered a read-only link to the finalized PDF once the envelope is executed and nothing before it.
      - `name` string, required
      - `email` string, email, required
  - SteppedEnvelopeCreateRequest: The same ad hoc envelope, routed as an ordered chain of steps. `steps` replaces `signers` and `signingOrder`: everyone in a step is emailed together, and the next step is only reached once every signer in the current one has signed.
    - `documentId` string, required: Public id of a `ready` document in this workspace that does not already have an envelope.
    - `subject` string, required: Subject line of the signing invitation.
    - `message` string: Body text carried in the invitation email.
    - `emailVerification` boolean: Require a one-time code emailed to the signer before they can open the document.
    - `expiresInDays` integer: How long the envelope stays signable, counted from creation.
    - `send` boolean: Requires envelopes:send in addition to envelopes:write. When true, readiness and send admission are checked before creation and durable send intent commits with the envelope.
    - `steps` object[], required: The signing chain, in order. At most 10 signers across all steps.
      - `signers` StepSignerCreateInput[], required: Everyone in this step is emailed together and may sign in any order.
        - `name` string, required
        - `email` string, email, required
        - `role` 'signer' | 'approver' | 'reviewer': What the recipient is asked to do. `signer` executes the document and must be assigned at least one field. `approver` approves or declines and may hold fields. `reviewer` acknowledges having read it and may hold none. Defaults to `signer`.
        - `phone` string: Optional valid international phone number. Common punctuation is accepted and the stored value is normalized to E.164.
        - `key` string: Caller-chosen correlation token so fields in the same request can point at this signer. Request-scoped: never persisted and never returned. Must be unique within the request.
    - `fields` FieldCreateInput[], required: Every signer must own at least one signer-assigned field. Sender fields do not satisfy signer coverage.
      - union: One field on a new envelope. `assignee` is the current assignment shape. Legacy `signerId` remains accepted for one release; provide exactly one of them.
        - object
          - `type` 'signature' | 'initials' | 'text' | 'date' | 'checkbox' | 'number' | 'email' | 'select' | 'radio' | 'textarea', required: How the field is filled and rendered. Values are stored as strings; signature and initials use images.
          - `signerId` string: Legacy signer assignment. Resolved against signer key, email, then 1-based position. Do not send with `assignee`.
          - `assignee` union, required: `sender` for a sender-owned static field, or an object identifying a signer in this request.
            - 'sender'
            - object
              - …
          - `name` string, nullable: Stable field name, unique within the envelope when present.
          - `value` string, nullable: Sender value or editable signer default. Empty string and null are empty values. Textarea values are limited to 10,000 characters; every other scalar field value is limited to 1,000.
          - `readonly` boolean: Only valid for sender-owned fields. Sender fields are always static in signer sessions.
          - `validation` object, nullable
            - `minLength` integer
            - `maxLength` integer
            - `pattern` string: Safe JavaScript regular expression without flags.
            - `min` number
            - `max` number
            - `minDate` string, date
            - `maxDate` string, date
          - `options` object[], nullable: Required for select/radio and forbidden for other types. Option values must be unique.
            - `value` string, required
            - `label` string, required
          - `pageIndex` integer, required: 0-based page, and it must be inside the document's page count.
          - `xRatio` number, required: Left edge as a fraction of page width, from the left.
          - `yRatio` number, required: Top edge as a fraction of page height, from the top.
          - `widthRatio` number, required: Width as a fraction of page width. `xRatio + widthRatio` must stay within the page.
          - `heightRatio` number, required: Height as a fraction of page height. `yRatio + heightRatio` must stay within the page.
          - `required` boolean: Whether the signer must fill this field before the envelope can complete.
          - `label` string: Shown to the signer beside the field. Most useful on `text` and `date`, where the field alone does not say what is wanted.
          - `detectionSource` 'agent' | 'manual' | 'agent-then-manual': Where the placement came from. Pass through what field detection returned so the provenance survives; omit it for hand-placed fields.
          - `detectionConfidence` number, nullable: The detector's confidence, when the placement came from detection.
          - `detectionReason` string, nullable: Why the detector placed the field here.
        - object
          - `type` 'signature' | 'initials' | 'text' | 'date' | 'checkbox' | 'number' | 'email' | 'select' | 'radio' | 'textarea', required: How the field is filled and rendered. Values are stored as strings; signature and initials use images.
          - `signerId` string, required: Legacy signer assignment. Resolved against signer key, email, then 1-based position. Do not send with `assignee`.
          - `assignee` union: `sender` for a sender-owned static field, or an object identifying a signer in this request.
            - 'sender'
            - object
              - …
          - `name` string, nullable: Stable field name, unique within the envelope when present.
          - `value` string, nullable: Sender value or editable signer default. Empty string and null are empty values. Textarea values are limited to 10,000 characters; every other scalar field value is limited to 1,000.
          - `readonly` boolean: Only valid for sender-owned fields. Sender fields are always static in signer sessions.
          - `validation` object, nullable
            - `minLength` integer
            - `maxLength` integer
            - `pattern` string: Safe JavaScript regular expression without flags.
            - `min` number
            - `max` number
            - `minDate` string, date
            - `maxDate` string, date
          - `options` object[], nullable: Required for select/radio and forbidden for other types. Option values must be unique.
            - `value` string, required
            - `label` string, required
          - `pageIndex` integer, required: 0-based page, and it must be inside the document's page count.
          - `xRatio` number, required: Left edge as a fraction of page width, from the left.
          - `yRatio` number, required: Top edge as a fraction of page height, from the top.
          - `widthRatio` number, required: Width as a fraction of page width. `xRatio + widthRatio` must stay within the page.
          - `heightRatio` number, required: Height as a fraction of page height. `yRatio + heightRatio` must stay within the page.
          - `required` boolean: Whether the signer must fill this field before the envelope can complete.
          - `label` string: Shown to the signer beside the field. Most useful on `text` and `date`, where the field alone does not say what is wanted.
          - `detectionSource` 'agent' | 'manual' | 'agent-then-manual': Where the placement came from. Pass through what field detection returned so the provenance survives; omit it for hand-placed fields.
          - `detectionConfidence` number, nullable: The detector's confidence, when the placement came from detection.
          - `detectionReason` string, nullable: Why the detector placed the field here.
    - `cc` CcRecipientInput[]: Copy recipients, delivered a read-only link to the finalized PDF once the envelope is executed and nothing before it.
      - `name` string, required
      - `email` string, email, required
  - TemplateEnvelopeCreateRequest: Create an immutable envelope snapshot from the current version of a reusable template.
    - `templateId` string, required
    - `recipients` object, required: Exactly one recipient for every template role key; unknown keys are rejected.
    - `values` object: Named sender field values. Only names in the template are accepted.
    - `subject` string: Literal override. When omitted, the interpolated template default is used.
    - `message` string: Literal override. When omitted, the interpolated template default is used.
    - `signingOrder` 'parallel' | 'sequential'
    - `recipientOrder` string[]: The signing sequence as template role keys, every role exactly once. When omitted, each signer keeps the template role's stored order. Only meaningful when signingOrder is sequential.
    - `emailVerification` boolean
    - `expiresInDays` integer
    - `fieldOverrides` TemplateFieldOverrides
      - `remove` string[]
      - `set` object
      - `add` TemplateEnvelopeAddedField[]
        - `name` string, required
        - `type` 'signature' | 'initials' | 'text' | 'date' | 'checkbox' | 'number' | 'email' | 'select' | 'radio' | 'textarea', required: How the field is filled and rendered. Values are stored as strings; signature and initials use images.
        - `assignee` union, required
          - 'sender'
          - object
            - `role` string, required
        - `pageIndex` integer, required
        - `xRatio` number, required
        - `yRatio` number, required
        - `widthRatio` number, required
        - `heightRatio` number, required
        - `required` boolean
        - `readonly` boolean
        - `value` string, nullable
        - `validation` FieldValidation
          - `minLength` integer
          - `maxLength` integer
          - `pattern` string
          - `min` number
          - `max` number
          - `minDate` string, date
          - `maxDate` string, date
        - `options` FieldOption[], nullable
          - `value` string, required
          - `label` string, required
        - `label` string
    - `send` boolean: Requires envelopes:send in addition to envelopes:write.

## Response `201`

The created envelope, with the public ids the server minted for its signers and fields. Status is `sending` when `send: true` committed durable send intent, otherwise `draft`.

- object
  - `data` EnvelopeCreated, required: The immutable response captured at creation. Template provenance and readiness reflect the original snapshot, even when replayed later.
    - `id` string, required: Public envelope id. This is the `envelopeId` path parameter for every later call on this envelope: `POST /envelopes/{envelopeId}/send`, `GET /envelopes/{envelopeId}`, `GET /envelopes/{envelopeId}/document`, and `GET /envelopes/{envelopeId}/audit-trail`.
    - `status` 'draft' | 'sending' | 'pending' | 'completing' | 'completed' | 'declined' | 'voided' | 'expired', required: Where the envelope is in its lifecycle. `sending` and `completing` are transient; `completed`, `declined`, `voided`, and `expired` are terminal.
    - `subject` string, nullable, required
    - `signingOrder` 'parallel' | 'sequential', required
    - `template` EnvelopeTemplateProvenance, nullable, required
      - `id` string, nullable, required: Null after the source template is deleted.
      - `version` integer, required
    - `signers` SignerSummary[], required
      - `id` string, required: Public signer id.
      - `name` string, required
      - `email` string, email, required
      - `role` 'signer' | 'approver' | 'reviewer', required: What the recipient is asked to do. `signer` executes the document and must be assigned at least one field. `approver` approves or declines and may hold fields. `reviewer` acknowledges having read it and may hold none. Defaults to `signer`.
      - `status` 'pending' | 'sent' | 'viewed' | 'signed' | 'approved' | 'acknowledged' | 'declined', required: Where one recipient is. `pending` means not yet invited, which on a sequential envelope is normal for everyone behind the current turn. `signed`, `approved` and `acknowledged` are the three positive terminal states, one per role: a signer signs, an approver approves, a reviewer acknowledges.
    - `fields` FieldSummary[], required
      - `id` string, required: Public field id.
      - `type` 'signature' | 'initials' | 'text' | 'date' | 'checkbox' | 'number' | 'email' | 'select' | 'radio' | 'textarea', required: How the field is filled and rendered. Values are stored as strings; signature and initials use images.
      - `name` string, nullable, required: Stable field name when one was supplied.
      - `assignee` 'sender' | 'signer', required
      - `readonly` boolean, required
      - `value` string, nullable, required: Initial sender value or signer default.
      - `pageIndex` integer, required: 0-based page the field sits on.
    - `readiness` EnvelopeReadiness, required: Computed send readiness. It is never persisted and may change after a draft edit.
      - `canSend` boolean, required: True when no readiness issue blocks sending. Credit availability is separate.
      - `issues` ReadinessIssue[], required
        - union
          - MissingSenderValueIssue
            - `code` 'missing_sender_value', required
            - `fieldId` string, required
            - `name` string, nullable, required
            - `label` string, nullable, required
          - InvalidSenderValueIssue
            - `code` 'invalid_sender_value', required
            - `fieldId` string, required
            - `name` string, nullable, required
            - `detail` string, required: Safe, human-readable validation detail.
          - UnassignedFieldIssue: Reserved for forward compatibility. Current persisted envelopes do not produce this issue.
            - `code` 'unassigned_field', required
            - `fieldId` string, required
          - NoSignerFieldsIssue
            - `code` 'no_signer_fields', required
    - `send` object, required
      - `requested` boolean, required
      - `status` 'not_requested' | 'scheduled', required: `scheduled` means the durable send intent committed. Queue transport is recovered asynchronously.
    - `expiresAt` string, date-time, nullable, required
    - `createdAt` string, date-time, required
    - `warnings` EnvelopeWarning[]: Non-blocking remarks about this envelope, currently recipient domains this workspace has never sent to. Empty when there is nothing to say. Absent on a response replayed from before this field existed.
      - `code` 'unfamiliar_recipient_domain', required: `unfamiliar_recipient_domain`: a signer is at a domain this workspace has never sent an envelope to.
      - `message` string, required: The warning in one sentence, written to be repeated to a person.
      - `domains` string[], required: The recipient domains this workspace has not sent to before.
    - `steps` EnvelopeSigningStep[], required: The routing chain as persisted. A parallel envelope is a single step holding every signer.
      - `step` integer, required
      - `status` 'completed' | 'active' | 'pending': Where this step sits relative to the step the envelope is on.
      - `signers` string[], required: Public ids of the signers in this step.
    - `cc` CcRecipientSummary[], required: Copy recipients on this envelope.
      - `id` string, required
      - `name` string, required
      - `email` string, email, required
    - `creditCost` integer, required: Credits sending this envelope costs: one for the envelope, one for each signer who is not the sender, and one more when `emailVerification` is on. An unmetered workspace is quoted the same price and charged nothing.
    - `creditLines` CreditLine[], required: The same total broken into its lines.
      - `kind` 'envelope' | 'signer' | 'otp', required
      - `quantity` integer, required: How many units this line charges for.
      - `unitCredits` integer, required: Credits per unit.
      - `credits` integer, required: `quantity` times `unitCredits`.

## Other responses

- `400`: The request is malformed or the resource is in the wrong state for a synchronous check. Codes: `validation_error`, `invalid_body`, `invalid_parameter`, `invalid_idempotency_key`, `invalid_event_id`, `invalid_file`, `invalid_url`, `invalid_status`, `key_mismatch`, `no_signers`, `not_completed`, `envelope_exists`, `document_not_ready`, `upload_failed`.
- `401`: The credential is missing, malformed, unknown, expired, revoked, or past its rotation grace window. Code: `unauthorized`. Every authentication failure is deliberately indistinguishable.
- `402`: Envelope creation requested immediate sending, but the metered workspace has no credit available. Code: `insufficient_credits`.
- `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`: this workspace requires a person to approve sends made by a credential, so an envelope cannot be created and sent in one request. Create it with `send: false`, then call `sendEnvelope`, which parks it for approval. The same status also covers the shared conflict codes: `document_in_use`, `envelope_exists`, `workspace_unavailable`, `idempotency_in_progress`.
- `422`: The direct or template snapshot contract failed. Template-specific codes identify role, value, archive, completeness, and override failures. `envelope_not_ready` carries readiness issues. The same status also carries `idempotency_key_reuse`. `recipient_denied` refuses a recipient whose address or domain is on the workspace's agent deny list; `details.recipients` names each one and the entry that matched.
- `429`: The `write` rate bucket is exhausted (`rate_limited`), the workspace hit a content or send ceiling (`workspace_limit_exceeded`), or, when the request also sends, the credential reached its own spend limit (`credential_limit_exceeded`, with `details.limit`, `details.window` and `details.resetAt`). Nothing was created in the last case.
- `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)
