API reference

Version 1.5.0 of the OpenAPI 3.1.0 file, rendered when this site was built.

Status. The SignWren API is opening in stages and may not be on your account yet. If Settings shows an API keys section, you can make sandbox keys now; live keys come with the Pro and Business plans. SignWren is an electronic signature app for small businesses; it is not a law firm and does not give legal advice.

What does the API cover?

The SignWren API lets your code create envelopes from PDFs or account templates, send them for signature, follow their status and download the finished files. It calls the same service layer as the SignWren web app, so the same rules apply: plan limits, the excluded-document warning, the signer's consent step and the audit trail.

Nothing in the API can sign for a person. Every signature is made by the signer in the signing ceremony.

Authentication. Send Authorization: Bearer <key>. Keys are made by an owner or admin in Settings, shown once, and start with sw_live_ (live) or sw_test_ (sandbox). A sandbox key sees and makes sandbox envelopes only, and a live key live envelopes only. Live keys need a plan that includes the API (Pro or Business); sandbox keys work on every plan, Free included. A key acts as the member who made it, with that member's current role, plus the key's scopes. Cookies are never accepted on /v1.

OAuth 2.0 for apps. An app that works for other people's SignWren accounts (an automation tool, an MCP client, a partner's product) uses SignWren's OAuth 2.0 authorization server instead of asking for a key: the authorization code grant with PKCE (S256 only, for every client), refresh tokens that rotate on every use, and the API key scopes. The person sees a consent screen that names the app, the organization and each scope, and picks live or sandbox (live needs a plan with the API). Send the access token (swo_at_..., one hour) as Authorization: Bearer <token>; it acts as the person who allowed the app, with their current role, plus the scopes they allowed. Presenting a refresh token a second time revokes the whole grant. Redirect URIs are registered with SignWren and compared exactly (any port for an http://127.0.0.1 redirect of a native app). Endpoints, on the app's host: GET /oauth/authorize (the consent screen), POST /api/oauth/token, POST /api/oauth/revoke (RFC 7009), and metadata at /.well-known/oauth-authorization-server (RFC 8414). The authorization response carries iss (RFC 9207). Apps are registered by SignWren; there is no self-service registration yet. People see and remove apps under Settings, Connected apps.

Ids. Every id has a type prefix: env_ envelopes, doc_ documents, rcp_ recipients, fld_ fields, tpl_ templates, whk_ webhook endpoints, key_ API keys, aex_ audit exports, and sev_ / eev_ audit log events.

IP allowlists. When an organization turns on its IP allowlist (Settings, Business plan), its API keys and the access tokens of apps connected to it work only from the addresses on the list. A request from anywhere else is 403 IP_NOT_ALLOWED, whatever the key or token.

Field positions are PDF points (1/72 inch) with the origin at the top-left corner of the page as displayed; y grows downward. Pages are numbered from 1.

Errors always have the same body (see Error). Another organization's resource, or one in the other environment, is 404 NOT_FOUND, never 403.

Idempotency. Every POST accepts an Idempotency-Key header (1 to 255 printable ASCII characters). The first answer for a key is saved for 24 hours and replayed (with Idempotent-Replayed: true) when the same key is sent again by the same API key. The same key with a different request is 422 IDEMPOTENCY_MISMATCH; a second request while the first is still running is 409 IDEMPOTENCY_IN_PROGRESS and can be retried.

Rate limits. Proposed: 25 requests a second per live key and 10 per sandbox key, with a short burst, and a ceiling per organization. Every answer carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; a refused request is 429 RATE_LIMITED with Retry-After.

Pagination. List endpoints take limit (1 to 100, default 25) and starting_after (the id of the last item you have) and return { "object": "list", "data": [...], "has_more": true|false }, newest first.

Versioning. Additive changes (new optional fields, new endpoints, new event types) ship on /v1 without notice in the version; removing or renaming a field, changing a type or a default needs /v2. Every change is listed in the changelog the day it ships.

Hosts in the spec:

  • https://api.signwren.com/v1: Proposed public host (not live yet).
  • https://app.signwren.com/api/v1: The app's own host.

The machine-readable file is served by the API at /v1/openapi.json without a key.

Which endpoints are there?

38 operations, grouped as in the spec. Each lists the scope its key needs.

What is in Documents?

Upload a document

POST/v1/documents

createDocument · scope envelopes:write · takes Idempotency-Key

Upload one PDF, Word (.docx), JPEG or PNG file as multipart form data in the file field. The same checks as the web app apply: the type is read from the file's bytes, files can be up to 25 MB and 100 pages, Word files with macros are refused, and a PDF that already carries digital signatures is flagged (pre_signed). The document waits up to 24 hours to be put in an envelope with document_ids on POST /envelopes, and can be used once. Word files and images are converted to PDF after they are put in an envelope. Scope: envelopes:write.

Request body (multipart/form-data): object

FieldTypeNotes
file requiredfileThe file. Its name is kept as the document's file name.

Answer 201: The stored document, ready to be put in an envelope. UploadedDocument

Errors: 400, 401, 402, 403, 409, 413, 415, 422, 429, in the Error shape (what they mean).

Example answer
{
  "id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
  "object": "document",
  "filename": "lease-14-alder-court.pdf",
  "kind": "pdf",
  "status": "ready",
  "page_count": 2,
  "pages": [
    {
      "width": 612,
      "height": 792
    },
    {
      "width": 612,
      "height": 792
    }
  ],
  "sha256": "9b74c9897bac770ffc029102a200c5de7d4e6bdfa0e3f1d4b9a2f0c1e8d7c6b5",
  "size_bytes": 48213,
  "pre_signed": false,
  "warnings": [],
  "envelope_id": null,
  "created_at": "2026-10-01T14:05:00.000Z",
  "expires_at": "2026-10-02T14:05:00.000Z"
}

What is in Envelopes?

List envelopes

GET/v1/envelopes

listEnvelopes · scope envelopes:read

Envelopes the key's member can see, newest first. Filter by status, by an external reference to find "the envelope for this record", or by one metadata pair (metadata_key and metadata_value together, an exact match). Scope: envelopes:read.

ParameterInTypeNotes
limitqueryinteger
starting_afterqueryEnvelopeIdId of the last envelope you already have.
statusqueryEnvelopeStatus
updated_sincequerystringOnly envelopes that changed (any event: sent, viewed, signed, completed, voided and so on) at or after this time, as an RFC 3339 date-time with a time zone. For sync jobs that can't receive webhooks: keep the time you started the last run and pass it, minus a minute or two. Each item's updated_at says when it last changed.
external_ref.systemquerystringRequired with the other external_ref. filters.
external_ref.object_typequerystring
external_ref.object_idquerystring
metadata_keyquerystringA metadata key. Required with metadata_value; envelopes whose metadata has this exact pair.
metadata_valuequerystringThe value for metadata_key, matched exactly.

Answer 200: One page of envelopes. EnvelopeList

Errors: 400, 401, 402, 403, 422, 429, in the Error shape (what they mean).

Example answer
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "env_7d1e0c2b9a8f4e3d8c7b6a5f4e3d2c1b",
      "object": "envelope",
      "environment": "sandbox",
      "title": "Lease, 14 Alder Court",
      "status": "sent",
      "status_label": "Sent",
      "metadata": {
        "crm_deal": "D-1042"
      },
      "created_at": "2026-10-01T14:05:00.000Z",
      "updated_at": "2026-10-01T14:20:31.000Z",
      "sent_at": "2026-10-01T14:07:12.000Z",
      "completed_at": null,
      "expires_at": "2026-10-31T14:07:12.000Z",
      "recipients": [
        {
          "id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
          "name": "Priya Raman",
          "email": "priya@example.com",
          "role": "signer",
          "status": "notified",
          "display_status": "Emailed"
        }
      ]
    }
  ]
}

Create a draft envelope

POST/v1/envelopes

createEnvelope · scope envelopes:write · takes Idempotency-Key

