Signature requests API reference
Send a template to people for signing and track progress.
- get/signature_requestsList signature requests
- post/signature_requestsSend a signature request
- get/signature_requests/{id}Get a signature request
- post/signature_requests/{id}/cancelCancel a signature request
- post/signature_requests/{id}/remindRemind waiting signers
List signature requests
/api/v1/signature_requests- Read-only keys can call this
Lists signature requests, newest first. Use it to build a dashboard of outstanding documents or to reconcile state if you missed webhooks. List items include signers but not their field values or the signed documents; fetch a single signature request for those.
Query parameters
limitintegerNumber of items per page, 1–100. Values outside that range fall back to 20 (below 1) or 100 (above 100).
Default201 to 100cursorintegerThe
next_cursorfrom the previous page. Omit it for the first page.template_idintegerOnly signature requests created from this template.
qstringCase-insensitive search on signer name, email or phone.
statusstringFilter by status.
openincludes requests that aresentorin_progress;expiredreturns unfinished requests past theirexpires_at. Any other value returns422 invalid_parameter.One ofopen,completed,declined,expired,canceled
Request sample
curl https://app.signwith.co/api/v1/signature_requests \
-H "Authorization: Bearer $SIGNWITH_API_KEY"const response = await fetch('https://app.signwith.co/api/v1/signature_requests', {
headers: {
Authorization: `Bearer ${process.env.SIGNWITH_API_KEY}`,
},
})
console.log(response.status, await response.json())import os
import requests
response = requests.get(
"https://app.signwith.co/api/v1/signature_requests",
headers={
"Authorization": f"Bearer {os.environ['SIGNWITH_API_KEY']}",
},
)
print(response.status_code, response.json())Response 200
A page of signature requests. Each item in data is a signature request object (list version).
{
"object": "list",
"data": [
{
"object": "signature_request",
"id": 4812,
"status": "in_progress",
"template": {
"id": 311,
"name": "Residential lease"
},
"signing_order": "sequential",
"source": "api",
"created_by": {
"id": 42,
"email": "[email protected]",
"name": "Ada Lovelace"
},
"signers": [
{
"object": "signer",
"id": 9120,
"signature_request_id": 4812,
"role": "Tenant",
"name": "Grace Hopper",
"email": "[email protected]",
"phone": null,
"external_id": "cust_8841",
"metadata": {},
"status": "signed",
"signing_url": null,
"sent_at": "2026-09-27T09:00:02Z",
"viewed_at": "2026-09-27T09:41:18Z",
"signed_at": "2026-09-27T09:44:51Z",
"declined_at": null,
"created_at": "2026-09-27T09:00:01Z",
"updated_at": "2026-09-27T09:44:51Z"
},
{
"object": "signer",
"id": 9121,
"signature_request_id": 4812,
"role": "Landlord",
"name": "Ada Lovelace",
"email": "[email protected]",
"phone": null,
"external_id": null,
"metadata": {},
"status": "sent",
"signing_url": "https://app.signwith.co/s/q8LkT2mZpV4r",
"sent_at": "2026-09-27T09:44:53Z",
"viewed_at": null,
"signed_at": null,
"declined_at": null,
"created_at": "2026-09-27T09:00:01Z",
"updated_at": "2026-09-27T09:44:53Z"
}
],
"expires_at": "2026-10-27T00:00:00Z",
"completed_at": null,
"canceled_at": null,
"created_at": "2026-09-27T09:00:01Z",
"updated_at": "2026-09-27T09:44:53Z"
}
],
"has_more": false,
"next_cursor": null
}Errors
| 401 | The API key is missing, invalid or expired, or its account has been archived. (unauthenticated, api_key_expired) |
| 403 | The key is read-only, or its user can't access this resource. (read_only_api_key, forbidden) |
| 422 | The request is valid JSON but can't be processed. See error.code; the codes each endpoint
can return are described in the endpoint's description and in the error table above. (missing_parameter, invalid_parameter, invalid_request, missing_documents, too_many_documents, document_too_large, invalid_document, invalid_file_type, pdf_encrypted, missing_signers, invalid_signer, already_completed, not_open, nothing_to_remind, invalid_field, template_has_no_fields, template_archived, signer_finished, idempotency_key_reused, invalid_discount_code, already_purchased, billing_details_required) |
| 429 | Rate limit exceeded (rate_limited, with Retry-After); more than 20 POST /feedback
messages in an hour (rate_limited, with Retry-After: seconds until the next clock hour); or — for
POST /signature_requests/{id}/remind — signers were reminded less than an hour ago
(remind_too_soon, with Retry-After). (rate_limited, remind_too_soon) |
| 500 | Something went wrong on SignWith's side. It's safe to retry with the same Idempotency-Key. (internal_error) |
Codes listed are the examples the spec gives; see all error codes.
Send a signature request
/api/v1/signature_requests- Needs a full-access key
- Accepts Idempotency-Key
Creates a signature request from a template and invites the signers. This is the main call for getting a document signed from your product.
- Assign each signer to a template role with
role. Signers without arolefill the template's roles in order. You can't send more signers than the template has roles, and each role can be given to only one signer. signersis a list of objects. Each signer needs a validemailor aphone(422 invalid_signer);prefillandmetadatamust be objects (422 invalid_parameter).expires_atmust be an ISO 8601 date-time in the future (422 invalid_parameter).- Use
prefillto fill fields for a signer ahead of time, keyed by field name (seeGET /templates/{id}for field names). - Set
send_email: falseto create the request without emailing anyone — for example to embed or deliver the signers'signing_urlyourself. Those signers have statusreadywhen it's their turn. - Set
require_email_otp: trueto require each signer to enter a one-time code sent to their email before they can view and sign.
The template must be active (422 template_archived) and have at least one field
(422 template_has_no_fields — add fields with PATCH /templates/{id}
or in the editor). The request keeps a snapshot of the template's fields and documents, so later
template edits don't change what these signers see.
The first signature request from a template uses one credit when it's signed; if you have no
credits left the request is rejected with 402 insufficient_credits — see
Buying credits for how to top up and retry. Send an
Idempotency-Key so retries don't send the document twice. Triggers the
signature_request.created webhook.
Headers
Idempotency-KeystringA unique value (e.g. a UUID) that makes retries of this request safe. A successful response is stored for 24 hours and replayed for retries with the same key.
at most 255 characters
Request body
template_idintegerRequiredThe template to send.
signersarray of objectsRequiredThe people to invite. At most one per template role.
at least 1 item9 child fields
rolestringThe template role this signer fills. If omitted, signers fill the template's roles in the order given.
namestringemailstringRequired unless
phoneis given.email addressphonestringPhone number in international format.
external_idstringYour own ID for this signer, returned in responses and webhooks.
metadataobjectArbitrary key/value data stored with the signer.
prefillobjectValues to fill in for a signer before they sign, keyed by field name (see the template's
fields). Values must suit the field type: text, dates asYYYY-MM-DD, checkboxes as booleans, and for image or signature fields a base64 image or an https URL.redirect_urlstringWhere to send this signer after they sign. Overrides the request-level
redirect_url.URLrequire_email_otpbooleanRequire each signer to enter a one-time code sent to their email before they can view and sign. Overrides the request-level
require_email_otpfor this signer.
signing_orderstringsequentialinvites signers one at a time in role order;parallelinvites everyone at once.One ofsequential,parallelDefaultsequentialsend_emailbooleanSet to
falseto create the request without emailing signers.DefaulttruemessageobjectCustom subject and body for the invitation email.
2 child fields
subjectstringbodystring
reply_tostringReply-to address for emails sent to signers.
email addressredirect_urlstringWhere to send signers after they sign.
URLexpires_atstring (date-time)ISO 8601 date-time in the future (e.g.
2026-12-31T17:00:00Z). After it the request expires and can no longer be signed.require_email_otpbooleanRequire each signer to enter a one-time code sent to their email before they can view and sign.
Defaultfalse
Request sample
curl -X POST https://app.signwith.co/api/v1/signature_requests \
-H "Authorization: Bearer $SIGNWITH_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"template_id": 311,
"signing_order": "sequential",
"expires_at": "2026-10-27T00:00:00Z",
"reply_to": "[email protected]",
"redirect_url": "https://acme.co/lease/signed",
"message": {
"subject": "Your lease for 14 Riverside Ave is ready to sign",
"body": "Hi Grace, please review and sign your lease."
},
"signers": [
{
"role": "Tenant",
"name": "Grace Hopper",
"email": "[email protected]",
"external_id": "cust_8841",
"metadata": {
"crm_deal_id": "D-2291"
},
"prefill": {
"Tenant name": "Grace Hopper",
"Monthly rent": "2150",
"Start date": "2026-11-01"
},
"require_email_otp": true
},
{
"role": "Landlord",
"name": "Ada Lovelace",
"email": "[email protected]"
}
]
}'const response = await fetch('https://app.signwith.co/api/v1/signature_requests', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SIGNWITH_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
template_id: 311,
signing_order: 'sequential',
expires_at: '2026-10-27T00:00:00Z',
reply_to: '[email protected]',
redirect_url: 'https://acme.co/lease/signed',
message: {
subject: 'Your lease for 14 Riverside Ave is ready to sign',
body: 'Hi Grace, please review and sign your lease.',
},
signers: [
{
role: 'Tenant',
name: 'Grace Hopper',
email: '[email protected]',
external_id: 'cust_8841',
metadata: {
crm_deal_id: 'D-2291',
},
prefill: {
'Tenant name': 'Grace Hopper',
'Monthly rent': '2150',
'Start date': '2026-11-01',
},
require_email_otp: true,
},
{
role: 'Landlord',
name: 'Ada Lovelace',
email: '[email protected]',
},
],
}),
})
console.log(response.status, await response.json())import os
import uuid
import requests
response = requests.post(
"https://app.signwith.co/api/v1/signature_requests",
headers={
"Authorization": f"Bearer {os.environ['SIGNWITH_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"template_id": 311,
"signing_order": "sequential",
"expires_at": "2026-10-27T00:00:00Z",
"reply_to": "[email protected]",
"redirect_url": "https://acme.co/lease/signed",
"message": {
"subject": "Your lease for 14 Riverside Ave is ready to sign",
"body": "Hi Grace, please review and sign your lease.",
},
"signers": [
{
"role": "Tenant",
"name": "Grace Hopper",
"email": "[email protected]",
"external_id": "cust_8841",
"metadata": {
"crm_deal_id": "D-2291",
},
"prefill": {
"Tenant name": "Grace Hopper",
"Monthly rent": "2150",
"Start date": "2026-11-01",
},
"require_email_otp": True,
},
{
"role": "Landlord",
"name": "Ada Lovelace",
"email": "[email protected]",
},
],
},
)
print(response.status_code, response.json())Example: No emails — deliver the signing links yourself
curl -X POST https://app.signwith.co/api/v1/signature_requests \
-H "Authorization: Bearer $SIGNWITH_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"template_id": 311,
"signing_order": "parallel",
"send_email": false,
"signers": [
{
"role": "Tenant",
"name": "Grace Hopper",
"email": "[email protected]"
},
{
"role": "Landlord",
"name": "Ada Lovelace",
"email": "[email protected]"
}
]
}'const response = await fetch('https://app.signwith.co/api/v1/signature_requests', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SIGNWITH_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
template_id: 311,
signing_order: 'parallel',
send_email: false,
signers: [
{
role: 'Tenant',
name: 'Grace Hopper',
email: '[email protected]',
},
{
role: 'Landlord',
name: 'Ada Lovelace',
email: '[email protected]',
},
],
}),
})
console.log(response.status, await response.json())import os
import uuid
import requests
response = requests.post(
"https://app.signwith.co/api/v1/signature_requests",
headers={
"Authorization": f"Bearer {os.environ['SIGNWITH_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"template_id": 311,
"signing_order": "parallel",
"send_email": False,
"signers": [
{
"role": "Tenant",
"name": "Grace Hopper",
"email": "[email protected]",
},
{
"role": "Landlord",
"name": "Ada Lovelace",
"email": "[email protected]",
},
],
},
)
print(response.status_code, response.json())Response 201
The signature request was created and signers were invited. Returns a signature request object.
Example response: see the example object below.
Errors
| 400 | The request body is not valid JSON. (invalid_json) |
| 401 | The API key is missing, invalid or expired, or its account has been archived. (unauthenticated, api_key_expired) |
| 402 | Not enough credits to send this document. Buy credits with GET /credit_packs and
POST /checkouts, then retry. See Buying credits. (insufficient_credits) |
| 403 | The key is read-only, or its user can't access this resource. (read_only_api_key, forbidden) |
| 404 | The resource doesn't exist or belongs to another account. (not_found) |
| 409 | A request with the same Idempotency-Key is still being processed. Retry shortly. (idempotency_key_in_use) |
| 422 | The request is valid JSON but can't be processed. See error.code; the codes each endpoint
can return are described in the endpoint's description and in the error table above. (missing_parameter, invalid_parameter, invalid_request, missing_documents, too_many_documents, document_too_large, invalid_document, invalid_file_type, pdf_encrypted, missing_signers, invalid_signer, already_completed, not_open, nothing_to_remind, invalid_field, template_has_no_fields, template_archived, signer_finished, idempotency_key_reused, invalid_discount_code, already_purchased, billing_details_required) |
| 429 | Rate limit exceeded (rate_limited, with Retry-After); more than 20 POST /feedback
messages in an hour (rate_limited, with Retry-After: seconds until the next clock hour); or — for
POST /signature_requests/{id}/remind — signers were reminded less than an hour ago
(remind_too_soon, with Retry-After). (rate_limited, remind_too_soon) |
| 500 | Something went wrong on SignWith's side. It's safe to retry with the same Idempotency-Key. (internal_error) |
Codes listed are the examples the spec gives; see all error codes.
Get a signature request
/api/v1/signature_requests/{id}- Read-only keys can call this
Returns a signature request with every signer's progress and field values. Once everyone has
signed (status: completed) it also includes download links for the signed documents, the
audit_trail_url and, when available, a combined_document_url. Use it after a
signature_request.completed webhook to download the final files.
Path parameters
idintegerRequiredSignature request ID.
Request sample
curl https://app.signwith.co/api/v1/signature_requests/4812 \
-H "Authorization: Bearer $SIGNWITH_API_KEY"const response = await fetch('https://app.signwith.co/api/v1/signature_requests/4812', {
headers: {
Authorization: `Bearer ${process.env.SIGNWITH_API_KEY}`,
},
})
console.log(response.status, await response.json())import os
import requests
response = requests.get(
"https://app.signwith.co/api/v1/signature_requests/4812",
headers={
"Authorization": f"Bearer {os.environ['SIGNWITH_API_KEY']}",
},
)
print(response.status_code, response.json())Response 200
The signature request. Returns a signature request object.
{
"object": "signature_request",
"id": 4812,
"status": "completed",
"template": {
"id": 311,
"name": "Residential lease"
},
"signing_order": "sequential",
"source": "api",
"created_by": {
"id": 42,
"email": "[email protected]",
"name": "Ada Lovelace"
},
"signers": [
{
"object": "signer",
"id": 9120,
"signature_request_id": 4812,
"role": "Tenant",
"name": "Grace Hopper",
"email": "[email protected]",
"phone": null,
"external_id": "cust_8841",
"metadata": {
"crm_deal_id": "D-2291"
},
"status": "signed",
"signing_url": null,
"sent_at": "2026-09-27T09:00:02Z",
"viewed_at": "2026-09-27T09:41:18Z",
"signed_at": "2026-09-27T09:44:51Z",
"declined_at": null,
"created_at": "2026-09-27T09:00:01Z",
"updated_at": "2026-09-27T09:44:51Z",
"values": [
{
"field": "Tenant name",
"value": "Grace Hopper"
},
{
"field": "Monthly rent",
"value": "2150"
},
{
"field": "Start date",
"value": "2026-11-01"
},
{
"field": "Pets",
"value": "Cat"
},
{
"field": "Tenant signature",
"value": "https://app.signwith.co/file/eyJfcmFpbHMiOnsiZGF0YSI6NTUxfX0/signature.png"
}
],
"documents": [
{
"name": "Lease agreement",
"url": "https://app.signwith.co/file/eyJfcmFpbHMiOnsiZGF0YSI6NTUyfX0/lease-agreement.pdf"
}
]
},
{
"object": "signer",
"id": 9121,
"signature_request_id": 4812,
"role": "Landlord",
"name": "Ada Lovelace",
"email": "[email protected]",
"phone": null,
"external_id": null,
"metadata": {},
"status": "signed",
"signing_url": null,
"sent_at": "2026-09-27T09:44:53Z",
"viewed_at": "2026-09-27T10:12:30Z",
"signed_at": "2026-09-27T10:14:58Z",
"declined_at": null,
"created_at": "2026-09-27T09:00:01Z",
"updated_at": "2026-09-27T10:14:58Z",
"values": [
{
"field": "Landlord signature",
"value": "https://app.signwith.co/file/eyJfcmFpbHMiOnsiZGF0YSI6NTYwfX0/signature.png"
}
],
"documents": [
{
"name": "Lease agreement",
"url": "https://app.signwith.co/file/eyJfcmFpbHMiOnsiZGF0YSI6NTYxfX0/lease-agreement.pdf"
}
]
}
],
"expires_at": "2026-10-27T00:00:00Z",
"completed_at": "2026-09-27T10:14:58Z",
"canceled_at": null,
"created_at": "2026-09-27T09:00:01Z",
"updated_at": "2026-09-27T10:14:58Z",
"documents": [
{
"name": "Lease agreement",
"url": "https://app.signwith.co/file/eyJfcmFpbHMiOnsiZGF0YSI6NTYxfX0/lease-agreement.pdf"
}
],
"audit_trail_url": "https://app.signwith.co/file/eyJfcmFpbHMiOnsiZGF0YSI6NTYyfX0/audit-trail.pdf",
"combined_document_url": null
}Errors
| 401 | The API key is missing, invalid or expired, or its account has been archived. (unauthenticated, api_key_expired) |
| 403 | The key is read-only, or its user can't access this resource. (read_only_api_key, forbidden) |
| 404 | The resource doesn't exist or belongs to another account. (not_found) |
| 429 | Rate limit exceeded (rate_limited, with Retry-After); more than 20 POST /feedback
messages in an hour (rate_limited, with Retry-After: seconds until the next clock hour); or — for
POST /signature_requests/{id}/remind — signers were reminded less than an hour ago
(remind_too_soon, with Retry-After). (rate_limited, remind_too_soon) |
| 500 | Something went wrong on SignWith's side. It's safe to retry with the same Idempotency-Key. (internal_error) |
Codes listed are the examples the spec gives; see all error codes.
Cancel a signature request
/api/v1/signature_requests/{id}/cancel- Needs a full-access key
- Accepts Idempotency-Key
Cancels a signature request so its signing links stop working — for example when a deal falls
through or the document was sent with a mistake. Fully signed requests can't be canceled
(422 already_completed). Canceling an already canceled request succeeds and returns it
unchanged. Triggers the signature_request.canceled webhook.
Path parameters
idintegerRequiredSignature request ID.
Headers
Idempotency-KeystringA unique value (e.g. a UUID) that makes retries of this request safe. A successful response is stored for 24 hours and replayed for retries with the same key.
at most 255 characters
Request sample
curl -X POST https://app.signwith.co/api/v1/signature_requests/4812/cancel \
-H "Authorization: Bearer $SIGNWITH_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"const response = await fetch('https://app.signwith.co/api/v1/signature_requests/4812/cancel', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SIGNWITH_API_KEY}`,
'Idempotency-Key': crypto.randomUUID(),
},
})
console.log(response.status, await response.json())import os
import uuid
import requests
response = requests.post(
"https://app.signwith.co/api/v1/signature_requests/4812/cancel",
headers={
"Authorization": f"Bearer {os.environ['SIGNWITH_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
)
print(response.status_code, response.json())Response 200
The canceled signature request. Returns a signature request object.
{
"object": "signature_request",
"id": 4812,
"status": "canceled",
"template": {
"id": 311,
"name": "Residential lease"
},
"signing_order": "sequential",
"source": "api",
"created_by": {
"id": 42,
"email": "[email protected]",
"name": "Ada Lovelace"
},
"signers": [
{
"object": "signer",
"id": 9120,
"signature_request_id": 4812,
"role": "Tenant",
"name": "Grace Hopper",
"email": "[email protected]",
"phone": null,
"external_id": "cust_8841",
"metadata": {},
"status": "viewed",
"signing_url": "https://app.signwith.co/s/Hn3cW8yRk2Qe",
"sent_at": "2026-09-27T09:00:02Z",
"viewed_at": "2026-09-27T09:41:18Z",
"signed_at": null,
"declined_at": null,
"created_at": "2026-09-27T09:00:01Z",
"updated_at": "2026-09-27T09:41:18Z",
"values": []
}
],
"expires_at": null,
"completed_at": null,
"canceled_at": "2026-09-28T08:30:12Z",
"created_at": "2026-09-27T09:00:01Z",
"updated_at": "2026-09-28T08:30:12Z"
}Errors
| 401 | The API key is missing, invalid or expired, or its account has been archived. (unauthenticated, api_key_expired) |
| 403 | The key is read-only, or its user can't access this resource. (read_only_api_key, forbidden) |
| 404 | The resource doesn't exist or belongs to another account. (not_found) |
| 409 | A request with the same Idempotency-Key is still being processed. Retry shortly. (idempotency_key_in_use) |
| 422 | The request is valid JSON but can't be processed. See error.code; the codes each endpoint
can return are described in the endpoint's description and in the error table above. (missing_parameter, invalid_parameter, invalid_request, missing_documents, too_many_documents, document_too_large, invalid_document, invalid_file_type, pdf_encrypted, missing_signers, invalid_signer, already_completed, not_open, nothing_to_remind, invalid_field, template_has_no_fields, template_archived, signer_finished, idempotency_key_reused, invalid_discount_code, already_purchased, billing_details_required) |
| 429 | Rate limit exceeded (rate_limited, with Retry-After); more than 20 POST /feedback
messages in an hour (rate_limited, with Retry-After: seconds until the next clock hour); or — for
POST /signature_requests/{id}/remind — signers were reminded less than an hour ago
(remind_too_soon, with Retry-After). (rate_limited, remind_too_soon) |
| 500 | Something went wrong on SignWith's side. It's safe to retry with the same Idempotency-Key. (internal_error) |
Codes listed are the examples the spec gives; see all error codes.
Remind waiting signers
/api/v1/signature_requests/{id}/remind- Needs a full-access key
- Accepts Idempotency-Key
Emails the signing link again to every signer who has been invited, hasn't signed or declined yet, and has an email address. Use it to nudge people who haven't acted. Signers still waiting for their turn in a sequential request are not reminded.
A request can be reminded at most once per hour (429 remind_too_soon, with Retry-After
set to the seconds left until it can be reminded again). Returns
422 not_open for canceled, declined or expired requests and 422 nothing_to_remind when nobody is
waiting.
Path parameters
idintegerRequiredSignature request ID.
Headers
Idempotency-KeystringA unique value (e.g. a UUID) that makes retries of this request safe. A successful response is stored for 24 hours and replayed for retries with the same key.
at most 255 characters
Request sample
curl -X POST https://app.signwith.co/api/v1/signature_requests/4812/remind \
-H "Authorization: Bearer $SIGNWITH_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"const response = await fetch('https://app.signwith.co/api/v1/signature_requests/4812/remind', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SIGNWITH_API_KEY}`,
'Idempotency-Key': crypto.randomUUID(),
},
})
console.log(response.status, await response.json())import os
import uuid
import requests
response = requests.post(
"https://app.signwith.co/api/v1/signature_requests/4812/remind",
headers={
"Authorization": f"Bearer {os.environ['SIGNWITH_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
)
print(response.status_code, response.json())Response 200
Which signers were reminded. Returns a reminder object.
{
"object": "reminder",
"signature_request_id": 4812,
"reminded_signer_ids": [
9121
]
}Errors
| 401 | The API key is missing, invalid or expired, or its account has been archived. (unauthenticated, api_key_expired) |
| 403 | The key is read-only, or its user can't access this resource. (read_only_api_key, forbidden) |
| 404 | The resource doesn't exist or belongs to another account. (not_found) |
| 409 | A request with the same Idempotency-Key is still being processed. Retry shortly. (idempotency_key_in_use) |
| 422 | The request is valid JSON but can't be processed. See error.code; the codes each endpoint
can return are described in the endpoint's description and in the error table above. (missing_parameter, invalid_parameter, invalid_request, missing_documents, too_many_documents, document_too_large, invalid_document, invalid_file_type, pdf_encrypted, missing_signers, invalid_signer, already_completed, not_open, nothing_to_remind, invalid_field, template_has_no_fields, template_archived, signer_finished, idempotency_key_reused, invalid_discount_code, already_purchased, billing_details_required) |
| 429 | Rate limit exceeded (rate_limited, with Retry-After); more than 20 POST /feedback
messages in an hour (rate_limited, with Retry-After: seconds until the next clock hour); or — for
POST /signature_requests/{id}/remind — signers were reminded less than an hour ago
(remind_too_soon, with Retry-After). (rate_limited, remind_too_soon) |
| 500 | Something went wrong on SignWith's side. It's safe to retry with the same Idempotency-Key. (internal_error) |
Codes listed are the examples the spec gives; see all error codes.
The signature request object
A signature request with full signer details. documents, audit_trail_url and
combined_document_url are present only when every signer has signed.
objectstringRequiredAlwayssignature_requestidintegerRequiredstatusstringRequiredsent— nobody has signed yet;in_progress— some signers have signed;completed— everyone signed;declined— a signer declined;expired— passedexpires_atbefore completion;canceled— canceled. A fully signed request is alwayscompleted, even if it was later archived.One ofsent,in_progress,completed,declined,expired,canceledtemplateobject or nullRequiredThe template this request was created from.
2 child fields
idintegerRequirednamestringRequired
signing_orderstringRequiredOne ofsequential,parallelsourcestringRequiredWhere the request was created:
api(this API),mcp(an AI assistant through the SignWith MCP server),invite(dashboard),link(shared link),bulkorembed.created_byobject or nullRequiredA SignWith user (a member of your team).
3 child fields
idintegerRequiredemailstringRequiredemail addressnamestring or nullRequiredFirst and last name, or
nullif not set.
signersarray of objectsRequiredSigners in role order, with their field values.
19 child fields
objectstringRequiredAlwayssigneridintegerRequiredsignature_request_idintegerRequiredrolestring or nullRequiredThe template role this signer fills.
namestring or nullRequiredemailstring or nullRequiredemail addressphonestring or nullRequiredexternal_idstring or nullRequiredYour own ID for this signer.
metadataobjectRequiredArbitrary key/value data you attached to the signer.
statusstringRequiredwaiting— an earlier signer in a sequential request has to sign first;ready— it's their turn but they weren't emailed (e.g.send_email: false, or the email is still queued), so sharesigning_urlyourself;sent— invited;viewed— opened the document;signed— finished signing;declined— declined to sign.One ofwaiting,ready,sent,viewed,signed,declinedsigning_urlstring or nullRequiredThe signer's private signing link. Anyone with it can sign as this signer, so only share it with them.
nullonce they've signed, or when the request is canceled, declined or expired, or its template is archived.URLsent_atstring (date-time) or nullRequiredviewed_atstring (date-time) or nullRequiredsigned_atstring (date-time) or nullRequireddeclined_atstring (date-time) or nullRequiredcreated_atstring (date-time)Requiredupdated_atstring (date-time)Requiredvaluesarray of objectsRequiredField values filled so far (prefilled or entered by the signer).
2 child fields
fieldstringRequiredField name (or a generated name such as
Text Field 2for unnamed fields).valuestring or number or boolean or array of anys or nullRequiredThe value entered or prefilled.
documentsarray of objectsThe signer's signed documents. Present only once they've signed.
2 child fields
namestringRequiredFile name without extension.
urlstringRequiredDownload link for the signed PDF.
URL
expires_atstring (date-time) or nullRequiredcompleted_atstring (date-time) or nullRequiredWhen the last signer signed.
canceled_atstring (date-time) or nullRequiredcreated_atstring (date-time)Requiredupdated_atstring (date-time)Requireddocumentsarray of objectsThe final signed documents.
2 child fields
namestringRequiredFile name without extension.
urlstringRequiredDownload link for the signed PDF.
URL
audit_trail_urlstring or nullDownload link for the audit trail PDF.
URLcombined_document_urlstring or nullDownload link for all documents and the audit trail merged into one PDF, if generated.
URL
{
"object": "signature_request",
"id": 4812,
"status": "sent",
"template": {
"id": 311,
"name": "Residential lease"
},
"signing_order": "sequential",
"source": "api",
"created_by": {
"id": 42,
"email": "[email protected]",
"name": "Ada Lovelace"
},
"signers": [
{
"object": "signer",
"id": 9120,
"signature_request_id": 4812,
"role": "Tenant",
"name": "Grace Hopper",
"email": "[email protected]",
"phone": null,
"external_id": "cust_8841",
"metadata": {
"crm_deal_id": "D-2291"
},
"status": "sent",
"signing_url": "https://app.signwith.co/s/Hn3cW8yRk2Qe",
"sent_at": "2026-09-27T09:00:02Z",
"viewed_at": null,
"signed_at": null,
"declined_at": null,
"created_at": "2026-09-27T09:00:01Z",
"updated_at": "2026-09-27T09:00:02Z",
"values": [
{
"field": "Tenant name",
"value": "Grace Hopper"
},
{
"field": "Monthly rent",
"value": "2150"
},
{
"field": "Start date",
"value": "2026-11-01"
}
]
},
{
"object": "signer",
"id": 9121,
"signature_request_id": 4812,
"role": "Landlord",
"name": "Ada Lovelace",
"email": "[email protected]",
"phone": null,
"external_id": null,
"metadata": {},
"status": "waiting",
"signing_url": "https://app.signwith.co/s/q8LkT2mZpV4r",
"sent_at": null,
"viewed_at": null,
"signed_at": null,
"declined_at": null,
"created_at": "2026-09-27T09:00:01Z",
"updated_at": "2026-09-27T09:00:01Z",
"values": []
}
],
"expires_at": "2026-10-27T00:00:00Z",
"completed_at": null,
"canceled_at": null,
"created_at": "2026-09-27T09:00:01Z",
"updated_at": "2026-09-27T09:00:01Z"
}Base URL, pagination and idempotency work the same on every endpoint: see making requests. Every error code is on errors.
Start building
The API and MCP server come with every account. 3 free documents a month, then pay per document.