Getting started
API access is included in the Team and Business plans. A workspace admin creates a key in Settings → API keys and chooses what it may do. The key is shown once; Closign keeps only a hash of it.
Check that the key works:
curl https://closign.io/api/v1/me \
-H "Authorization: Bearer $CLOSIGN_API_KEY"
GET /me answers with the workspace, the key's permissions and your rate limit. Then create an envelope from a PDF or a template with POST /envelopes, send it, follow its status, and download the signed PDF once it's completed.
Authentication
Send the key on every request as a bearer token in the Authorization header. Keys start with cs_live_.
A key belongs to the workspace, not to a person. Envelopes it creates are owned by the admin who created the key, or by the workspace owner if that admin has left. The audit trail records that they were made through the API, with the key's name.
Each key has one or more permissions:
| Permission | Allows |
|---|---|
envelopes:read |
Listing and reading envelopes, downloading signed PDFs and audit certificates |
envelopes:write |
Creating, sending, voiding and reminding envelopes |
templates:read |
Listing and reading templates |
Revoke a key in Settings → API keys and it stops working at once. Keep keys on your server: never put one in a web page or a mobile app.
Requests and responses
- The base URL is
https://closign.io/api/v1, over HTTPS only. - Send JSON with
Content-Type: application/json. To upload a PDF as a file, usemultipart/form-data; thenrecipients,fieldsandvariablesare JSON strings. - Answers are JSON, except the PDF downloads. One object is under
data, a list isdataplus pagination. Every object says what it is inobject:envelope,recipientortemplate. - Times are UTC in ISO 8601, such as
2026-10-08T09:30:12Z. - Envelope IDs start with
env_and recipient IDs withrcp_. Templates are known by their slug, such asquotation-acceptance.
Errors
Every error has the same shape:
{
"error": {
"code": "validation_error",
"message": "The recipients field is required.",
"details": { "recipients": ["The recipients field is required."] }
}
}
code is stable: check it in your code. message is written for people and may change. details is there when there's more to say; for validation_error it lists each field with its problems.
| Status | Meaning |
|---|---|
| 401 | No key, or a key that isn't valid |
| 402 | The plan's document limit is reached |
| 403 | The workspace can't use the API, or the key doesn't have the permission |
| 404 | Not found, or not in this workspace |
| 409 | The envelope's state doesn't allow it (yet) |
| 422 | Something in the request isn't valid |
| 429 | Too many requests |
| 500 | Something went wrong on our side: safe to retry |
Every code is listed under Error codes, below.
Rate limits
Requests are counted per workspace, all its keys together:
| Plan | Requests a minute |
|---|---|
| Team | 60 |
| Business | 300 |
Within that, a workspace can create 30 envelopes a minute. Every answer has the headers X-RateLimit-Limit and X-RateLimit-Remaining; a 429 also has Retry-After, in seconds. An IP address that sends 30 requests in a minute with a missing or wrong key is blocked until the minute is up.
Idempotency
POST /envelopes takes an Idempotency-Key header: any string up to 255 characters that's unique to the envelope you mean to create, such as a UUID or your own order number. If a request times out, send it again with the same key: within 24 hours you get the first answer again, with the header Idempotent-Replayed: true, instead of a second envelope.
- The same key with a different request: 422
idempotency_key_reused. - The same key while the first request is still running: 409
idempotency_in_progress. - Only successful answers are kept. After an error, fix the request and send it again with the same key.
Keys are per workspace.
Pagination
GET /envelopes answers a page at a time: per_page (25 unless you ask for up to 100) and page. meta.total and meta.last_page say how many there are, and links.next is the next page's URL, or null on the last page.
Envelope status
| Status | Meaning |
|---|---|
draft |
Not sent yet |
sent |
Out for signing |
expired |
Sent, but the signing links expired before everyone signed |
changes_requested |
An approver asked for changes, so signing is paused |
completed |
Everyone has signed and the signed PDF is ready |
declined |
A signer declined |
voided |
You voided it |
The signed PDF takes a few seconds to build after the last signature. Until it's ready the envelope stays sent, with every recipient signed, so completed always means the signed PDF can be downloaded. To follow progress, check GET /envelopes/{id} or list GET /envelopes?status=completed; once a minute is plenty.
A recipient is not_sent while the envelope is a draft, waiting until it's their turn, then sent, viewed, and signed (an approver: approved), declined or changes_requested. not_needed means someone else in their signing group signed. A CC is pending, then copied once they're sent the signed copy.
Signing order
Give each signer and approver a routing_order, starting at 1. Lower numbers go first, and recipients with the same number are emailed at the same time. The next number starts when everyone before it has finished. Give a routing_order to every signer and approver, or to none, and they go in list order.
Approvers approve the document before the recipients after them; they fill in nothing. CCs are sent the signed PDF when it's completed.
Signing links and codes
The API never returns signing links or signing codes. Closign emails each recipient their own link when it's their turn, and confirms their email address with a one-time code before they sign.
Error codes
Every error.code the API answers with.
| Code | Status | Meaning |
|---|---|---|
unauthenticated |
401 | No API key in the Authorization header.
|
invalid_api_key |
401 | The key isn't valid: mistyped, revoked or expired. |
workspace_suspended |
403 | The workspace is suspended, so its keys don't work. |
plan_without_api |
403 | The workspace's plan doesn't include API access. |
no_active_member |
403 | There's nobody active in the workspace for the key to act as. |
missing_ability |
403 | The key doesn't have the permission this endpoint needs; details.required names it.
|
forbidden |
403 | Not allowed. |
rate_limited |
429 | Too many requests. Wait for the Retry-After seconds.
|
not_found |
404 | No such endpoint, or no such envelope or template in this workspace. |
method_not_allowed |
405 | The endpoint doesn't take this HTTP method. |
validation_error |
422 | Something in the request isn't valid; details lists each field's problems.
|
http_error |
4xx | Another HTTP error, such as a request that's too large (413). |
server_error |
500 | Something went wrong on our side. It's safe to retry; if it keeps happening, write to support@closign.io. |
invalid_idempotency_key |
422 | Idempotency-Key is empty or longer than 255 characters.
|
idempotency_key_reused |
422 | This Idempotency-Key was already used for a different request.
|
idempotency_in_progress |
409 | A request with this Idempotency-Key is still being processed.
|
unsupported_file_type |
422 | The file isn't a PDF. |
file_too_large |
422 | The PDF is over the size limit. |
pdf_encrypted |
422 | The PDF is password-protected. Remove the password and send it again. |
pdf_unreadable |
422 | The PDF can't be read. Export it again as a standard PDF. |
unknown_template |
422 | There's no template with this template_id.
|
unknown_template_role |
422 | A signer's template_role is missing or isn't one of the template's roles; details.roles lists them.
|
duplicate_template_role |
422 | Two signers have the same template_role.
|
missing_template_roles |
422 | One of the template's roles has no signer; details.missing lists them.
|
unknown_variables |
422 | The template has no such variable; details.unknown lists them.
|
invalid_variables |
422 | A value doesn't fit its variable's type or options; details.invalid lists them.
|
missing_variables |
422 | A required variable has no value; details.missing lists them.
|
template_failed |
422 or 500 | The document couldn't be made from the template. |
missing_fields |
422 | A signer has no field to fill in; details.recipients lists them.
|
plan_limit_reached |
402 | The workspace has sent as many documents as its plan allows this month; the message says what to do, and details.upgrade_url links to the plans.
|
restricted_document |
422 | The document looks like one the IT Act excludes from e-signing. Set acknowledge_restricted to send it anyway.
|
cannot_send |
409 or 422 | The envelope can't be sent as it is; the message says why. |
already_sent |
409 | The envelope isn't a draft any more. |
not_out_for_signing |
409 | Only an envelope that's out for signing can be voided or reminded. |
nobody_to_remind |
409 | Everyone whose turn it is has already finished. |
reminded_recently |
429 | Everyone whose turn it is was reminded in the last hour. |
not_completed |
409 | The signed PDF is ready once the envelope is completed. |
not_finished |
409 | The audit certificate is ready once the envelope is completed, declined or voided. |
Account
The key itself.
Check a key
/me
The key's workspace and plan, its permissions, who it acts as, and the rate limit. Any key can call it.
curl https://closign.io/api/v1/me \
-H "Authorization: Bearer $CLOSIGN_API_KEY"
{
"data": {
"workspace": {
"name": "Your company",
"plan": "Team"
},
"key": {
"name": "CRM",
"prefix": "cs_live_PhnU",
"abilities": [
"envelopes:read",
"envelopes:write",
"templates:read"
]
},
"acting_as": {
"name": "Workspace admin",
"email": "admin@example.com"
},
"rate_limit": {
"requests_per_minute": 60
}
}
}
Envelopes
Documents sent for signing: create, send, follow, void, remind and download.
Create an envelope
/envelopes
envelopes:write
Create an envelope from a PDF or a template, with its recipients. Send it straight away with "send": true, or keep it as a draft and send it later with POST /envelopes/{id}/send.
The document is one of:
file: the PDF, asmultipart/form-data(up to 25 MB);file_base64: the PDF in base64, in JSON (up to about 23 MB), with itsfile_name;template_id: one of the templates fromGET /templates, filled in withvariables.
A PDF is checked by its content, and password-protected PDFs are refused.
Recipients are a signer (fills in fields and signs), an approver (approves before the recipients after them, and fills in nothing) or a cc (is sent the signed copy). There's at least one signer. See Signing order for routing_order.
Fields (for a PDF) say where each signer fills in. A field's position is in % of the page: x and y are its top-left corner, measured from the page's top-left, and width and height its size. recipient_index is the signer's position in recipients, counting from 0. To send, every signer needs at least one field.
From a template, give every signer a template_role (a role's key or name from GET /templates/{id}, one signer per role) and fill in variables by name. The template places its own fields.
Sending checks what the app checks: the plan's document limit (402), and a document the IT Act excludes from e-signing (a will or a power of attorney, say) needs acknowledge_restricted. Creating and sending is all or nothing: when the send is refused, no envelope is kept.
Parameters
| Parameter | Type | Description |
|---|---|---|
Idempotency-Key
header
|
string |
Retry safely: the same key within 24 hours gets the first answer again instead of a second envelope. Up to 255 characters. |
Body
| Field | Type | Description |
|---|---|---|
title
|
string |
Defaults to the file's name or the template's title. Up to 255 characters. |
message
|
string |
Your message in the signing request email. Up to 2,000 characters. |
file_base64
|
base64 |
The PDF in base64; a |
file_name
|
string |
The PDF's file name, with Up to 255 characters. |
template_id
|
string |
Make it from this template ( Up to 100 characters. |
variables
|
object |
With |
letterhead
|
boolean |
With |
initials
|
boolean |
With |
recipients
required
|
array of objects |
1 to 50 items. |
recipients[].name
required
|
string |
Up to 255 characters. |
recipients[].email
required
|
string (email) |
Up to 255 characters. |
recipients[].role
required
|
string |
|
recipients[].routing_order
|
integer |
Signers and approvers: lower goes first, equal at the same time. Give it to all of them or none (then list order). 1 to 50. |
recipients[].template_role
|
string |
With Up to 120 characters. |
fields
|
array of objects |
For a PDF: where each signer fills in. Up to 500 items. |
fields[].recipient_index
required
|
integer |
The signer's position in 0 or more. |
fields[].type
required
|
string |
|
fields[].page
required
|
integer |
1 or more. |
fields[].x
required
|
number |
The left edge, in % of the page's width. 0 to 100. |
fields[].y
required
|
number |
The top edge, in % of the page's height from the top. 0 to 100. |
fields[].width
required
|
number |
In % of the page's width; More than 0, up to 100. |
fields[].height
required
|
number |
In % of the page's height; More than 0, up to 100. |
fields[].label
|
string |
Up to 255 characters. |
fields[].required
|
boolean |
Default true. |
fields[].options
|
array of strings |
For Up to 30 items. |
send
|
boolean |
Send it now. Otherwise it's a draft, sent with Default false. |
expires_in_days
|
integer |
The signing links expire after this many days; 0 means never. Defaults to the workspace's setting.
|
reminder_every_days
|
integer |
Remind whoever's turn it is every this many days; 0 means off. Defaults to the workspace's setting.
|
acknowledge_restricted
|
boolean |
Send it even if it looks like a document the IT Act excludes from e-signing ( |
Or as multipart/form-data
multipart/form-data, to upload the PDF as a file. The other fields are as in JSON; recipients and fields are JSON strings, and booleans are true or false.
| Field | Type | Description |
|---|---|---|
file
required
|
file |
The PDF, up to 25 MB. |
recipients
required
|
string |
The |
fields
|
string |
The |
curl https://closign.io/api/v1/envelopes \
-H "Authorization: Bearer $CLOSIGN_API_KEY" \
-H "Idempotency-Key: order-1042" \
-F file=@services-agreement.pdf \
-F title="Master services agreement" \
-F send=true \
-F 'recipients=[{"name":"Legal approver","email":"legal@example.com","role":"approver","routing_order":1},{"name":"Client signatory","email":"client@example.com","role":"signer","routing_order":2}]' \
-F 'fields=[{"recipient_index":1,"type":"signature","page":11,"x":10,"y":72,"width":30,"height":6}]'
{
"title": "Master services agreement",
"message": "Please review and sign by Friday.",
"file_base64": "JVBERi0xLjMKMyAwIG9iago8PC9UeXBl…",
"file_name": "services-agreement.pdf",
"recipients": [
{
"name": "Legal approver",
"email": "legal@example.com",
"role": "approver",
"routing_order": 1
},
{
"name": "Client signatory",
"email": "client@example.com",
"role": "signer",
"routing_order": 2
},
{
"name": "Our director",
"email": "director@example.com",
"role": "signer",
"routing_order": 3
},
{
"name": "Accounts",
"email": "accounts@example.com",
"role": "cc"
}
],
"fields": [
{
"recipient_index": 1,
"type": "signature",
"page": 11,
"x": 10,
"y": 72,
"width": 30,
"height": 6
},
{
"recipient_index": 1,
"type": "date",
"page": 11,
"x": 10,
"y": 80,
"width": 20,
"height": 3
},
{
"recipient_index": 2,
"type": "signature",
"page": 11,
"x": 55,
"y": 72,
"width": 30,
"height": 6
}
],
"send": true,
"reminder_every_days": 2,
"expires_in_days": 30
}
{
"template_id": "quotation-acceptance",
"title": "Quotation Q-2026-114",
"variables": {
"quote_ref": "Q-2026-114 dated 28 September 2026",
"scope": "Website redesign and six months of support",
"timeline": "8 weeks from the advance",
"fee_amount": "177000",
"currency": "INR"
},
"recipients": [
{
"name": "Our director",
"email": "director@example.com",
"role": "signer",
"template_role": "Supplier"
},
{
"name": "Client signatory",
"email": "client@example.com",
"role": "signer",
"template_role": "Client"
}
],
"send": true
}
{
"data": {
"id": "env_01M4DCFF50MMEY085SAM4RXQYY",
"object": "envelope",
"title": "Master services agreement",
"status": "sent",
"message": "Please review and sign by Friday.",
"source": "upload",
"template_id": null,
"pages": 11,
"signing_order": "sequential",
"reminder_every_days": 2,
"expires_at": "2026-11-07T23:59:59Z",
"void_reason": null,
"created_at": "2026-10-08T09:30:12Z",
"sent_at": "2026-10-08T09:30:13Z",
"completed_at": null,
"voided_at": null,
"recipients": [
{
"id": "rcp_01M4DCH9R0WTT0VT7XNH45ES50",
"object": "recipient",
"name": "Legal approver",
"email": "legal@example.com",
"role": "approver",
"routing_order": 1,
"status": "sent",
"sent_at": "2026-10-08T09:30:13Z",
"viewed_at": null,
"acted_at": null,
"decline_reason": null,
"comment": null
},
{
"id": "rcp_01M4DCK4B0G4THVBMA661ZT2QG",
"object": "recipient",
"name": "Client signatory",
"email": "client@example.com",
"role": "signer",
"routing_order": 2,
"status": "waiting",
"sent_at": null,
"viewed_at": null,
"acted_at": null,
"decline_reason": null,
"comment": null
},
{
"id": "rcp_01M4DCMYY0637M1DPK63THFQEG",
"object": "recipient",
"name": "Our director",
"email": "director@example.com",
"role": "signer",
"routing_order": 3,
"status": "waiting",
"sent_at": null,
"viewed_at": null,
"acted_at": null,
"decline_reason": null,
"comment": null
},
{
"id": "rcp_01M4DCPSH0EF2CNVZWVJ7Z6KXY",
"object": "recipient",
"name": "Accounts",
"email": "accounts@example.com",
"role": "cc",
"routing_order": null,
"status": "pending",
"sent_at": null,
"viewed_at": null,
"acted_at": null
}
]
}
}
Errors
| Code | Status | Meaning |
|---|---|---|
validation_error |
422 | Something in the request isn't valid; details lists each field's problems.
|
unsupported_file_type |
422 | The file isn't a PDF. |
file_too_large |
422 | The PDF is over the size limit. |
pdf_encrypted |
422 | The PDF is password-protected. Remove the password and send it again. |
pdf_unreadable |
422 | The PDF can't be read. Export it again as a standard PDF. |
unknown_template |
422 | There's no template with this template_id.
|
unknown_template_role |
422 | A signer's template_role is missing or isn't one of the template's roles; details.roles lists them.
|
duplicate_template_role |
422 | Two signers have the same template_role.
|
missing_template_roles |
422 | One of the template's roles has no signer; details.missing lists them.
|
unknown_variables |
422 | The template has no such variable; details.unknown lists them.
|
invalid_variables |
422 | A value doesn't fit its variable's type or options; details.invalid lists them.
|
missing_variables |
422 | A required variable has no value; details.missing lists them.
|
template_failed |
422 or 500 | The document couldn't be made from the template. |
missing_fields |
422 | A signer has no field to fill in; details.recipients lists them.
|
plan_limit_reached |
402 | The workspace has sent as many documents as its plan allows this month; the message says what to do, and details.upgrade_url links to the plans.
|
restricted_document |
422 | The document looks like one the IT Act excludes from e-signing. Set acknowledge_restricted to send it anyway.
|
cannot_send |
409 or 422 | The envelope can't be sent as it is; the message says why. |
invalid_idempotency_key |
422 | Idempotency-Key is empty or longer than 255 characters.
|
idempotency_key_reused |
422 | This Idempotency-Key was already used for a different request.
|
idempotency_in_progress |
409 | A request with this Idempotency-Key is still being processed.
|
List envelopes
/envelopes
envelopes:read
The workspace's envelopes, newest first, a page at a time. Filter by status.
Parameters
| Parameter | Type | Description |
|---|---|---|
status
query
|
array of strings |
One or more statuses, separated by commas.
|
page
query
|
integer |
1 or more; default 1. |
per_page
query
|
integer |
1 to 100; default 25. |
curl "https://closign.io/api/v1/envelopes?status=sent,completed&per_page=50" \
-H "Authorization: Bearer $CLOSIGN_API_KEY"
{
"data": [
{
"id": "env_01M4DCFF50MMEY085SAM4RXQYY",
"object": "envelope",
"title": "Master services agreement",
"status": "completed",
"message": "Please review and sign by Friday.",
"source": "upload",
"template_id": null,
"pages": 11,
"signing_order": "sequential",
"reminder_every_days": 2,
"expires_at": "2026-11-07T23:59:59Z",
"void_reason": null,
"created_at": "2026-10-08T09:30:12Z",
"sent_at": "2026-10-08T09:30:13Z",
"completed_at": "2026-10-09T14:05:41Z",
"voided_at": null,
"recipients": [
{
"id": "rcp_01M4DCH9R0WTT0VT7XNH45ES50",
"object": "recipient",
"name": "Legal approver",
"email": "legal@example.com",
"role": "approver",
"routing_order": 1,
"status": "approved",
"sent_at": "2026-10-08T09:30:13Z",
"viewed_at": "2026-10-08T10:02:55Z",
"acted_at": "2026-10-08T10:06:20Z",
"decline_reason": null,
"comment": "Clause 7 reads well now."
},
{
"id": "rcp_01M4DCK4B0G4THVBMA661ZT2QG",
"object": "recipient",
"name": "Client signatory",
"email": "client@example.com",
"role": "signer",
"routing_order": 2,
"status": "signed",
"sent_at": "2026-10-08T10:06:21Z",
"viewed_at": "2026-10-08T16:40:02Z",
"acted_at": "2026-10-08T16:44:37Z",
"decline_reason": null,
"comment": null
},
{
"id": "rcp_01M4DCMYY0637M1DPK63THFQEG",
"object": "recipient",
"name": "Our director",
"email": "director@example.com",
"role": "signer",
"routing_order": 3,
"status": "signed",
"sent_at": "2026-10-08T16:44:38Z",
"viewed_at": "2026-10-09T14:01:10Z",
"acted_at": "2026-10-09T14:05:39Z",
"decline_reason": null,
"comment": null
},
{
"id": "rcp_01M4DCPSH0EF2CNVZWVJ7Z6KXY",
"object": "recipient",
"name": "Accounts",
"email": "accounts@example.com",
"role": "cc",
"routing_order": null,
"status": "copied",
"sent_at": "2026-10-09T14:05:41Z",
"viewed_at": null,
"acted_at": null
}
]
}
],
"links": {
"first": "https://closign.io/api/v1/envelopes?page=1",
"last": "https://closign.io/api/v1/envelopes?page=4",
"prev": null,
"next": "https://closign.io/api/v1/envelopes?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 4,
"links": [],
"path": "https://closign.io/api/v1/envelopes",
"per_page": 25,
"to": 25,
"total": 87
}
}
Errors
| Code | Status | Meaning |
|---|---|---|
validation_error |
422 | Something in the request isn't valid; details lists each field's problems.
|
Get an envelope
/envelopes/{id}
envelopes:read
An envelope with its recipients and where each one is.
Parameters
| Parameter | Type | Description |
|---|---|---|
id
path required
|
string |
The envelope's ID. |
curl https://closign.io/api/v1/envelopes/env_01M4DCFF50MMEY085SAM4RXQYY \
-H "Authorization: Bearer $CLOSIGN_API_KEY"
{
"data": {
"id": "env_01M4DCFF50MMEY085SAM4RXQYY",
"object": "envelope",
"title": "Master services agreement",
"status": "completed",
"message": "Please review and sign by Friday.",
"source": "upload",
"template_id": null,
"pages": 11,
"signing_order": "sequential",
"reminder_every_days": 2,
"expires_at": "2026-11-07T23:59:59Z",
"void_reason": null,
"created_at": "2026-10-08T09:30:12Z",
"sent_at": "2026-10-08T09:30:13Z",
"completed_at": "2026-10-09T14:05:41Z",
"voided_at": null,
"recipients": [
{
"id": "rcp_01M4DCH9R0WTT0VT7XNH45ES50",
"object": "recipient",
"name": "Legal approver",
"email": "legal@example.com",
"role": "approver",
"routing_order": 1,
"status": "approved",
"sent_at": "2026-10-08T09:30:13Z",
"viewed_at": "2026-10-08T10:02:55Z",
"acted_at": "2026-10-08T10:06:20Z",
"decline_reason": null,
"comment": "Clause 7 reads well now."
},
{
"id": "rcp_01M4DCK4B0G4THVBMA661ZT2QG",
"object": "recipient",
"name": "Client signatory",
"email": "client@example.com",
"role": "signer",
"routing_order": 2,
"status": "signed",
"sent_at": "2026-10-08T10:06:21Z",
"viewed_at": "2026-10-08T16:40:02Z",
"acted_at": "2026-10-08T16:44:37Z",
"decline_reason": null,
"comment": null
},
{
"id": "rcp_01M4DCMYY0637M1DPK63THFQEG",
"object": "recipient",
"name": "Our director",
"email": "director@example.com",
"role": "signer",
"routing_order": 3,
"status": "signed",
"sent_at": "2026-10-08T16:44:38Z",
"viewed_at": "2026-10-09T14:01:10Z",
"acted_at": "2026-10-09T14:05:39Z",
"decline_reason": null,
"comment": null
},
{
"id": "rcp_01M4DCPSH0EF2CNVZWVJ7Z6KXY",
"object": "recipient",
"name": "Accounts",
"email": "accounts@example.com",
"role": "cc",
"routing_order": null,
"status": "copied",
"sent_at": "2026-10-09T14:05:41Z",
"viewed_at": null,
"acted_at": null
}
]
}
}
Errors
| Code | Status | Meaning |
|---|---|---|
not_found |
404 | No such endpoint, or no such envelope or template in this workspace. |
Send a draft
/envelopes/{id}/send
envelopes:write
Send a draft now, and optionally change its message, reminders and expiry. Every signer needs a field, and the plan's document limit applies.
Parameters
| Parameter | Type | Description |
|---|---|---|
id
path required
|
string |
The envelope's ID. |
Body
| Field | Type | Description |
|---|---|---|
message
|
string |
Replaces the message in the signing request email. Up to 2,000 characters. |
expires_in_days
|
integer |
|
reminder_every_days
|
integer |
|
acknowledge_restricted
|
boolean |
|
curl -X POST https://closign.io/api/v1/envelopes/env_01M4DCFF50MMEY085SAM4RXQYY/send \
-H "Authorization: Bearer $CLOSIGN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"message":"Please review and sign by Friday.","expires_in_days":30}'
{
"data": {
"id": "env_01M4DCFF50MMEY085SAM4RXQYY",
"object": "envelope",
"title": "Master services agreement",
"status": "sent",
"message": "Please review and sign by Friday.",
"source": "upload",
"template_id": null,
"pages": 11,
"signing_order": "sequential",
"reminder_every_days": 2,
"expires_at": "2026-11-07T23:59:59Z",
"void_reason": null,
"created_at": "2026-10-08T09:30:12Z",
"sent_at": "2026-10-08T09:30:13Z",
"completed_at": null,
"voided_at": null,
"recipients": [
{
"id": "rcp_01M4DCH9R0WTT0VT7XNH45ES50",
"object": "recipient",
"name": "Legal approver",
"email": "legal@example.com",
"role": "approver",
"routing_order": 1,
"status": "sent",
"sent_at": "2026-10-08T09:30:13Z",
"viewed_at": null,
"acted_at": null,
"decline_reason": null,
"comment": null
},
{
"id": "rcp_01M4DCK4B0G4THVBMA661ZT2QG",
"object": "recipient",
"name": "Client signatory",
"email": "client@example.com",
"role": "signer",
"routing_order": 2,
"status": "waiting",
"sent_at": null,
"viewed_at": null,
"acted_at": null,
"decline_reason": null,
"comment": null
},
{
"id": "rcp_01M4DCMYY0637M1DPK63THFQEG",
"object": "recipient",
"name": "Our director",
"email": "director@example.com",
"role": "signer",
"routing_order": 3,
"status": "waiting",
"sent_at": null,
"viewed_at": null,
"acted_at": null,
"decline_reason": null,
"comment": null
},
{
"id": "rcp_01M4DCPSH0EF2CNVZWVJ7Z6KXY",
"object": "recipient",
"name": "Accounts",
"email": "accounts@example.com",
"role": "cc",
"routing_order": null,
"status": "pending",
"sent_at": null,
"viewed_at": null,
"acted_at": null
}
]
}
}
Errors
| Code | Status | Meaning |
|---|---|---|
not_found |
404 | No such endpoint, or no such envelope or template in this workspace. |
already_sent |
409 | The envelope isn't a draft any more. |
missing_fields |
422 | A signer has no field to fill in; details.recipients lists them.
|
plan_limit_reached |
402 | The workspace has sent as many documents as its plan allows this month; the message says what to do, and details.upgrade_url links to the plans.
|
restricted_document |
422 | The document looks like one the IT Act excludes from e-signing. Set acknowledge_restricted to send it anyway.
|
cannot_send |
409 or 422 | The envelope can't be sent as it is; the message says why. |
validation_error |
422 | Something in the request isn't valid; details lists each field's problems.
|
Void an envelope
/envelopes/{id}/void
envelopes:write
Cancel an envelope that's out for signing (sent, expired or changes_requested). The signing links stop working, and everyone who was emailed a link is told, with your reason.
Parameters
| Parameter | Type | Description |
|---|---|---|
id
path required
|
string |
The envelope's ID. |
Body
| Field | Type | Description |
|---|---|---|
reason
required
|
string |
Everyone who was emailed a signing link is told. Up to 1,000 characters. |
curl -X POST https://closign.io/api/v1/envelopes/env_01M4DCFF50MMEY085SAM4RXQYY/void \
-H "Authorization: Bearer $CLOSIGN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reason":"Sending a corrected version."}'
{
"data": {
"id": "env_01M4DCFF50MMEY085SAM4RXQYY",
"object": "envelope",
"title": "Master services agreement",
"status": "voided",
"message": "Please review and sign by Friday.",
"source": "upload",
"template_id": null,
"pages": 11,
"signing_order": "sequential",
"reminder_every_days": 2,
"expires_at": "2026-11-07T23:59:59Z",
"void_reason": "Sending a corrected version.",
"created_at": "2026-10-08T09:30:12Z",
"sent_at": "2026-10-08T09:30:13Z",
"completed_at": null,
"voided_at": "2026-10-08T11:15:00Z",
"recipients": [
{
"id": "rcp_01M4DCH9R0WTT0VT7XNH45ES50",
"object": "recipient",
"name": "Legal approver",
"email": "legal@example.com",
"role": "approver",
"routing_order": 1,
"status": "sent",
"sent_at": "2026-10-08T09:30:13Z",
"viewed_at": null,
"acted_at": null,
"decline_reason": null,
"comment": null
},
{
"id": "rcp_01M4DCK4B0G4THVBMA661ZT2QG",
"object": "recipient",
"name": "Client signatory",
"email": "client@example.com",
"role": "signer",
"routing_order": 2,
"status": "waiting",
"sent_at": null,
"viewed_at": null,
"acted_at": null,
"decline_reason": null,
"comment": null
},
{
"id": "rcp_01M4DCMYY0637M1DPK63THFQEG",
"object": "recipient",
"name": "Our director",
"email": "director@example.com",
"role": "signer",
"routing_order": 3,
"status": "waiting",
"sent_at": null,
"viewed_at": null,
"acted_at": null,
"decline_reason": null,
"comment": null
},
{
"id": "rcp_01M4DCPSH0EF2CNVZWVJ7Z6KXY",
"object": "recipient",
"name": "Accounts",
"email": "accounts@example.com",
"role": "cc",
"routing_order": null,
"status": "pending",
"sent_at": null,
"viewed_at": null,
"acted_at": null
}
]
}
}
Errors
| Code | Status | Meaning |
|---|---|---|
not_found |
404 | No such endpoint, or no such envelope or template in this workspace. |
not_out_for_signing |
409 | Only an envelope that's out for signing can be voided or reminded. |
validation_error |
422 | Something in the request isn't valid; details lists each field's problems.
|
Remind
/envelopes/{id}/remind
envelopes:write
Email the signing link again to everyone whose turn it is. Each recipient can be reminded once an hour; the answer says who was reminded and who was skipped.
Parameters
| Parameter | Type | Description |
|---|---|---|
id
path required
|
string |
The envelope's ID. |
curl -X POST https://closign.io/api/v1/envelopes/env_01M4DCFF50MMEY085SAM4RXQYY/remind \
-H "Authorization: Bearer $CLOSIGN_API_KEY"
{
"data": {
"reminded": [
{
"id": "rcp_01M4DCK4B0G4THVBMA661ZT2QG",
"email": "client@example.com"
}
],
"skipped": []
}
}
Errors
| Code | Status | Meaning |
|---|---|---|
not_found |
404 | No such endpoint, or no such envelope or template in this workspace. |
not_out_for_signing |
409 | Only an envelope that's out for signing can be voided or reminded. |
nobody_to_remind |
409 | Everyone whose turn it is has already finished. |
reminded_recently |
429 | Everyone whose turn it is was reminded in the last hour. |
Download the signed PDF
/envelopes/{id}/document
envelopes:read
The signed PDF, with the audit certificate at the end, once the envelope is completed.
Parameters
| Parameter | Type | Description |
|---|---|---|
id
path required
|
string |
The envelope's ID. |
curl -o services-agreement-signed.pdf https://closign.io/api/v1/envelopes/env_01M4DCFF50MMEY085SAM4RXQYY/document \
-H "Authorization: Bearer $CLOSIGN_API_KEY"
Response: The PDF file itself (Content-Type: application/pdf).
Errors
| Code | Status | Meaning |
|---|---|---|
not_found |
404 | No such endpoint, or no such envelope or template in this workspace. |
not_completed |
409 | The signed PDF is ready once the envelope is completed. |
Download the audit certificate
/envelopes/{id}/audit
envelopes:read
The audit certificate on its own: who did what and when, with IP addresses and the document's fingerprints. Ready once the envelope is completed, declined or voided.
Parameters
| Parameter | Type | Description |
|---|---|---|
id
path required
|
string |
The envelope's ID. |
curl -o services-agreement-certificate.pdf https://closign.io/api/v1/envelopes/env_01M4DCFF50MMEY085SAM4RXQYY/audit \
-H "Authorization: Bearer $CLOSIGN_API_KEY"
Response: The PDF file itself (Content-Type: application/pdf).
Errors
| Code | Status | Meaning |
|---|---|---|
not_found |
404 | No such endpoint, or no such envelope or template in this workspace. |
not_finished |
409 | The audit certificate is ready once the envelope is completed, declined or voided. |
Templates
The template library an envelope can be made from.
List templates
/templates
templates:read
The templates an envelope can be made from, with who signs them (roles) and what fills them in (variables).
curl https://closign.io/api/v1/templates \
-H "Authorization: Bearer $CLOSIGN_API_KEY"
{
"data": [
{
"id": "quotation-acceptance",
"object": "template",
"title": "Quotation acceptance",
"description": "The client accepts a quotation or estimate so work can start: scope, price, GST, advance and timeline.",
"category": "business",
"roles": [
{
"key": "party_1",
"name": "Supplier",
"witness": false
},
{
"key": "party_2",
"name": "Client",
"witness": false
}
],
"variables": [
{
"name": "quote_ref",
"label": "Quotation number / date",
"type": "string",
"required": true,
"hint": "e.g. \"Q-2026-114 dated 28 September 2026\""
},
{
"name": "scope",
"label": "Work or goods quoted",
"type": "text",
"required": true
},
{
"name": "timeline",
"label": "Delivery / completion",
"type": "string",
"required": true
},
{
"name": "fee_amount",
"label": "Amount",
"type": "money",
"required": true
},
{
"name": "currency",
"label": "Currency",
"type": "choice",
"required": true,
"default": "INR",
"options": [
"INR",
"USD",
"EUR",
"GBP"
]
},
{
"name": "advance",
"label": "Advance",
"type": "money",
"required": false
}
]
}
]
}
Get a template
/templates/{id}
templates:read
One template: its roles, for each signer's template_role, and its variables, for variables.
Parameters
| Parameter | Type | Description |
|---|---|---|
id
path required
|
string |
The template's ID. |
curl https://closign.io/api/v1/templates/quotation-acceptance \
-H "Authorization: Bearer $CLOSIGN_API_KEY"
{
"data": {
"id": "quotation-acceptance",
"object": "template",
"title": "Quotation acceptance",
"description": "The client accepts a quotation or estimate so work can start: scope, price, GST, advance and timeline.",
"category": "business",
"roles": [
{
"key": "party_1",
"name": "Supplier",
"witness": false
},
{
"key": "party_2",
"name": "Client",
"witness": false
}
],
"variables": [
{
"name": "quote_ref",
"label": "Quotation number / date",
"type": "string",
"required": true,
"hint": "e.g. \"Q-2026-114 dated 28 September 2026\""
},
{
"name": "scope",
"label": "Work or goods quoted",
"type": "text",
"required": true
},
{
"name": "timeline",
"label": "Delivery / completion",
"type": "string",
"required": true
},
{
"name": "fee_amount",
"label": "Amount",
"type": "money",
"required": true
},
{
"name": "currency",
"label": "Currency",
"type": "choice",
"required": true,
"default": "INR",
"options": [
"INR",
"USD",
"EUR",
"GBP"
]
},
{
"name": "advance",
"label": "Advance",
"type": "money",
"required": false
}
]
}
}
Errors
| Code | Status | Meaning |
|---|---|---|
not_found |
404 | No such endpoint, or no such envelope or template in this workspace. |
Objects
What the API answers with, field by field.
Envelope
A document sent, or to be sent, for signing.
| Field | Type | Description |
|---|---|---|
id
|
string |
|
object
|
string |
|
title
|
string |
|
status
|
string |
See Envelope status.
|
message
|
string or null |
Your message in the signing request email. |
source
|
string |
Made from a PDF, from a template, or drafted with AI in the app.
|
template_id
|
string or null |
The template it was made from. |
pages
|
integer or null |
|
signing_order
|
string |
|
reminder_every_days
|
integer |
Automatic reminders every this many days; 0 means off. |
expires_at
|
timestamp or null |
When the signing links expire; |
void_reason
|
string or null |
|
created_at
|
timestamp |
|
sent_at
|
timestamp or null |
|
completed_at
|
timestamp or null |
When the signed PDF was ready. |
voided_at
|
timestamp or null |
|
recipients
|
array of Recipient objects |
Signers and approvers in routing order, then CCs. |
Recipient
A signer, an approver or a CC. Signing links and codes are never part of it.
| Field | Type | Description |
|---|---|---|
id
|
string |
|
object
|
string |
|
name
|
string or null |
|
email
|
string (email) |
|
role
|
string |
|
routing_order
|
integer or null |
|
status
|
string |
See Envelope status.
|
sent_at
|
timestamp or null |
When their signing request, or for a CC the signed copy, was emailed. |
viewed_at
|
timestamp or null |
When they first opened the document. |
acted_at
|
timestamp or null |
When they signed, approved, declined or asked for changes. |
decline_reason
not always present
|
string or null |
Signers and approvers only: why they declined. |
comment
not always present
|
string or null |
Signers and approvers only: an approver's note with their approval, or the changes they asked for. |
Template
A template an envelope can be made from.
| Field | Type | Description |
|---|---|---|
id
|
string |
The template's slug. |
object
|
string |
|
title
|
string |
|
description
|
string or null |
|
category
|
string or null |
|
roles
|
array of objects |
Who signs it: each signer's |
roles[].key
|
string |
|
roles[].name
|
string |
|
roles[].witness
|
boolean |
Signs as a witness. |
variables
|
array of objects |
What fills it in, by |
variables[].name
|
string |
|
variables[].label
|
string |
|
variables[].type
|
string |
|
variables[].required
|
boolean |
|
variables[].default
not always present
|
any |
Used when no value is given. |
variables[].options
not always present
|
array of strings |
The values a |
variables[].hint
not always present
|
string |
An example of what to fill in. |