Create a draft from documents uploaded with POST /documents, optionally with its recipients, fields, metadata and external references in the same call. Fields name their recipient by recipient_index (the position in this request's recipients) and their document by id. To start from an account template, use POST /templates/{template_id}/envelopes. Scope: envelopes:write.

Request body (application/json): CreateEnvelopeRequest

Answer 201: The draft, with its documents, recipients and fields. Envelope

Errors: 400, 401, 402, 403, 409, 422, 429, in the Error shape (what they mean).

Example request body
{
  "title": "Lease, 14 Alder Court",
  "message": "Hi Priya, here is the lease we talked about.",
  "document_ids": [
    "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788"
  ],
  "recipients": [
    {
      "name": "Priya Raman",
      "email": "priya@example.com",
      "role": "signer"
    }
  ],
  "fields": [
    {
      "document_id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
      "recipient_index": 0,
      "type": "signature",
      "page": 2,
      "x": 72,
      "y": 600,
      "width": 180,
      "height": 40
    }
  ],
  "metadata": {
    "crm_deal": "D-1042"
  },
  "external_refs": [
    {
      "system": "servicenow",
      "object_type": "sn_hr_core_case",
      "object_id": "9d385017c611228701d22104cc95c371"
    }
  ]
}
Example answer
{
  "id": "env_7d1e0c2b9a8f4e3d8c7b6a5f4e3d2c1b",
  "object": "envelope",
  "environment": "sandbox",
  "title": "Lease, 14 Alder Court",
  "message": "Hi Priya, here is the lease we talked about.",
  "status": "draft",
  "status_label": "Draft",
  "signing_order": "parallel",
  "expiry_days": 30,
  "expires_at": null,
  "reminders": {
    "enabled": true,
    "interval_days": 3,
    "max_count": 3
  },
  "metadata": {
    "crm_deal": "D-1042"
  },
  "external_refs": [
    {
      "system": "servicenow",
      "object_type": "sn_hr_core_case",
      "object_id": "9d385017c611228701d22104cc95c371",
      "url": null
    }
  ],
  "warnings": [],
  "template": null,
  "documents": [
    {
      "id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
      "object": "document",
      "position": 0,
      "filename": "lease-14-alder-court.pdf",
      "source_type": "pdf",
      "status": "ready",
      "page_count": 2,
      "pages": [
        {
          "width": 612,
          "height": 792
        },
        {
          "width": 612,
          "height": 792
        }
      ],
      "sha256": "9b74c9897bac770ffc029102a200c5de7d4e6bdfa0e3f1d4b9a2f0c1e8d7c6b5",
      "size_bytes": 48213,
      "pre_signed": false,
      "failure_reason": null
    }
  ],
  "recipients": [
    {
      "id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
      "object": "recipient",
      "name": "Priya Raman",
      "email": "priya@example.com",
      "role": "signer",
      "routing_order": 1,
      "status": "pending",
      "display_status": "Waiting",
      "signing_method": "email",
      "email_code": false,
      "sms_phone": null,
      "has_access_code": false,
      "template_role": null,
      "embedded": false,
      "client_user_id": null,
      "signature_meaning": null,
      "notified_at": null,
      "first_viewed_at": null,
      "signed_at": null,
      "declined_at": null,
      "decline_reason": null
    }
  ],
  "fields": [
    {
      "id": "fld_5e4d3c2b1a0948f7a6b5c4d3e2f1a0b9",
      "object": "field",
      "document_id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
      "recipient_id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
      "type": "signature",
      "page": 2,
      "x": 72,
      "y": 600,
      "width": 180,
      "height": 40,
      "required": true,
      "label": null,
      "placeholder": null,
      "max_length": null,
      "filled": false
    }
  ],
  "created_at": "2026-10-01T14:05:00.000Z",
  "sent_at": null,
  "completed_at": null,
  "voided_at": null,
  "void_reason": null,
  "declined_at": null,
  "expired_at": null,
  "final_sha256": null,
  "certificate_sha256": null,
  "signature_level": null
}

Get an envelope

GET/v1/envelopes/{envelope_id}

getEnvelope · scope envelopes:read

Status, recipients with their status, documents and fields. Scope envelopes:read.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId

Answer 200: The envelope. Envelope

Errors: 401, 402, 403, 404, 429, in the Error shape (what they mean).

Example answer
{
  "id": "env_7d1e0c2b9a8f4e3d8c7b6a5f4e3d2c1b",
  "object": "envelope",
  "environment": "sandbox",
  "title": "Lease, 14 Alder Court",
  "message": "Hi Priya, here is the lease we talked about.",
  "status": "draft",
  "status_label": "Draft",
  "signing_order": "parallel",
  "expiry_days": 30,
  "expires_at": null,
  "reminders": {
    "enabled": true,
    "interval_days": 3,
    "max_count": 3
  },
  "metadata": {
    "crm_deal": "D-1042"
  },
  "external_refs": [
    {
      "system": "servicenow",
      "object_type": "sn_hr_core_case",
      "object_id": "9d385017c611228701d22104cc95c371",
      "url": null
    }
  ],
  "warnings": [],
  "template": null,
  "documents": [
    {
      "id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
      "object": "document",
      "position": 0,
      "filename": "lease-14-alder-court.pdf",
      "source_type": "pdf",
      "status": "ready",
      "page_count": 2,
      "pages": [
        {
          "width": 612,
          "height": 792
        },
        {
          "width": 612,
          "height": 792
        }
      ],
      "sha256": "9b74c9897bac770ffc029102a200c5de7d4e6bdfa0e3f1d4b9a2f0c1e8d7c6b5",
      "size_bytes": 48213,
      "pre_signed": false,
      "failure_reason": null
    }
  ],
  "recipients": [
    {
      "id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
      "object": "recipient",
      "name": "Priya Raman",
      "email": "priya@example.com",
      "role": "signer",
      "routing_order": 1,
      "status": "pending",
      "display_status": "Waiting",
      "signing_method": "email",
      "email_code": false,
      "sms_phone": null,
      "has_access_code": false,
      "template_role": null,
      "embedded": false,
      "client_user_id": null,
      "signature_meaning": null,
      "notified_at": null,
      "first_viewed_at": null,
      "signed_at": null,
      "declined_at": null,
      "decline_reason": null
    }
  ],
  "fields": [
    {
      "id": "fld_5e4d3c2b1a0948f7a6b5c4d3e2f1a0b9",
      "object": "field",
      "document_id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
      "recipient_id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
      "type": "signature",
      "page": 2,
      "x": 72,
      "y": 600,
      "width": 180,
      "height": 40,
      "required": true,
      "label": null,
      "placeholder": null,
      "max_length": null,
      "filled": false
    }
  ],
  "created_at": "2026-10-01T14:05:00.000Z",
  "sent_at": null,
  "completed_at": null,
  "voided_at": null,
  "void_reason": null,
  "declined_at": null,
  "expired_at": null,
  "final_sha256": null,
  "certificate_sha256": null,
  "signature_level": null
}

Change a draft, or its metadata

PATCH/v1/envelopes/{envelope_id}

updateEnvelope · scope envelopes:write

Title, message, signing order, expiry and reminders can change while the envelope is a draft (409 ENVELOPE_LOCKED after sending). metadata can change at any time; it replaces the whole set. Scope: envelopes:write.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId

Request body (application/json): UpdateEnvelopeRequest

Answer 200: The envelope after the change. Envelope

Errors: 400, 401, 402, 403, 404, 409, 422, 429, in the Error shape (what they mean).

Example request body
{
  "title": "Lease, 14 Alder Court (revised)",
  "reminders": {
    "enabled": true,
    "interval_days": 2,
    "max_count": 3
  }
}
Example answer
{
  "id": "env_7d1e0c2b9a8f4e3d8c7b6a5f4e3d2c1b",
  "object": "envelope",
  "environment": "sandbox",
  "title": "Lease, 14 Alder Court",
  "message": "Hi Priya, here is the lease we talked about.",
  "status": "draft",
  "status_label": "Draft",
  "signing_order": "parallel",
  "expiry_days": 30,
  "expires_at": null,
  "reminders": {
    "enabled": true,
    "interval_days": 3,
    "max_count": 3
  },
  "metadata": {
    "crm_deal": "D-1042"
  },
  "external_refs": [
    {
      "system": "servicenow",
      "object_type": "sn_hr_core_case",
      "object_id": "9d385017c611228701d22104cc95c371",
      "url": null
    }
  ],
  "warnings": [],
  "template": null,
  "documents": [
    {
      "id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
      "object": "document",
      "position": 0,
      "filename": "lease-14-alder-court.pdf",
      "source_type": "pdf",
      "status": "ready",
      "page_count": 2,
      "pages": [
        {
          "width": 612,
          "height": 792
        },
        {
          "width": 612,
          "height": 792
        }
      ],
      "sha256": "9b74c9897bac770ffc029102a200c5de7d4e6bdfa0e3f1d4b9a2f0c1e8d7c6b5",
      "size_bytes": 48213,
      "pre_signed": false,
      "failure_reason": null
    }
  ],
  "recipients": [
    {
      "id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
      "object": "recipient",
      "name": "Priya Raman",
      "email": "priya@example.com",
      "role": "signer",
      "routing_order": 1,
      "status": "pending",
      "display_status": "Waiting",
      "signing_method": "email",
      "email_code": false,
      "sms_phone": null,
      "has_access_code": false,
      "template_role": null,
      "embedded": false,
      "client_user_id": null,
      "signature_meaning": null,
      "notified_at": null,
      "first_viewed_at": null,
      "signed_at": null,
      "declined_at": null,
      "decline_reason": null
    }
  ],
  "fields": [
    {
      "id": "fld_5e4d3c2b1a0948f7a6b5c4d3e2f1a0b9",
      "object": "field",
      "document_id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
      "recipient_id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
      "type": "signature",
      "page": 2,
      "x": 72,
      "y": 600,
      "width": 180,
      "height": 40,
      "required": true,
      "label": null,
      "placeholder": null,
      "max_length": null,
      "filled": false
    }
  ],
  "created_at": "2026-10-01T14:05:00.000Z",
  "sent_at": null,
  "completed_at": null,
  "voided_at": null,
  "void_reason": null,
  "declined_at": null,
  "expired_at": null,
  "final_sha256": null,
  "certificate_sha256": null,
  "signature_level": null
}

Send a draft for signature

POST/v1/envelopes/{envelope_id}/send

sendEnvelope · scope envelopes:write · takes Idempotency-Key

Sends exactly as the web app does. The sender's business details must be set in Settings first (422 SEND_BLOCKED names what is missing). If the document looks like a kind that electronic signature laws exclude, the answer is 422 WARNING_UNACKNOWLEDGED with the categories, unless acknowledge_warnings lists them; each acknowledgement is recorded in the audit trail with the key's name. A PDF with existing digital signatures needs confirm_pre_signed: true. Sending twice is refused by the envelope's state (409 INVALID_STATE), with or without an idempotency key. Scope envelopes:write.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId

Request body (application/json, optional): SendRequest

Answer 200: The envelope, now sent. Envelope

Errors: 400, 401, 402, 403, 404, 409, 422, 429, in the Error shape (what they mean).

Example request body
{
  "acknowledge_warnings": [
    "EXC_WILL"
  ]
}
Example answer
{
  "id": "env_7d1e0c2b9a8f4e3d8c7b6a5f4e3d2c1b",
  "object": "envelope",
  "environment": "sandbox",
  "title": "Lease, 14 Alder Court",
  "message": "Hi Priya, here is the lease we talked about.",
  "status": "draft",
  "status_label": "Draft",
  "signing_order": "parallel",
  "expiry_days": 30,
  "expires_at": null,
  "reminders": {
    "enabled": true,
    "interval_days": 3,
    "max_count": 3
  },
  "metadata": {
    "crm_deal": "D-1042"
  },
  "external_refs": [
    {
      "system": "servicenow",
      "object_type": "sn_hr_core_case",
      "object_id": "9d385017c611228701d22104cc95c371",
      "url": null
    }
  ],
  "warnings": [],
  "template": null,
  "documents": [
    {
      "id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
      "object": "document",
      "position": 0,
      "filename": "lease-14-alder-court.pdf",
      "source_type": "pdf",
      "status": "ready",
      "page_count": 2,
      "pages": [
        {
          "width": 612,
          "height": 792
        },
        {
          "width": 612,
          "height": 792
        }
      ],
      "sha256": "9b74c9897bac770ffc029102a200c5de7d4e6bdfa0e3f1d4b9a2f0c1e8d7c6b5",
      "size_bytes": 48213,
      "pre_signed": false,
      "failure_reason": null
    }
  ],
  "recipients": [
    {
      "id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
      "object": "recipient",
      "name": "Priya Raman",
      "email": "priya@example.com",
      "role": "signer",
      "routing_order": 1,
      "status": "pending",
      "display_status": "Waiting",
      "signing_method": "email",
      "email_code": false,
      "sms_phone": null,
      "has_access_code": false,
      "template_role": null,
      "embedded": false,
      "client_user_id": null,
      "signature_meaning": null,
      "notified_at": null,
      "first_viewed_at": null,
      "signed_at": null,
      "declined_at": null,
      "decline_reason": null
    }
  ],
  "fields": [
    {
      "id": "fld_5e4d3c2b1a0948f7a6b5c4d3e2f1a0b9",
      "object": "field",
      "document_id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
      "recipient_id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
      "type": "signature",
      "page": 2,
      "x": 72,
      "y": 600,
      "width": 180,
      "height": 40,
      "required": true,
      "label": null,
      "placeholder": null,
      "max_length": null,
      "filled": false
    }
  ],
  "created_at": "2026-10-01T14:05:00.000Z",
  "sent_at": null,
  "completed_at": null,
  "voided_at": null,
  "void_reason": null,
  "declined_at": null,
  "expired_at": null,
  "final_sha256": null,
  "certificate_sha256": null,
  "signature_level": null
}

Remind a recipient now

POST/v1/envelopes/{envelope_id}/remind

remindRecipient · scope envelopes:write · takes Idempotency-Key

Emails a reminder to a signer whose turn it is. Once every 24 hours per recipient (429 RATE_LIMITED with details.retry_at otherwise). Scope envelopes:write.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId

Request body (application/json): RemindRequest

Answer 200: The reminder is queued. Recipient

Errors: 400, 401, 402, 403, 404, 409, 422, 429, in the Error shape (what they mean).

Example request body
{
  "recipient_id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9"
}
Example answer
{
  "id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
  "object": "recipient",
  "name": "Priya Raman",
  "email": "priya@example.com",
  "role": "signer",
  "routing_order": 1,
  "status": "pending",
  "display_status": "Waiting",
  "signing_method": "email",
  "email_code": false,
  "sms_phone": null,
  "has_access_code": false,
  "template_role": null,
  "embedded": false,
  "client_user_id": null,
  "signature_meaning": null,
  "notified_at": null,
  "first_viewed_at": null,
  "signed_at": null,
  "declined_at": null,
  "decline_reason": null
}

Void an envelope that is out for signature

POST/v1/envelopes/{envelope_id}/void

voidEnvelope · scope envelopes:write · takes Idempotency-Key

Signing links stop working and signers who were emailed get a short notice with the reason. Scope envelopes:write.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId

Request body (application/json): VoidRequest

Answer 200: The envelope, now voided. Envelope

Errors: 400, 401, 402, 403, 404, 409, 422, 429, in the Error shape (what they mean).

Example request body
{
  "reason": "The rent amount was wrong. A corrected lease is on its way."
}
Example answer
{
  "id": "env_7d1e0c2b9a8f4e3d8c7b6a5f4e3d2c1b",
  "object": "envelope",
  "environment": "sandbox",
  "title": "Lease, 14 Alder Court",
  "message": "Hi Priya, here is the lease we talked about.",
  "status": "draft",
  "status_label": "Draft",
  "signing_order": "parallel",
  "expiry_days": 30,
  "expires_at": null,
  "reminders": {
    "enabled": true,
    "interval_days": 3,
    "max_count": 3
  },
  "metadata": {
    "crm_deal": "D-1042"
  },
  "external_refs": [
    {
      "system": "servicenow",
      "object_type": "sn_hr_core_case",
      "object_id": "9d385017c611228701d22104cc95c371",
      "url": null
    }
  ],
  "warnings": [],
  "template": null,
  "documents": [
    {
      "id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
      "object": "document",
      "position": 0,
      "filename": "lease-14-alder-court.pdf",
      "source_type": "pdf",
      "status": "ready",
      "page_count": 2,
      "pages": [
        {
          "width": 612,
          "height": 792
        },
        {
          "width": 612,
          "height": 792
        }
      ],
      "sha256": "9b74c9897bac770ffc029102a200c5de7d4e6bdfa0e3f1d4b9a2f0c1e8d7c6b5",
      "size_bytes": 48213,
      "pre_signed": false,
      "failure_reason": null
    }
  ],
  "recipients": [
    {
      "id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
      "object": "recipient",
      "name": "Priya Raman",
      "email": "priya@example.com",
      "role": "signer",
      "routing_order": 1,
      "status": "pending",
      "display_status": "Waiting",
      "signing_method": "email",
      "email_code": false,
      "sms_phone": null,
      "has_access_code": false,
      "template_role": null,
      "embedded": false,
      "client_user_id": null,
      "signature_meaning": null,
      "notified_at": null,
      "first_viewed_at": null,
      "signed_at": null,
      "declined_at": null,
      "decline_reason": null
    }
  ],
  "fields": [
    {
      "id": "fld_5e4d3c2b1a0948f7a6b5c4d3e2f1a0b9",
      "object": "field",
      "document_id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
      "recipient_id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
      "type": "signature",
      "page": 2,
      "x": 72,
      "y": 600,
      "width": 180,
      "height": 40,
      "required": true,
      "label": null,
      "placeholder": null,
      "max_length": null,
      "filled": false
    }
  ],
  "created_at": "2026-10-01T14:05:00.000Z",
  "sent_at": null,
  "completed_at": null,
  "voided_at": null,
  "void_reason": null,
  "declined_at": null,
  "expired_at": null,
  "final_sha256": null,
  "certificate_sha256": null,
  "signature_level": null
}

The audit trail

GET/v1/envelopes/{envelope_id}/events

listEnvelopeEvents · scope envelopes:read

Every audit event in order, with the result of recomputing the envelope's hash chain. The whole trail is returned in one page. Scope envelopes:read.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId

Answer 200: The audit trail. EventList

Errors: 401, 402, 403, 404, 429, in the Error shape (what they mean).

Example answer
{
  "object": "list",
  "has_more": false,
  "chain": {
    "valid": true,
    "length": 2,
    "head_hash": "4f0c3d2b1a09887766554433221100ffeeddccbbaa99887766554433221100ff",
    "broken_at_seq": null,
    "reason": null
  },
  "data": [
    {
      "seq": 1,
      "type": "envelope.created",
      "description": "Draft created",
      "occurred_at": "2026-10-01T14:05:00.000Z",
      "actor_type": "user",
      "recipient_id": null,
      "ip_address": "203.0.113.7",
      "user_agent": "my-crm/2.1",
      "details": {
        "apiKey": {
          "id": "2c9e1d4b-7a3f-4e8d-9b6c-5a4f3e2d1c0b",
          "name": "CRM sync"
        }
      }
    },
    {
      "seq": 2,
      "type": "document.uploaded",
      "description": "Added lease-14-alder-court.pdf",
      "occurred_at": "2026-10-01T14:05:01.000Z",
      "actor_type": "user",
      "recipient_id": null,
      "ip_address": "203.0.113.7",
      "user_agent": "my-crm/2.1",
      "details": {
        "filename": "lease-14-alder-court.pdf"
      }
    }
  ]
}

What is in Recipients?

Add a recipient to a draft

POST/v1/envelopes/{envelope_id}/recipients

addRecipient · scope envelopes:write · takes Idempotency-Key

Same limits as the editor (up to 10 recipients). Scope envelopes:write.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId

Request body (application/json): RecipientInput

Answer 201: The new recipient. Recipient

Errors: 400, 401, 402, 403, 404, 409, 422, 429, in the Error shape (what they mean).

Example request body
{
  "name": "Tom Brennan",
  "email": "tom@example.com",
  "role": "signer",
  "routing_order": 2
}
Example answer
{
  "id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
  "object": "recipient",
  "name": "Priya Raman",
  "email": "priya@example.com",
  "role": "signer",
  "routing_order": 1,
  "status": "pending",
  "display_status": "Waiting",
  "signing_method": "email",
  "email_code": false,
  "sms_phone": null,
  "has_access_code": false,
  "template_role": null,
  "embedded": false,
  "client_user_id": null,
  "signature_meaning": null,
  "notified_at": null,
  "first_viewed_at": null,
  "signed_at": null,
  "declined_at": null,
  "decline_reason": null
}

Change a recipient on a draft

PATCH/v1/envelopes/{envelope_id}/recipients/{recipient_id}

updateRecipient · scope envelopes:write

Only the properties sent change. Scope envelopes:write.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId
recipient_id requiredpathRecipientId

Request body (application/json): RecipientPatch

Answer 200: The recipient after the change. Recipient

Errors: 400, 401, 402, 403, 404, 409, 422, 429, in the Error shape (what they mean).

Example request body
{
  "email": "priya.raman@example.com"
}
Example answer
{
  "id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
  "object": "recipient",
  "name": "Priya Raman",
  "email": "priya@example.com",
  "role": "signer",
  "routing_order": 1,
  "status": "pending",
  "display_status": "Waiting",
  "signing_method": "email",
  "email_code": false,
  "sms_phone": null,
  "has_access_code": false,
  "template_role": null,
  "embedded": false,
  "client_user_id": null,
  "signature_meaning": null,
  "notified_at": null,
  "first_viewed_at": null,
  "signed_at": null,
  "declined_at": null,
  "decline_reason": null
}

Remove a recipient from a draft

DELETE/v1/envelopes/{envelope_id}/recipients/{recipient_id}

deleteRecipient · scope envelopes:write

Their fields are removed with them. Scope envelopes:write.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId
recipient_id requiredpathRecipientId

Answer 200: Removed. Deleted

Errors: 401, 402, 403, 404, 409, 429, in the Error shape (what they mean).

Example answer
{
  "id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
  "object": "recipient",
  "deleted": true
}

What is in Fields?

Add a field to a draft

POST/v1/envelopes/{envelope_id}/fields

addField · scope envelopes:write · takes Idempotency-Key

Positions are PDF points from the top-left of the page as displayed. Same rules as the editor: minimum sizes, inside the page, at most 500 fields, signature fields always required. After sending, 409 ENVELOPE_LOCKED. Scope envelopes:write.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId

Request body (application/json): FieldInput

Answer 201: The new field. Field

Errors: 400, 401, 402, 403, 404, 409, 422, 429, in the Error shape (what they mean).

Example request body
{
  "document_id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
  "recipient_id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
  "type": "date_signed",
  "page": 2,
  "x": 300,
  "y": 650,
  "width": 110,
  "height": 18
}
Example answer
{
  "id": "fld_5e4d3c2b1a0948f7a6b5c4d3e2f1a0b9",
  "object": "field",
  "document_id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
  "recipient_id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
  "type": "date_signed",
  "page": 2,
  "x": 300,
  "y": 650,
  "width": 110,
  "height": 18,
  "required": true,
  "label": null,
  "placeholder": null,
  "max_length": null,
  "filled": false
}

Move, resize or change a field on a draft

PATCH/v1/envelopes/{envelope_id}/fields/{field_id}

updateField · scope envelopes:write

Only the properties sent change. Scope envelopes:write.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId
field_id requiredpathFieldId

Request body (application/json): FieldPatch

Answer 200: The field after the change. Field

Errors: 400, 401, 402, 403, 404, 409, 422, 429, in the Error shape (what they mean).

Example request body
{
  "x": 320,
  "y": 655
}
Example answer
{
  "id": "fld_5e4d3c2b1a0948f7a6b5c4d3e2f1a0b9",
  "object": "field",
  "document_id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
  "recipient_id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
  "type": "date_signed",
  "page": 2,
  "x": 300,
  "y": 650,
  "width": 110,
  "height": 18,
  "required": true,
  "label": null,
  "placeholder": null,
  "max_length": null,
  "filled": false
}

Remove a field from a draft

DELETE/v1/envelopes/{envelope_id}/fields/{field_id}

deleteField · scope envelopes:write

Scope envelopes:write.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId
field_id requiredpathFieldId

Answer 200: Removed. Deleted

Errors: 401, 402, 403, 404, 409, 429, in the Error shape (what they mean).

Example answer
{
  "id": "fld_5e4d3c2b1a0948f7a6b5c4d3e2f1a0b9",
  "object": "field",
  "deleted": true
}

What is in Downloads?

Download an original document

GET/v1/envelopes/{envelope_id}/documents/{document_id}

downloadDocument · scope envelopes:read

The document as it was prepared for signing, at any status. Each download is recorded in the audit trail. Scope envelopes:read.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId
document_id requiredpathDocumentId

Answer 200: The PDF, byte for byte as stored. file

Errors: 401, 402, 403, 404, 429, in the Error shape (what they mean).

Download the signed, sealed PDF

GET/v1/envelopes/{envelope_id}/documents/combined

downloadCombinedDocument · scope envelopes:read

The finished PDF of a completed envelope, byte for byte as stored, with ETag and Repr-Digest set from its SHA-256. Once a year SignWren adds a fresh document timestamp to stored sealed PDFs so they stay checkable for longer; after that this answers with the newest copy, so its SHA-256 differs from the envelope's final_sha256 (the original). Signatures and content are the same, and both copies pass the public check page. 409 INVALID_STATE until the envelope is completed. Each download is recorded in the audit trail. Scope envelopes:read.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId

Answer 200: The PDF, byte for byte as stored. file

Errors: 401, 402, 403, 404, 409, 429, in the Error shape (what they mean).

Download the certificate of completion

GET/v1/envelopes/{envelope_id}/certificate

downloadCertificate · scope envelopes:read

Same rules as the combined PDF. Scope envelopes:read.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId

Answer 200: The PDF, byte for byte as stored. file

Errors: 401, 402, 403, 404, 409, 429, in the Error shape (what they mean).

What is in Templates?

List account templates

GET/v1/templates

listTemplates · scope templates:read

Account templates the key's member can see, newest first, with their roles and sender fields. Scope templates:read.

ParameterInTypeNotes
limitqueryinteger
starting_afterqueryTemplateIdId of the last template you already have.

Answer 200: One page of templates. TemplateList

Errors: 400, 401, 402, 403, 422, 429, in the Error shape (what they mean).

Example answer
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "tpl_1c2d3e4f5a6b47c8d9e0f1a2b3c4d5e6",
      "object": "template",
      "name": "Residential lease",
      "visibility": "org",
      "current_version": 3,
      "signing_order": "sequential",
      "message": "Please review and sign the lease.",
      "expiry_days": 30,
      "roles": [
        {
          "id": "tenant",
          "name": "Tenant",
          "kind": "signer",
          "routing_order": 1,
          "optional": false
        },
        {
          "id": "landlord",
          "name": "Landlord",
          "kind": "signer",
          "routing_order": 2,
          "optional": false
        }
      ],
      "sender_fields": [
        {
          "id": "monthly_rent",
          "label": "Monthly rent",
          "fill": "prefilled",
          "required": true,
          "default_value": null
        }
      ],
      "merge_fields": [
        "tenant.full_name"
      ],
      "merge_field_mappings": [
        {
          "name": "tenant.full_name",
          "target_kind": "placeholder",
          "target": "[Tenant's full legal name]",
          "required": true
        }
      ],
      "documents": [
        {
          "filename": "lease.pdf",
          "page_count": 4
        }
      ],
      "created_at": "2026-09-20T10:00:00.000Z",
      "updated_at": "2026-09-28T16:30:00.000Z"
    }
  ]
}

