---
title: "Get an editable draft snapshot"
method: GET
path: "/envelopes/{envelopeId}/draft"
tags: ["Envelopes"]
---

# Get an editable draft snapshot

`GET /envelopes/{envelopeId}/draft`

## Request

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

Returns the canonical draft state: editor version, routing, signers, complete field geometry and configuration, detection metadata, invitation settings, expiry, and computed readiness. A sent envelope answers invalid_status. A cross-workspace id answers 404.

## Response `200`

The canonical editable snapshot.

- object
  - `data` DraftEnvelope, required
    - `id` string, required
    - `status` 'draft', required
    - `editorVersion` integer, required
    - `document` object, required
      - `id` string, required
      - `filename` string, required
      - `pageCount` integer, required
    - `subject` string, required
    - `message` string, nullable, required
    - `signingOrder` 'parallel' | 'sequential', required
    - `emailVerification` boolean, required
    - `expiresAt` string, date-time, required
    - `signers` DraftSigner[], required
      - `id` string, required
      - `key` string, required: Equals id and can be used in the next replacement.
      - `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`.
      - `phone` string, nullable, required
      - `order` integer, required
    - `fields` DraftField[], 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` for a sender-owned static field, or an object identifying a signer in this request.
        - 'sender'
        - object
          - `signerId` string, required
      - `name` string, nullable, required: Stable field name, unique within the envelope when present.
      - `value` string, nullable, required: 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, required: Only valid for sender-owned fields. Sender fields are always static in signer sessions.
      - `validation` object, nullable, required
        - `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: 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, required: Whether the signer must fill this field before the envelope can complete.
      - `label` string, nullable, required: 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', required: Where the placement came from. Pass through what field detection returned so the provenance survives; omit it for hand-placed fields.
      - `detectionConfidence` number, nullable, required: The detector's confidence, when the placement came from detection.
      - `detectionReason` string, nullable, required: Why the detector placed the field here.
      - `id` string, required
    - `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
    - `createdAt` string, date-time, required
    - `updatedAt` string, date-time, required
    - `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.
- `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`: `invalid_status`: sending has begun and the envelope is no longer editable. The shared route mapper also defines `version_conflict` for the PUT operation.
- `429`: A limit was reached. Codes: `rate_limited` (the credential's per-bucket HTTP limit, or the per-workspace detection throttle), `workspace_limit_exceeded` (a workspace content or send limit; `details.limit` names it), `daily_budget_exceeded` (the workspace's daily detection budget). The four rate-limit headers are sent on this response and on no successful one, so a client cannot read its remaining budget without being refused. Retrying after `Retry-After` is always safe: a rate-limit refusal happens before the handler runs.
- `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)