Get an account template

GET/v1/templates/{template_id}

getTemplate · scope templates:read

The current version's roles, sender fields and settings. Scope templates:read.

ParameterInTypeNotes
template_id requiredpathTemplateId

Answer 200: The template. Template

Errors: 401, 402, 403, 404, 429, in the Error shape (what they mean).

Example answer
{
  "id": "tpl_1c2d3e4f5a6b47c8d9e0f1a2b3c4d5e6",
  "object": "template",
  "name": "Residential lease",
  "visibility": "org",
  "current_version": 3,
  "signing_order": "sequential",
  "message": "Please review and sign the lease.",
  "expiry_days": 30,
  "roles": [
    {
      "id": "tenant",
      "name": "Tenant",
      "kind": "signer",
      "routing_order": 1,
      "optional": false
    },
    {
      "id": "landlord",
      "name": "Landlord",
      "kind": "signer",
      "routing_order": 2,
      "optional": false
    }
  ],
  "sender_fields": [
    {
      "id": "monthly_rent",
      "label": "Monthly rent",
      "fill": "prefilled",
      "required": true,
      "default_value": null
    }
  ],
  "merge_fields": [
    "tenant.full_name"
  ],
  "merge_field_mappings": [
    {
      "name": "tenant.full_name",
      "target_kind": "placeholder",
      "target": "[Tenant's full legal name]",
      "required": true
    }
  ],
  "documents": [
    {
      "filename": "lease.pdf",
      "page_count": 4
    }
  ],
  "created_at": "2026-09-20T10:00:00.000Z",
  "updated_at": "2026-09-28T16:30:00.000Z"
}

Create (and optionally send) an envelope from a template

POST/v1/templates/{template_id}/envelopes

createEnvelopeFromTemplate · scope templates:read envelopes:write · takes Idempotency-Key

A draft from the template's current version: one person per role, values for sender fields (drawn into the document) and prefilled fields (shown to the signer, who can change them). With send: true the draft is sent in the same request, with the same rules as POST /envelopes/{envelope_id}/send; if sending is refused, the draft stays and the error names it in details.envelope_id. Scopes templates:read and envelopes:write.

Merge fields: merge_fields fills the template's mapped placeholders (the text is laid out again with the values) and mapped sender or prefilled fields. The answer's merge lists what was applied, ignored and missing. A required merge field without a value keeps the envelope a draft; with send: true that is 422 MERGE_FIELDS_MISSING with details.envelope_id and details.missing_merge_fields. merge_from_record: true also reads values from the record in the first external_refs entry through the account's connection to that system (503 INTEGRATIONS_NOT_CONFIGURED when the server has no connections set up).

ParameterInTypeNotes
template_id requiredpathTemplateId

Request body (application/json): CreateFromTemplateRequest

Answer 201: The new envelope (sent when `send` was true). Envelope

Errors: 400, 401, 402, 403, 404, 409, 422, 429, in the Error shape (what they mean).

Example request body
{
  "recipients": [
    {
      "role_id": "tenant",
      "name": "Priya Raman",
      "email": "priya@example.com"
    },
    {
      "role_id": "landlord",
      "name": "Dana Whitfield",
      "email": "dana@example.com"
    }
  ],
  "prefill": {
    "monthly_rent": "1,450"
  },
  "merge_fields": {
    "tenant.full_name": "Priya Raman"
  },
  "send": true,
  "metadata": {
    "crm_deal": "D-1042"
  }
}
Example answer
{
  "id": "env_7d1e0c2b9a8f4e3d8c7b6a5f4e3d2c1b",
  "object": "envelope",
  "environment": "sandbox",
  "title": "Lease, 14 Alder Court",
  "message": "Hi Priya, here is the lease we talked about.",
  "status": "draft",
  "status_label": "Draft",
  "signing_order": "parallel",
  "expiry_days": 30,
  "expires_at": null,
  "reminders": {
    "enabled": true,
    "interval_days": 3,
    "max_count": 3
  },
  "metadata": {
    "crm_deal": "D-1042"
  },
  "external_refs": [
    {
      "system": "servicenow",
      "object_type": "sn_hr_core_case",
      "object_id": "9d385017c611228701d22104cc95c371",
      "url": null
    }
  ],
  "warnings": [],
  "template": null,
  "documents": [
    {
      "id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
      "object": "document",
      "position": 0,
      "filename": "lease-14-alder-court.pdf",
      "source_type": "pdf",
      "status": "ready",
      "page_count": 2,
      "pages": [
        {
          "width": 612,
          "height": 792
        },
        {
          "width": 612,
          "height": 792
        }
      ],
      "sha256": "9b74c9897bac770ffc029102a200c5de7d4e6bdfa0e3f1d4b9a2f0c1e8d7c6b5",
      "size_bytes": 48213,
      "pre_signed": false,
      "failure_reason": null
    }
  ],
  "recipients": [
    {
      "id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
      "object": "recipient",
      "name": "Priya Raman",
      "email": "priya@example.com",
      "role": "signer",
      "routing_order": 1,
      "status": "pending",
      "display_status": "Waiting",
      "signing_method": "email",
      "email_code": false,
      "sms_phone": null,
      "has_access_code": false,
      "template_role": null,
      "embedded": false,
      "client_user_id": null,
      "signature_meaning": null,
      "notified_at": null,
      "first_viewed_at": null,
      "signed_at": null,
      "declined_at": null,
      "decline_reason": null
    }
  ],
  "fields": [
    {
      "id": "fld_5e4d3c2b1a0948f7a6b5c4d3e2f1a0b9",
      "object": "field",
      "document_id": "doc_3f1c2a9e8b7d4c6fa1e2d3c4b5a69788",
      "recipient_id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
      "type": "signature",
      "page": 2,
      "x": 72,
      "y": 600,
      "width": 180,
      "height": 40,
      "required": true,
      "label": null,
      "placeholder": null,
      "max_length": null,
      "filled": false
    }
  ],
  "created_at": "2026-10-01T14:05:00.000Z",
  "sent_at": null,
  "completed_at": null,
  "voided_at": null,
  "void_reason": null,
  "declined_at": null,
  "expired_at": null,
  "final_sha256": null,
  "certificate_sha256": null,
  "signature_level": null
}

What is in Webhooks?

Signed HTTPS notifications when envelopes change (P3-HOOK).

List webhook endpoints

GET/v1/webhook-endpoints

listWebhookEndpoints · scope webhooks:manage

The endpoints of the key's environment (a sandbox key sees sandbox endpoints only), newest first, always as one page. Secrets are never included. An app connected with OAuth sees only the endpoints it made itself (app names it); keys and the web app see every endpoint, the apps' ones included. Scope: webhooks:manage.

Answer 200: The endpoints. WebhookEndpointList

Errors: 401, 403, 429, in the Error shape (what they mean).

Example answer
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "whk_4b3a2f1e0d9c48b7a6f5e4d3c2b1a090",
      "object": "webhook_endpoint",
      "url": "https://example.com/signwren/webhook",
      "description": "CRM sync",
      "events": [
        "envelope.completed",
        "recipient.signed"
      ],
      "environment": "sandbox",
      "livemode": false,
      "status": "enabled",
      "disabled_reason": null,
      "disabled_at": null,
      "secret_hint": "whsec_...Q2xR",
      "rotation_ends_at": null,
      "app": null,
      "created_at": "2026-10-01T14:05:00.000Z",
      "updated_at": "2026-10-01T14:05:00.000Z"
    }
  ]
}

Add a webhook endpoint

POST/v1/webhook-endpoints

createWebhookEndpoint · scope webhooks:manage · takes Idempotency-Key

Subscribe an https:// URL to envelope events in the key's environment. The URL must resolve only to public addresses (private, loopback, link-local and cloud metadata ranges are refused, here and again before every delivery) and redirects are never followed. The answer carries the signing secret once; it is never shown again (rotate it if you lose it). Live endpoints need a plan that includes the API; sandbox endpoints work on every plan. Scope: webhooks:manage.

Endpoints made by an app. When an app connected with OAuth makes an endpoint (an automation tool subscribing to an event, for example), the endpoint belongs to that connection: it carries app, only that connection can see and change it, and SignWren deletes it when the connection ends (someone removes the app in Settings, the app revokes its token, or the person who allowed it leaves the account) or when the receiver answers a delivery with 410 Gone. Each environment has room for 16 endpoints made with keys or in the app, and each app connection for 50 of its own.

Request body (application/json): CreateWebhookEndpointRequest

Answer 201: The endpoint, with its signing secret (shown this once). WebhookEndpointWithSecret

Errors: 400, 401, 402, 403, 409, 422, 429, in the Error shape (what they mean).

Example request body
{
  "url": "https://example.com/signwren/webhook",
  "events": [
    "envelope.completed",
    "recipient.signed"
  ],
  "description": "CRM sync"
}
Example answer
{
  "id": "whk_4b3a2f1e0d9c48b7a6f5e4d3c2b1a090",
  "object": "webhook_endpoint",
  "url": "https://example.com/signwren/webhook",
  "description": "CRM sync",
  "events": [
    "envelope.completed",
    "recipient.signed"
  ],
  "environment": "sandbox",
  "livemode": false,
  "status": "enabled",
  "disabled_reason": null,
  "disabled_at": null,
  "secret_hint": "whsec_...Q2xR",
  "rotation_ends_at": null,
  "app": null,
  "created_at": "2026-10-01T14:05:00.000Z",
  "updated_at": "2026-10-01T14:05:00.000Z",
  "secret": "whsec_ZXhhbXBsZSBvbmx5LCBub3QgYSByZWFsIHdlYmhvb2s"
}

Get a webhook endpoint

GET/v1/webhook-endpoints/{webhook_id}

getWebhookEndpoint · scope webhooks:manage

One endpoint of the key's environment. Scope webhooks:manage.

ParameterInTypeNotes
webhook_id requiredpathWebhookEndpointId

Answer 200: The endpoint. WebhookEndpoint

Errors: 401, 403, 404, 429, in the Error shape (what they mean).

Example answer
{
  "id": "whk_4b3a2f1e0d9c48b7a6f5e4d3c2b1a090",
  "object": "webhook_endpoint",
  "url": "https://example.com/signwren/webhook",
  "description": "CRM sync",
  "events": [
    "envelope.completed",
    "recipient.signed"
  ],
  "environment": "sandbox",
  "livemode": false,
  "status": "enabled",
  "disabled_reason": null,
  "disabled_at": null,
  "secret_hint": "whsec_...Q2xR",
  "rotation_ends_at": null,
  "app": null,
  "created_at": "2026-10-01T14:05:00.000Z",
  "updated_at": "2026-10-01T14:05:00.000Z"
}

Change a webhook endpoint, or turn it off and on

PATCH/v1/webhook-endpoints/{webhook_id}

updateWebhookEndpoint · scope webhooks:manage

Change the URL (checked again), the events or the description, or set enabled. An endpoint that failed every delivery for 3 days is turned off by SignWren (status: disabled, disabled_reason: failing); enabled: true turns it back on. Scope: webhooks:manage.

ParameterInTypeNotes
webhook_id requiredpathWebhookEndpointId

Request body (application/json): UpdateWebhookEndpointRequest

Answer 200: The endpoint after the change. WebhookEndpoint

Errors: 400, 401, 403, 404, 422, 429, in the Error shape (what they mean).

Example request body
{
  "enabled": true
}
Example answer
{
  "id": "whk_4b3a2f1e0d9c48b7a6f5e4d3c2b1a090",
  "object": "webhook_endpoint",
  "url": "https://example.com/signwren/webhook",
  "description": "CRM sync",
  "events": [
    "envelope.completed",
    "recipient.signed"
  ],
  "environment": "sandbox",
  "livemode": false,
  "status": "enabled",
  "disabled_reason": null,
  "disabled_at": null,
  "secret_hint": "whsec_...Q2xR",
  "rotation_ends_at": null,
  "app": null,
  "created_at": "2026-10-01T14:05:00.000Z",
  "updated_at": "2026-10-01T14:05:00.000Z"
}

Delete a webhook endpoint

DELETE/v1/webhook-endpoints/{webhook_id}

deleteWebhookEndpoint · scope webhooks:manage

The endpoint and its delivery log are deleted. Scope webhooks:manage.

ParameterInTypeNotes
webhook_id requiredpathWebhookEndpointId

Answer 200: Deleted. WebhookEndpointDeleted

Errors: 401, 403, 404, 429, in the Error shape (what they mean).

Example answer
{
  "id": "whk_4b3a2f1e0d9c48b7a6f5e4d3c2b1a090",
  "object": "webhook_endpoint",
  "deleted": true
}

Rotate an endpoint's signing secret

POST/v1/webhook-endpoints/{webhook_id}/rotate-secret

rotateWebhookSecret · scope webhooks:manage · takes Idempotency-Key

Makes a new secret and returns it once. For 24 hours the old secret signs as well, so each delivery carries two v1= signatures and receivers can switch over without dropping events. expire_now: true stops the old secret at once. Scope: webhooks:manage.

ParameterInTypeNotes
webhook_id requiredpathWebhookEndpointId

Request body (application/json, optional): RotateWebhookSecretRequest

Answer 200: The endpoint with its new secret (shown this once). WebhookEndpointWithSecret

Errors: 400, 401, 403, 404, 429, in the Error shape (what they mean).

Example request body
{
  "expire_now": false
}
Example answer
{
  "id": "whk_4b3a2f1e0d9c48b7a6f5e4d3c2b1a090",
  "object": "webhook_endpoint",
  "url": "https://example.com/signwren/webhook",
  "description": "CRM sync",
  "events": [
    "envelope.completed",
    "recipient.signed"
  ],
  "environment": "sandbox",
  "livemode": false,
  "status": "enabled",
  "disabled_reason": null,
  "disabled_at": null,
  "secret_hint": "whsec_...Q2xR",
  "rotation_ends_at": "2026-10-02T14:05:00.000Z",
  "app": null,
  "created_at": "2026-10-01T14:05:00.000Z",
  "updated_at": "2026-10-01T14:05:00.000Z",
  "secret": "whsec_YW5vdGhlciBleGFtcGxlLCBub3QgYSByZWFsIHNlY3J"
}

Send a test event

POST/v1/webhook-endpoints/{webhook_id}/test

sendTestWebhookEvent · scope webhooks:manage · takes Idempotency-Key

Queues one webhook.test event to this endpoint; it goes out within seconds and appears in the delivery log. A test event is tried once. The endpoint must be on (409 otherwise). Scope: webhooks:manage.

ParameterInTypeNotes
webhook_id requiredpathWebhookEndpointId

Answer 202: The queued delivery. WebhookDelivery

Errors: 401, 403, 404, 409, 429, in the Error shape (what they mean).

Example answer
{
  "id": "whd_9a8b7c6d5e4f40312a3b4c5d6e7f8091",
  "object": "webhook_delivery",
  "webhook_endpoint_id": "whk_4b3a2f1e0d9c48b7a6f5e4d3c2b1a090",
  "event_id": "evt_0b9d6a512f0c4d559b2f7a0d6c1e2f30",
  "event_type": "webhook.test",
  "environment": "sandbox",
  "livemode": false,
  "status": "pending",
  "attempts": 0,
  "next_attempt_at": null,
  "last_status_code": 200,
  "last_error": null,
  "delivered_at": "2026-10-01T14:20:31.000Z",
  "is_test": true,
  "resend_of": null,
  "created_at": "2026-10-01T14:20:00.000Z",
  "attempt_log": []
}

The delivery log

GET/v1/webhook-endpoints/{webhook_id}/deliveries

listWebhookDeliveries · scope webhooks:manage

Deliveries to this endpoint, newest first, each with every attempt: time, status code, how long it took and the first 1 KB of the response body. Deliveries are kept for 30 days. Scope: webhooks:manage.

ParameterInTypeNotes
webhook_id requiredpathWebhookEndpointId
limitqueryinteger
starting_afterqueryWebhookDeliveryIdId of the last delivery you already have.

Answer 200: One page of deliveries. WebhookDeliveryList

Errors: 400, 401, 403, 404, 422, 429, in the Error shape (what they mean).

Example answer
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "whd_9a8b7c6d5e4f40312a3b4c5d6e7f8091",
      "object": "webhook_delivery",
      "webhook_endpoint_id": "whk_4b3a2f1e0d9c48b7a6f5e4d3c2b1a090",
      "event_id": "evt_0b9d6a512f0c4d559b2f7a0d6c1e2f30",
      "event_type": "envelope.completed",
      "environment": "sandbox",
      "livemode": false,
      "status": "delivered",
      "attempts": 2,
      "next_attempt_at": null,
      "last_status_code": 200,
      "last_error": null,
      "delivered_at": "2026-10-01T14:20:31.000Z",
      "is_test": false,
      "resend_of": null,
      "created_at": "2026-10-01T14:20:00.000Z",
      "attempt_log": [
        {
          "attempt": 1,
          "at": "2026-10-01T14:20:00.400Z",
          "duration_ms": 10004,
          "status_code": null,
          "error": "timeout: no complete answer within 10 s",
          "response_excerpt": null,
          "manual": false
        },
        {
          "attempt": 2,
          "at": "2026-10-01T14:20:30.900Z",
          "duration_ms": 182,
          "status_code": 200,
          "error": null,
          "response_excerpt": "ok",
          "manual": false
        }
      ]
    }
  ]
}

Resend a delivery

POST/v1/webhook-endpoints/{webhook_id}/deliveries/{delivery_id}/resend

resendWebhookDelivery · scope webhooks:manage · takes Idempotency-Key

Sends a finished delivery again, with the same event id and body, as a new entry in the log. Not while it is still being retried, and not to an endpoint that is off (409). Scope: webhooks:manage.

ParameterInTypeNotes
webhook_id requiredpathWebhookEndpointId
delivery_id requiredpathWebhookDeliveryId

Answer 202: The new, queued delivery. WebhookDelivery

Errors: 401, 403, 404, 409, 429, in the Error shape (what they mean).

Example answer
{
  "id": "whd_0f1e2d3c4b5a49687f6e5d4c3b2a1908",
  "object": "webhook_delivery",
  "webhook_endpoint_id": "whk_4b3a2f1e0d9c48b7a6f5e4d3c2b1a090",
  "event_id": "evt_0b9d6a512f0c4d559b2f7a0d6c1e2f30",
  "event_type": "envelope.completed",
  "environment": "sandbox",
  "livemode": false,
  "status": "pending",
  "attempts": 0,
  "next_attempt_at": null,
  "last_status_code": 200,
  "last_error": null,
  "delivered_at": "2026-10-01T14:20:31.000Z",
  "is_test": false,
  "resend_of": "whd_9a8b7c6d5e4f40312a3b4c5d6e7f8091",
  "created_at": "2026-10-01T14:20:00.000Z",
  "attempt_log": []
}

What is in Embedded?

Signing and sending inside your own app (P3-EMB). Add the sites your app runs on in Settings, under Embedding (separately for live and sandbox). Then ask for a short-lived URL and either put it in an iframe on one of those sites or send the person to it.

Get a signing URL for an embedded signer

POST/v1/envelopes/{envelope_id}/recipients/{recipient_id}/signing_url

createSigningUrl · scope envelopes:write · takes Idempotency-Key

For a recipient added with embedded: true on a sent envelope, when it is their turn. The URL must be opened within 5 minutes and then works for the browser that opened it only. Asking again stops earlier embedded URLs for the same signer. frame_origin picks one of the sites allowed in Settings (needed when more than one is allowed); return_url must be on one of them. Errors: 409 INVALID_STATE (not sent, finished, or not an embedded signer), 403 NOT_YOUR_TURN, 422 VALIDATION with details.reason EMBED_ORIGINS_REQUIRED, EMBED_ORIGIN_NOT_ALLOWED, EMBED_ORIGIN_REQUIRED or RETURN_URL_NOT_ALLOWED. Scope envelopes:write.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId
recipient_id requiredpathRecipientId

Request body (application/json, optional): EmbedUrlRequest

Answer 201: The signing URL. SigningUrl

Errors: 400, 401, 402, 403, 404, 409, 422, 429, in the Error shape (what they mean).

Example request body
{
  "frame_origin": "https://app.example.com",
  "return_url": "https://app.example.com/leases/4471"
}
Example answer
{
  "object": "signing_url",
  "url": "https://app.signwren.com/embedded/sign/q3Jr9xWbVv0nF2yQe1Lk8sPz4tUa7cMd6hGi5oRj0bE",
  "expires_at": "2026-10-01T15:05:00.000Z",
  "envelope_id": "env_7d1e0c2b9a8f4e3d8c7b6a5f4e3d2c1b",
  "recipient_id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
  "frame_origin": "https://app.example.com",
  "return_url": "https://app.example.com/leases/4471",
  "environment": "sandbox"
}

Get a URL to the field editor for a draft

POST/v1/envelopes/{envelope_id}/sender_view_url

createSenderViewUrl · scope envelopes:write · takes Idempotency-Key

A URL to place fields on this draft and send it, inside your app. It must be opened within 5 minutes, works for the browser that opened it only (for an hour), reaches this one draft and nothing else, and acts as this API key, or as the app and the person behind an OAuth access token (checked again on every request, so revoking the app ends the view). Pressing Send posts signwren:sent to the parent window. frame_origin must be one of the sites allowed in Settings; only SignWren's own Outlook and Gmail add-ins may name SignWren's own address instead. Errors: 409 ENVELOPE_LOCKED (already sent), 422 VALIDATION as for signing URLs. Scope envelopes:write.

ParameterInTypeNotes
envelope_id requiredpathEnvelopeId

Request body (application/json, optional): EmbedUrlRequest

Answer 201: The sending view URL. SenderViewUrl

Errors: 400, 401, 402, 403, 404, 409, 422, 429, in the Error shape (what they mean).

Example request body
{
  "frame_origin": "https://app.example.com"
}
Example answer
{
  "object": "sender_view_url",
  "url": "https://app.signwren.com/embedded/send/Yk2p8QwLx4vN0aT7rB1sC9dE3fG6hJ5mZ2uV8iO1qW4",
  "expires_at": "2026-10-01T15:05:00.000Z",
  "envelope_id": "env_7d1e0c2b9a8f4e3d8c7b6a5f4e3d2c1b",
  "frame_origin": "https://app.example.com",
  "return_url": null,
  "environment": "live"
}

What is in Spec?

This specification as JSON

GET/v1/openapi.json

getOpenApiSpec · no key needed

Answer 200: The OpenAPI document. object

Example answer
{
  "openapi": "3.1.0",
  "info": {
    "title": "SignWren API",
    "version": "1.0.0"
  }
}

What is in Account?

The organization, environment and person a key or access token acts for.

Who this key or access token acts for

GET/v1/me

getMe · scope none

The organization, the environment and the person a key or OAuth access token acts for, and its scopes. Apps call it to check that a connection works and to label it, for example "Priya Raman, Wren Lane Lettings (sandbox)". Any valid key or access token may call it; it needs no scope.

Answer 200: The key or token and who it acts for. Me

Errors: 401, 403, 429, in the Error shape (what they mean).

Example answer
{
  "object": "me",
  "environment": "sandbox",
  "livemode": false,
  "credential": {
    "type": "oauth",
    "name": "Zapier",
    "scopes": [
      "envelopes:read",
      "envelopes:write",
      "templates:read",
      "webhooks:manage"
    ]
  },
  "organization": {
    "name": "Wren Lane Lettings",
    "region": "us"
  },
  "user": {
    "name": "Priya Raman",
    "email": "priya@example.com",
    "role": "owner"
  }
}

What is in Audit log?

The organization's security log and every envelope audit event, for security reviews and your own archive (P4-AUD). Owners and admins only (the key or token acts with its member's current role), Business plan, scope audit:read, and live keys or tokens only: sandbox credentials get 403 LIVE_KEY_REQUIRED.

List audit log events

GET/v1/audit-events

listAuditEvents · scope audit:read

Security events or envelope audit events in a date range, newest first. from and to take a date (2026-10-01, a whole UTC day; to includes that day) or an RFC 3339 date-time; a range can cover up to 366 days. Envelope pages also carry chains: the result of recomputing the hash chain of each envelope on the page. Scope audit:read; owners and admins; Business plan; live only.

ParameterInTypeNotes
source requiredquery"security" | "envelope"security for the security log, envelope for envelope audit events.
from requiredquerystringStart of the range (included).
to requiredquerystringEnd of the range. A date includes that whole day; a date-time is excluded.
limitqueryinteger
starting_afterquerystringId of the last event you already have (sev_... or eev_..., matching source).

Answer 200: One page of events. AuditLogEventList

Errors: 401, 402, 403, 422, 429, in the Error shape (what they mean).

Example answer
{
  "object": "list",
  "source": "envelope",
  "has_more": false,
  "data": [
    {
      "id": "eev_4812",
      "object": "audit_event",
      "source": "envelope",
      "type": "recipient.signed",
      "occurred_at": "2026-09-14T15:02:11.000Z",
      "envelope_id": "env_7d1e0c2b9a8f4e3d8c7b6a5f4e3d2c1b",
      "environment": "live",
      "seq": 6,
      "actor_type": "recipient",
      "actor_id": null,
      "recipient_id": "rcp_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
      "ip_address": "203.0.113.7",
      "user_agent": "Mozilla/5.0",
      "details": {},
      "prev_hash": "4f0c3d2b1a09887766554433221100ffeeddccbbaa99887766554433221100ff",
      "hash": "9b74c9897bac770ffc029102a200c5de7d4e6bdfa0e3f1d4b9a2f0c1e8d7c6b5"
    }
  ],
  "chains": [
    {
      "envelope_id": "env_7d1e0c2b9a8f4e3d8c7b6a5f4e3d2c1b",
      "environment": "live",
      "valid": true,
      "length": 6,
      "head_hash": "9b74c9897bac770ffc029102a200c5de7d4e6bdfa0e3f1d4b9a2f0c1e8d7c6b5",
      "broken_at_seq": null,
      "reason": null
    }
  ]
}

Prepare an audit log export

POST/v1/audit-exports

createAuditExport · scope audit:read · takes Idempotency-Key

Queues a ZIP of the security log and envelope audit events for a date range (up to 366 days), with manifest.json holding the SHA-256 of each file. Poll GET /audit-exports/{export_id}; when status is ready, download it from download_url. Finished exports are kept for 7 days. At most 5 exports can be waiting at once (429 RATE_LIMITED). Scope audit:read; owners and admins; Business plan; live only.

Request body (application/json): CreateAuditExportRequest

Answer 202: The export, queued. AuditExport

Errors: 400, 401, 402, 403, 422, 429, 503, in the Error shape (what they mean).

Example request body
{
  "from": "2026-01-01",
  "to": "2026-09-30",
  "format": "csv"
}
Example answer
{
  "id": "aex_5a4b3c2d1e0f49a8b7c6d5e4f3a2b1c0",
  "object": "audit_export",
  "status": "queued",
  "format": "csv",
  "from": "2026-01-01T00:00:00.000Z",
  "to": "2026-10-01T00:00:00.000Z",
  "created_at": "2026-10-02T09:00:00.000Z",
  "completed_at": null,
  "expires_at": null,
  "filename": "signwren-audit-alder-property-2026-01-01-to-2026-09-30.zip",
  "sha256": null,
  "bytes": null,
  "download_url": null,
  "download_url_expires_at": null,
  "manifest": null,
  "error": null
}

Get an audit log export

GET/v1/audit-exports/{export_id}

getAuditExport · scope audit:read

The export's status. When it is ready, download_url is a link to the ZIP that works without a key for 60 minutes (ask again for a fresh one), sha256 and bytes describe the ZIP, and manifest is the manifest inside it. After 7 days the status is expired and the files are gone. Scope audit:read; owners and admins; live only.

ParameterInTypeNotes
export_id requiredpathAuditExportId

Answer 200: The export. AuditExport

Errors: 401, 402, 403, 404, 429, in the Error shape (what they mean).

Example answer
{
  "id": "aex_5a4b3c2d1e0f49a8b7c6d5e4f3a2b1c0",
  "object": "audit_export",
  "status": "ready",
  "format": "csv",
  "from": "2026-01-01T00:00:00.000Z",
  "to": "2026-10-01T00:00:00.000Z",
  "created_at": "2026-10-02T09:00:00.000Z",
  "completed_at": "2026-10-02T09:01:12.000Z",
  "expires_at": "2026-10-09T09:01:12.000Z",
  "filename": "signwren-audit-alder-property-2026-01-01-to-2026-09-30.zip",
  "sha256": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
  "bytes": 482113,
  "download_url": "https://app.signwren.com/api/files/eyJ2IjoxLCJlIjoiNWE0YiJ9.c2lnbmF0dXJl",
  "download_url_expires_at": "2026-10-02T10:05:00.000Z",
  "manifest": {
    "object": "audit_export_manifest",
    "version": 1,
    "files": [
      {
        "name": "security_events.csv",
        "sha256": "3f79bb7b435b05321651daefd374cdc681dc06faa65e374e38337b88ca046dea",
        "bytes": 90211,
        "rows": 412
      }
    ]
  },
  "error": null
}

What does SignWren send to webhook endpoints?

SignWren posts these to your endpoints; the webhooks page explains how to verify them.

An envelope event delivered to your endpoint

SignWren POSTs this to each enabled endpoint of the envelope's environment that subscribed to the event type. Answer with any 2xx within 10 seconds. Anything else, a timeout or a redirect is retried after 30 s, 2 min, 10 min, 30 min, 1 h, then every 6 h, for up to 3 days.

HeaderExample
SignWren-Signaturet=1790812805,v1=9f180999dd3c5224b30dfc773b297c7ca5110b8949528fb6e24216ee199d5100
SignWren-Event-Id
SignWren-Event-Type

Body: WebhookEvent

Example body
{
  "id": "evt_0b9d6a512f0c4d559b2f7a0d6c1e2f30",
  "type": "recipient.signed",
  "created": 1790812805,
  "environment": "sandbox",
  "livemode": false,
  "data": {
    "envelope": {
      "id": "7d1e0c2b-9a8f-4e3d-8c7b-6a5f4e3d2c1b",
      "status": "sent",
      "metadata": {
        "crm_deal": "D-1042"
      }
    },
    "recipient": {
      "id": "0a1b2c3d-4e5f-4071-8293-a4b5c6d7e8f9",
      "status": "signed"
    }
  }
}

What do the objects look like?

Every object the API sends or accepts, as named in the spec. Fields marked required are always present in answers (possibly as null when the type allows it).

EnvelopeId

Type: string matching ^env_[0-9a-f]{32}$

DocumentId

Type: string matching ^doc_[0-9a-f]{32}$

RecipientId

Type: string matching ^rcp_[0-9a-f]{32}$

FieldId

Type: string matching ^fld_[0-9a-f]{32}$

TemplateId

Type: string matching ^tpl_[0-9a-f]{32}$

AuditExportId

Type: string matching ^aex_[0-9a-f]{32}$

Timestamp

Type: string

NullableTimestamp

Type: string | null

Sha256

Type: string matching ^[0-9a-f]{64}$

Error

FieldTypeNotes
error requiredobject

Deleted

FieldTypeNotes
id requiredstring
object required"recipient" | "field"
deleted requiredtrue

EnvelopeStatus

completing is shown as "Finishing up" (the last signature is in and the PDF is being sealed); delayed means finishing failed three times and SignWren is looking at it.

Type: "draft" | "sent" | "completing" | "delayed" | "completed" | "declined" | "voided" | "expired"

Environment

Type: "live" | "sandbox"

PageSize

FieldTypeNotes
width requirednumber
height requirednumber

Warning

FieldTypeNotes
category requiredstring
matched_terms requiredstring[]
acknowledgedboolean

UploadedDocument

FieldTypeNotes
id requiredDocumentId
object required"document"
filename requiredstring
kind required"pdf" | "docx" | "image"
status required"ready" | "attached"ready to be put in an envelope, attached once it is in one.
page_count requiredinteger | nullNull for Word files and images until they are converted.
pages requiredPageSize[] | null
sha256 requiredSha256
size_bytes requiredinteger
pre_signed requiredboolean
warnings requiredWarning[]
envelope_id requiredEnvelopeId | null
created_at requiredTimestamp
expires_at requiredTimestamp

EnvelopeDocument

FieldTypeNotes
id requiredDocumentId
object required"document"
position requiredinteger
filename requiredstring
source_type required"pdf" | "docx" | "image" | "template"
status required"processing" | "ready" | "failed"
page_count requiredinteger | null
pages requiredPageSize[] | null
sha256 requiredSha256 | null
size_bytes requiredinteger
pre_signed requiredboolean
failure_reason requiredstring | null

Recipient

FieldTypeNotes
id requiredRecipientId
object required"recipient"
name requiredstring
email requiredstring
role required"signer" | "cc"
routing_order requiredinteger
status required"pending" | "notified" | "viewed" | "consented" | "signed" | "declined" | "withdrew_consent"
display_status required"Waiting" | "Emailed" | "Bounced" | "Viewed" | "Signed" | "Declined" | "Asked for paper"The label the dashboard shows.
signing_method required"email" | "in_person"
email_code requiredboolean
sms_phone requiredstring | null
has_access_code requiredboolean
template_role requiredstring | null
embedded requiredbooleanSigns inside your app from a signing URL; SignWren sends them no signing email.
client_user_id requiredstring | nullYour own id for an embedded signer.
signature_meaning requiredstring | nullThe meaning of this signer's signature ("Reviewed"), when the organization uses signature meaning (Business plan). The signer sees it before signing and confirms who they are at the moment of signing; their printed name, the date and time and the meaning are drawn next to the signature and repeated on the certificate. A set of features for your own 21 CFR Part 11 program: SignWren doesn't certify or validate Part 11 compliance.
notified_at requiredNullableTimestamp
first_viewed_at requiredNullableTimestamp
signed_at requiredNullableTimestamp
declined_at requiredNullableTimestamp
decline_reason requiredstring | null

RecipientSummary

FieldTypeNotes
id requiredRecipientId
name requiredstring
email requiredstring
role required"signer" | "cc"
status requiredstring
display_status requiredstring

Field

FieldTypeNotes
id requiredFieldId
object required"field"
document_id requiredDocumentId
recipient_id requiredRecipientId
type requiredFieldType
page requiredinteger
x requirednumberPDF points from the left edge.
y requirednumberPDF points from the top edge (top-left origin).
width requirednumber
height requirednumber
required requiredboolean
label requiredstring | null
placeholder requiredstring | null
max_length requiredinteger | null
filled requiredboolean

FieldType

Type: "signature" | "initials" | "date_signed" | "name" | "text" | "checkbox"

ExternalRef

FieldTypeNotes
system requiredstring
object_type requiredstring
object_id requiredstring
urlstring | null

Metadata

Up to 50 string pairs (keys up to 40 characters, values up to 500). Returned on every read.

Type: object

Reminders

FieldTypeNotes
enabled requiredboolean
interval_days requiredinteger
max_count requiredinteger

Envelope

FieldTypeNotes
id requiredEnvelopeId
object required"envelope"
environment requiredEnvironment
title requiredstring
message requiredstring | null
status requiredEnvelopeStatus
status_label requiredstring
signing_order required"parallel" | "sequential"
expiry_days requiredinteger
expires_at requiredNullableTimestamp
reminders requiredReminders
metadata requiredMetadata
external_refs requiredExternalRef[]
warnings requiredWarning[]
template requiredobject | null
documents requiredEnvelopeDocument[]
recipients requiredRecipient[]
fields requiredField[]
created_at requiredTimestamp
sent_at requiredNullableTimestamp
completed_at requiredNullableTimestamp
voided_at requiredNullableTimestamp
void_reason requiredstring | null
declined_at requiredNullableTimestamp
expired_at requiredNullableTimestamp
final_sha256 requiredSha256 | nullSHA-256 of the sealed PDF as completed, the hash in envelope.completed. It doesn't change. After a yearly timestamp refresh the combined download is a newer copy with its own hash (see its ETag).
certificate_sha256 requiredSha256 | null
signature_level required"simple" | nullThe kind of signatures on a completed envelope, null before it completes. Always simple: SignWren signatures are electronic signatures under Article 3(10) of the EU eIDAS Regulation (Regulation (EU) No 910/2014), often called simple electronic signatures. They are not advanced or qualified electronic signatures, and SignWren's seal on the file is SignWren's, not a signature by any signer. General information, not legal advice.
mergeMergeResult

EnvelopeListItem

FieldTypeNotes
id requiredEnvelopeId
object required"envelope"
environment requiredEnvironment
title requiredstring
status requiredEnvelopeStatus
status_label requiredstring
metadata requiredMetadata
created_at requiredTimestamp
updated_at requiredTimestampWhen the envelope last changed (any event). Filter with updated_since.
sent_at requiredNullableTimestamp
completed_at requiredNullableTimestamp
expires_at requiredNullableTimestamp
recipients requiredRecipientSummary[]

EnvelopeList

FieldTypeNotes
object required"list"
has_more requiredboolean
data requiredEnvelopeListItem[]

AuditEvent

FieldTypeNotes
seq requiredinteger
type requiredstring
description requiredstring
occurred_at requiredTimestamp
actor_type required"user" | "recipient" | "system"
recipient_id requiredRecipientId | null
ip_address requiredstring | null
user_agent requiredstring | null
details requiredobject

EventList

FieldTypeNotes
object required"list"
has_more requiredfalse
data requiredAuditEvent[]
chain requiredobjectThe result of recomputing the hash chain over the events.

SecurityLogEvent

One row of the organization's security log (sign-in and admin events recorded with its id).

FieldTypeNotes
id requiredstring
object required"audit_event"
source required"security"
type requiredstringFor example role_changed, api_key.created, ip_allowlist.changed.
occurred_at requiredTimestamp
user_id requiredstring | null
email requiredstring | null
ip_address requiredstring | null
user_agent requiredstring | null
metadata requiredobject

EnvelopeLogEvent

One envelope audit event with its place in the envelope's hash chain.

FieldTypeNotes
id requiredstring
object required"audit_event"
source required"envelope"
type requiredstring
occurred_at requiredTimestamp
envelope_id requiredEnvelopeId
environment requiredEnvironment
seq requiredinteger
actor_type required"user" | "recipient" | "system"
actor_id requiredstring | null
recipient_id requiredRecipientId | null
ip_address requiredstring | null
user_agent requiredstring | null
details requiredobject
prev_hash requiredSha256
hash requiredSha256

ChainResult

FieldTypeNotes
envelope_id requiredEnvelopeId
environment requiredEnvironment
valid requiredboolean
length requiredintegerEvents in the envelope's whole chain.
head_hash requiredstring | null
broken_at_seq requiredinteger | null
reason required"sequence_gap" | "prev_hash_mismatch" | "hash_mismatch" | "wrong_envelope" | null

AuditLogEventList

FieldTypeNotes
object required"list"
source required"security" | "envelope"
has_more requiredboolean
data requiredSecurityLogEvent | EnvelopeLogEvent[]
chains requiredChainResult[]For envelope pages, each envelope on the page and the result of recomputing its chain. Empty for security pages.

CreateAuditExportRequest

FieldTypeNotes
from requiredstringA date (whole UTC day) or an RFC 3339 date-time. Included.
to requiredstringA date (that whole day is included) or an RFC 3339 date-time (excluded).
format"csv" | "jsonl"

AuditExport

FieldTypeNotes
id requiredAuditExportId
object required"audit_export"
status required"queued" | "running" | "ready" | "failed" | "expired"
format required"csv" | "jsonl"
from requiredTimestamp
to requiredTimestamp
created_at requiredTimestamp
completed_at requiredNullableTimestamp
expires_at requiredNullableTimestamp
filename requiredstring
sha256 requiredstring | nullSHA-256 of the whole ZIP.
bytes requiredinteger | null
download_url requiredstring | nullWorks without a key for 60 minutes. Ask again for a fresh one.
download_url_expires_at requiredNullableTimestamp
manifest requiredobject | nullThe manifest.json inside the ZIP, once it is ready.
error requiredstring | null

TemplateRole

FieldTypeNotes
id requiredstring
name requiredstring
kind required"signer" | "cc"
routing_order requiredinteger
optional requiredboolean

TemplateSenderField

FieldTypeNotes
id requiredstring
label requiredstring | null
fill required"sender" | "prefilled"sender: drawn into the document when the envelope is made. prefilled: shown to the signer, who can change it.
required requiredboolean
default_value requiredstring | null

Template

FieldTypeNotes
id requiredTemplateId
object required"template"
name requiredstring
visibility required"private" | "team" | "org"
current_version requiredinteger
signing_order required"parallel" | "sequential"
message requiredstring | null
expiry_days requiredinteger
roles requiredTemplateRole[]
sender_fields requiredTemplateSenderField[]
merge_fields requiredstring[]The template's merge field names, such as employee.first_name (written {{employee.first_name}} in the app), sorted. Send values for them in merge_fields when you create an envelope.
merge_field_mappings requiredMergeFieldMapping[]What each merge field fills, set up by the template's owner in the app.
documents requiredobject[]
created_at requiredTimestamp
updated_at requiredTimestamp

MergeFieldMapping

FieldTypeNotes
name requiredstring
target_kind required"placeholder" | "field"A bracketed placeholder in the template's text, or one of its sender or prefilled fields.
target requiredstringThe placeholder with its brackets, or the template field id.
required requiredbooleanWithout a value the envelope stays a draft (P3-INT-03.3).

MergeResult

Only on the answer to POST /templates/{template_id}/envelopes: which merge fields were filled, which names in merge_fields the template doesn't use (ignored), and which required ones had no value (the envelope is then a draft).

FieldTypeNotes
applied requiredstring[]
ignored requiredstring[]
missing requiredstring[]

TemplateList

FieldTypeNotes
object required"list"
has_more requiredboolean
data requiredTemplate[]

RecipientInput

FieldTypeNotes
name requiredstring
emailstringRequired unless signing_method is in_person.
role required"signer" | "cc"
routing_orderintegerUsed when the envelope's signing order is sequential.
signing_method"email" | "in_person"
access_codestring | nullA code you give the signer yourself. Stored hashed and never returned.
email_codebooleanAsk for a code sent to the signer's email (plan feature).
sms_phonestring | nullMobile number for a text message code (plan feature; allowed countries only).
embeddedbooleanSign inside your app from POST .../signing_url instead of an emailed link. Signers only, not with signing_method: in_person. Needs client_user_id.
client_user_idstring | nullYour own id for this person. Shown on the certificate for an embedded signer.
signature_meaningstring | nullOne of the organization's signature meanings (Settings, Signature meaning; Business plan). Only for a signer who is a member of the organization and signs from their own emailed link (not in person, not embedded); otherwise 422 VALIDATION with param: signature_meaning. null removes it.

RecipientPatch

FieldTypeNotes
namestring
emailstring
role"signer" | "cc"
routing_orderinteger
signing_method"email" | "in_person"
access_codestring | null
email_codeboolean
sms_phonestring | null
embeddedboolean
client_user_idstring | null
signature_meaningstring | null

EmbedUrlRequest

FieldTypeNotes
frame_originstringOne of the sites allowed in Settings. Needed when more than one is allowed.
return_urlstringWhere to send the person afterwards. Its origin must be an allowed site.

SigningUrl

FieldTypeNotes
object required"signing_url"
url requiredstringOpen within 5 minutes; works for one browser.
expires_at requiredTimestamp
envelope_id requiredEnvelopeId
recipient_id requiredRecipientId
frame_origin requiredstring
return_url requiredstring | null
environment requiredEnvironment

SenderViewUrl

FieldTypeNotes
object required"sender_view_url"
url requiredstringOpen within 5 minutes; works for one browser.
expires_at requiredTimestamp
envelope_id requiredEnvelopeId
frame_origin requiredstring
return_url requiredstring | null
environment requiredEnvironment

FieldInput

FieldTypeNotes
document_id requiredDocumentId
recipient_id requiredRecipientId
type requiredFieldType
page requiredinteger
x requirednumber
y requirednumber
width requirednumber
height requirednumber
requiredboolean
labelstring | null
placeholderstring | null
max_lengthinteger | null

FieldPatch

FieldTypeNotes
recipient_idRecipientId
typeFieldType
pageinteger
xnumber
ynumber
widthnumber
heightnumber
requiredboolean
labelstring | null
placeholderstring | null
max_lengthinteger | null

NewEnvelopeField

FieldTypeNotes
document_id requiredDocumentId
recipient_index requiredintegerPosition of the recipient in this request's recipients.
type requiredFieldType
page requiredinteger
x requirednumber
y requirednumber
width requirednumber
height requirednumber
requiredboolean
labelstring | null
placeholderstring | null
max_lengthinteger | null

CreateEnvelopeRequest

FieldTypeNotes
title requiredstring
messagestring | null
signing_order"parallel" | "sequential"
expiry_daysinteger
remindersReminders
document_idsDocumentId[]
recipientsRecipientInput[]
fieldsNewEnvelopeField[]
metadataMetadata
external_refsExternalRef[]

UpdateEnvelopeRequest

FieldTypeNotes
titlestring
messagestring | null
signing_order"parallel" | "sequential"
expiry_daysinteger
remindersReminders
metadataMetadata

SendRequest

FieldTypeNotes
acknowledge_warningsstring[]Warning categories you have read and accept (from warnings or the WARNING_UNACKNOWLEDGED error).
confirm_pre_signedboolean

RemindRequest

FieldTypeNotes
recipient_id requiredRecipientId

VoidRequest

FieldTypeNotes
reason requiredstring

CreateFromTemplateRequest

FieldTypeNotes
recipients requiredobject[]
prefillobjectValues for sender and prefilled fields, by template field id.
titlestring
messagestring
confirm_duplicate_emailsboolean
metadataMetadata
external_refsExternalRef[]
sendboolean
acknowledge_warningsstring[]
merge_fieldsobjectValues for the template's merge fields, by name (employee.first_name, or {{employee.first_name}}). Mapped placeholders are filled before the PDF is made; names the template doesn't use are ignored and listed in merge.ignored. Up to 100 names, each value up to 500 characters (as one line of plain text).
merge_from_recordbooleanAlso read values from the record in the first external_refs entry, through the account's connection to that system. Values in merge_fields win.

Me

FieldTypeNotes
object required"me"
environment requiredEnvironment
livemode requiredboolean
credential requiredobject
organization requiredobject
user requiredobjectThe member the key or token acts as (who made the key, or who allowed the app).

CreateFileLinkRequest

FieldTypeNotes
file"signed_pdf" | "certificate"
expires_in_minutesinteger

WebhookEndpointId

Type: string matching ^whk_[0-9a-f]{32}$

WebhookDeliveryId

Type: string matching ^whd_[0-9a-f]{32}$

WebhookEventType

Type: "envelope.created" | "envelope.sent" | "recipient.viewed" | "recipient.signed" | "recipient.declined" | "recipient.withdrew_consent" | "envelope.completing" | "envelope.completed" | "envelope.declined" | "envelope.voided" | "envelope.expired" | "envelope.deleted"

WebhookEndpoint

FieldTypeNotes
id requiredWebhookEndpointId
object required"webhook_endpoint"
url requiredstring
description requiredstring
events requiredstring[]Event types, or ["*"] for all of them.
environment requiredEnvironment
livemode requiredboolean
status required"enabled" | "disabled"
disabled_reason required"manual" | "failing" | nullfailing: every delivery failed for 3 days and SignWren turned it off.
disabled_at requiredNullableTimestamp
secret_hint requiredstringThe secret's prefix and last four characters.
rotation_ends_at requiredNullableTimestamp
app requiredobject | nullSet when an app connected with OAuth made this endpoint: the endpoint belongs to that connection and is deleted when the connection ends. Null for endpoints made with a key or in the app.
created_at requiredTimestamp
updated_at requiredTimestamp

WebhookEndpointWithSecret

FieldTypeNotes
id requiredWebhookEndpointId
object required"webhook_endpoint"
url requiredstring
description requiredstring
events requiredstring[]Event types, or ["*"] for all of them.
environment requiredEnvironment
livemode requiredboolean
status required"enabled" | "disabled"
disabled_reason required"manual" | "failing" | nullfailing: every delivery failed for 3 days and SignWren turned it off.
disabled_at requiredNullableTimestamp
secret_hint requiredstringThe secret's prefix and last four characters.
rotation_ends_at requiredNullableTimestamp
app requiredobject | nullSet when an app connected with OAuth made this endpoint: the endpoint belongs to that connection and is deleted when the connection ends. Null for endpoints made with a key or in the app.
created_at requiredTimestamp
updated_at requiredTimestamp
secret requiredstringThe signing secret. Shown in this answer only.

WebhookEndpointList

FieldTypeNotes
object required"list"
data requiredWebhookEndpoint[]
has_more requiredboolean

WebhookEndpointDeleted

FieldTypeNotes
id requiredWebhookEndpointId
object required"webhook_endpoint"
deleted requiredtrue

CreateWebhookEndpointRequest

FieldTypeNotes
url requiredstringhttps:// on port 443 or a port above 1023, resolving to public addresses only.
events requiredWebhookEventType | "*"[]
descriptionstring
environmentEnvironmentOptional; must match the key's environment if given.

UpdateWebhookEndpointRequest

FieldTypeNotes
urlstring
eventsWebhookEventType | "*"[]
descriptionstring
enabledboolean

RotateWebhookSecretRequest

FieldTypeNotes
expire_nowboolean

WebhookAttempt

FieldTypeNotes
attempt requiredinteger
at requiredTimestamp
duration_ms requiredinteger
status_code requiredinteger | null
error requiredstring | nullWhy it failed (timeout, connection, refused_address, redirect, http_status), or null.
response_excerpt requiredstring | nullThe first 1 KB of the response body.
manual requiredbooleanA test event or a resend.

WebhookDelivery

FieldTypeNotes
id requiredWebhookDeliveryId
object required"webhook_delivery"
webhook_endpoint_id requiredWebhookEndpointId
event_id requiredstringThe event's id
event_type requiredstring
environment requiredEnvironment
livemode requiredboolean
status required"pending" | "delivered" | "failed"failed means retries stopped (3 days, or a test or resend that failed once).
attempts requiredinteger
next_attempt_at requiredNullableTimestamp
last_status_code requiredinteger | null
last_error requiredstring | null
delivered_at requiredNullableTimestamp
is_test requiredboolean
resend_of requiredWebhookDeliveryId | null
created_at requiredTimestamp
attempt_log requiredWebhookAttempt[]

WebhookDeliveryList

FieldTypeNotes
object required"list"
data requiredWebhookDelivery[]
has_more requiredboolean

WebhookEvent

The JSON body of every delivery. Verify the SignWren-Signature header first: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of <t>.<raw body> with the endpoint's secret (during a rotation there are two v1 values; accept if either matches). Refuse a t more than 5 minutes from your clock. Drop events whose id you have already handled.

FieldTypeNotes
id requiredstring
type requiredWebhookEventType | "webhook.test"
created requiredintegerUnix seconds when it happened.
environment requiredEnvironment
livemode requiredbooleanFalse for sandbox envelopes.
data requiredobject

Last checked against the API spec on .