Documents
Files you send for reading or signing: upload a contract, add recipients, send it, follow what happens to it and download it again. A document is core to every Closepact plan — no module is needed.
The usual integration
POST /documents— upload the PDF (multipart). It is processed in the background;statusgoes fromprocessingtoready.POST /documents/{document}/send— name the recipients. Each gets an email with a private link; the document'slifecycle_statusbecomessent.- Wait for the
signature.completedwebhook (or pollGET /documents/{document}untillifecycle_statusissigned). GET /documents/{document}/download-link— a short-lived URL to fetch the file.
Two statuses. status is about the file: processing → ready, or failed when the virus scan rejects it or processing gives up (quarantined is reserved). lifecycle_status is about the recipients: draft → sent → opened → signed, with expired and canceled as other endings. The happy path only moves forward, and once a terminal status is reached only another terminal status replaces it — with one exception: sending the document again starts a new round at sent, from any status except signed.
Webhooks for this area: signature.requested (sent), document.viewed (a recipient opened their link), signer.signed (one recipient signed), signature.completed (everyone who must sign has) and signature.canceled. Each carries the document_id and the signature request's id; see the Webhooks section for the payloads.
Endpoints
- GET
/documents - POST
/documents - GET
/documents/{document} - PATCH
/documents/{document} - DELETE
/documents/{document} - GET
/documents/{document}/download-link - GET
/documents/{document}/timeline - GET
/documents/{document}/engagement - GET
/documents/{document}/recipients - POST
/documents/{document}/recipients - DELETE
/documents/{document}/recipients/{recipient} - POST
/documents/{document}/send - POST
/documents/{document}/remind
List documents
/documentsEvery document in the organization, newest first, a page at a time. List items carry the document's own fields; owner, recipients, active_send, tags and engagement are only returned by GET /documents/{document}.
Filter by file type, file status or lifecycle_status. Each filter takes one value (filter[lifecycle_status]=sent) or several (filter[lifecycle_status][]=sent&filter[lifecycle_status][]=opened).
Polling for signatures? Prefer the signature.completed webhook; if you must poll, filter on lifecycle_status rather than fetching every document.
curl 'https://api.closepact.com/v1/api/documents' \
-H "Authorization: Bearer $CLOSEPACT_API_KEY"{
"data": [
{
"id": "01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47",
"title": "Serviceavtal 2027 – Byggbolaget i Mälardalen AB",
"type": "pdf",
"mime_type": "application/pdf",
"size_bytes": 284516,
"page_count": 6,
"page_images_status": "ready",
"status": "ready",
"lifecycle_status": "opened",
"value_cents": 18500000,
"currency": "SEK",
"sent_at": "2026-10-12T08:14:03+00:00",
"expires_at": "2026-11-11T22:59:59+00:00",
"days_left": 24,
"checksum_sha256": "9f2c4b7e1d0a8c3f5e6b2d9a7c4e1f0b3d8a6c2e5f9b1d7a4c0e3f6b8d2a5c9e",
"signer_otp_required": true,
"signer_verification_channel": "email",
"bankid_sign_required": false,
"bankid_identification_required": false,
"metadata": {
"original_name": "Serviceavtal_2027_Byggbolaget.pdf",
"has_text": true,
"text_length": 18234,
"processed_at": "2026-10-12T08:12:41+00:00"
},
"created_at": "2026-10-12T08:12:37+00:00",
"updated_at": "2026-10-13T06:51:19+00:00"
}
],
"links": {
"first": null,
"last": null,
"prev": null,
"next": "https://api.closepact.com/v1/api/documents?cursor=eyJjcmVhdGVkX2F0IjoiMjAyNi0xMC0xMiAwODoxMjozNyJ9"
},
"meta": {
"path": "https://api.closepact.com/v1/api/documents",
"per_page": 25,
"next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0xMC0xMiAwODoxMjozNyJ9",
"prev_cursor": null
}
}Query parameters
qstringCase-insensitive match anywhere in the title.
Example
Serviceavtalfilter[type]stringOnly documents of this file type. Repeat as
filter[type][]for several.One of
pdfdocximagevideofilefilter[status]stringOnly documents whose file is in this processing status. Repeat as
filter[status][]for several.One of
processingreadyfailedquarantinedfilter[lifecycle_status]stringOnly documents at this point with their recipients. Repeat as
filter[lifecycle_status][]for several.One of
draftsentopenedsigneddeclinedexpiredcanceledlimitintegerItems per page. Values outside 1–100 are clamped.
Default
251–100cursorstringOpaque cursor from
meta.next_cursorormeta.prev_cursorof the previous page.
Responses
Headers
X-RateLimit-Limit— Requests this key may make per minute.X-RateLimit-Remaining— Requests left in the current minute.
dataarray of objectsrequiredidstring(uuid)requiredtitlestringrequiredtypestringrequiredDerived from the detected MIME type on upload.
One of
pdfdocximagevideofilemime_typestringnullablerequiredsize_bytesintegerrequiredpage_countintegernullablerequiredKnown once a PDF has been processed.
page_images_statusstringnullablerequiredPage images back the signing view and per-page statistics.
unavailablefor anything that is not a PDF.One of
pendingreadyunavailablestatusstringrequiredThe file:
processingafter upload, thenready, orfailedwhen the virus scan rejects it or processing gives up.quarantinedis reserved.One of
processingreadyfailedquarantinedlifecycle_statusstringrequiredThe document with its recipients.
draft— never sent.sent— sent to recipients.opened— a recipient has opened it.signed— every signer and approver has signed (thesignature.completedwebhook fires).expired—expires_atpassed before it was signed.canceled— its signature request was canceled.declinedis reserved: a decline is recorded on the recipient and onactive_send.status.One of
draftsentopenedsigneddeclinedexpiredcanceledvalue_centsintegernullablerequiredWhat the document is worth, in the currency's minor unit (öre for SEK).
currencystringrequiredISO 4217 code.
Example
"SEK"sent_atstring(date-time)nullablerequiredWhen it was first sent.
expires_atstring(date-time)nullablerequireddays_leftintegernullablerequiredWhole days until
expires_at; negative once overdue; null without a deadline.checksum_sha256stringnullablerequiredSHA-256 of the uploaded file, hex.
signer_otp_requiredbooleanrequiredSigners confirm who they are with a six-digit code (valid 10 minutes, 5 tries) before they can sign or decline; the code being sent and entered are recorded in the audit trail. Sent by email, or by SMS when
signer_verification_channelissms. On by default for new documents; change it withPATCH /documents/{document}.signer_verification_channelstringrequiredWhere the one-time code goes.
smssends it to the recipient'sphone_number; a recipient without one, or a server without an SMS provider, gets it by email instead, and the signing page says so. Each SMS is billed as metered usage (0.89 kr).One of
emailsmsbankid_sign_requiredbooleanrequiredSigners and approvers sign with Swedish BankID instead of a typed or drawn signature. The BankID app shows the document's title and SHA-256; BankID's signature and OCSP response are kept as evidence and their hashes recorded in the audit trail. Each completed BankID signature is billed as metered usage (4.49 kr). Needs a tier with BankID (402 otherwise) and BankID configured on the server (503
bankid_unavailableotherwise).bankid_identification_requiredbooleanrequiredEvery recipient identifies with BankID before the document is shown to them at all. Same requirements as
bankid_sign_required.metadataobject | array of valuesnullablerequiredFacts recorded while processing the file. Sent as an empty array
[]when there are none.created_atstring(date-time)requiredupdated_atstring(date-time)requiredownerobjectnullableWho uploaded it (for an upload through the API, the member the key acts as); null if that account no longer exists. Detail responses only.
recipientsarray of objectsEveryone it has been sent to, in signing order. Detail and send responses only.
active_sendobjectnullableThe signature request currently in play, or null if it was never sent. Detail and send responses only. Full detail:
GET /signatures/{id}.tagsarray of objectsDetail responses only.
engagementobjectHow much a document has been read. Detail responses only.
linksobjectrequiredmetaobjectrequired
- 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.Errors
- 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs:Errors
featurenames the capability (the operation'sx-feature) andrequired_tierthe cheapest tier that grants it. Every operation answers this with"feature": "core.api"when no module the organization holds is on a tier that includes the API. - 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (Errors
module_seat_required— an administrator assigns seats under Settings → Users), or the organization is suspended. - 429The key has made more requests this minute than its limit allows.Errors
Upload a document
/documentswrite Permission documents.uploadUploads a file as multipart/form-data and creates a document in draft.
Fields. file (required) and title (optional; defaults to the file name).
Types. Any file type is accepted. type is derived from the detected MIME type: application/pdf → pdf; Word and other Office formats → docx; image/* → image; video/* → video; anything else → file. Upload a PDF for anything that will be signed: page images — what the signing view shows and what per-page reading statistics are counted against — are only produced from PDFs.
Size. Validation allows up to 51,200 KB (50 MiB). The server also caps the whole request at 50 MiB including the multipart overhead, and refuses a larger request before it reaches the API, so in practice keep files a little under the limit — 49 MB is safe.
Processing. The response comes back at once with status: processing. A background job virus-scans the file, extracts its text and renders page images, then sets status to ready (or failed if the file is infected or processing gives up). You can add recipients and send while it is still processing.
curl -X POST 'https://api.closepact.com/v1/api/documents' \
-H "Authorization: Bearer $CLOSEPACT_API_KEY" \
-F 'file=@contract.pdf'{
"data": {
"id": "01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47",
"title": "Serviceavtal 2027 – Byggbolaget i Mälardalen AB",
"type": "pdf",
"mime_type": "application/pdf",
"size_bytes": 284516,
"page_count": null,
"page_images_status": "pending",
"status": "processing",
"lifecycle_status": "draft",
"value_cents": null,
"currency": "SEK",
"sent_at": null,
"expires_at": null,
"days_left": null,
"checksum_sha256": "9f2c4b7e1d0a8c3f5e6b2d9a7c4e1f0b3d8a6c2e5f9b1d7a4c0e3f6b8d2a5c9e",
"signer_otp_required": true,
"signer_verification_channel": "email",
"bankid_sign_required": false,
"bankid_identification_required": false,
"metadata": {
"original_name": "Serviceavtal_2027_Byggbolaget.pdf"
},
"created_at": "2026-10-12T08:12:37+00:00",
"updated_at": "2026-10-12T08:12:37+00:00"
}
}Request body
multipart/form-data · required
filestring(binary)requiredThe file. At most 51,200 KB (50 MiB); see Size above.
titlestringDisplay title. Defaults to the uploaded file's name.
at most 255 characters
Responses
Headers
X-RateLimit-Limit— Requests this key may make per minute.X-RateLimit-Remaining— Requests left in the current minute.
dataobjectrequiredA file in Closepact, and where it stands with its recipients.
idstring(uuid)requiredtitlestringrequiredtypestringrequiredDerived from the detected MIME type on upload.
One of
pdfdocximagevideofilemime_typestringnullablerequiredsize_bytesintegerrequiredpage_countintegernullablerequiredKnown once a PDF has been processed.
page_images_statusstringnullablerequiredPage images back the signing view and per-page statistics.
unavailablefor anything that is not a PDF.One of
pendingreadyunavailablestatusstringrequiredThe file:
processingafter upload, thenready, orfailedwhen the virus scan rejects it or processing gives up.quarantinedis reserved.One of
processingreadyfailedquarantinedlifecycle_statusstringrequiredThe document with its recipients.
draft— never sent.sent— sent to recipients.opened— a recipient has opened it.signed— every signer and approver has signed (thesignature.completedwebhook fires).expired—expires_atpassed before it was signed.canceled— its signature request was canceled.declinedis reserved: a decline is recorded on the recipient and onactive_send.status.One of
draftsentopenedsigneddeclinedexpiredcanceledvalue_centsintegernullablerequiredWhat the document is worth, in the currency's minor unit (öre for SEK).
currencystringrequiredISO 4217 code.
Example
"SEK"sent_atstring(date-time)nullablerequiredWhen it was first sent.
expires_atstring(date-time)nullablerequireddays_leftintegernullablerequiredWhole days until
expires_at; negative once overdue; null without a deadline.checksum_sha256stringnullablerequiredSHA-256 of the uploaded file, hex.
signer_otp_requiredbooleanrequiredSigners confirm who they are with a six-digit code (valid 10 minutes, 5 tries) before they can sign or decline; the code being sent and entered are recorded in the audit trail. Sent by email, or by SMS when
signer_verification_channelissms. On by default for new documents; change it withPATCH /documents/{document}.signer_verification_channelstringrequiredWhere the one-time code goes.
smssends it to the recipient'sphone_number; a recipient without one, or a server without an SMS provider, gets it by email instead, and the signing page says so. Each SMS is billed as metered usage (0.89 kr).One of
emailsmsbankid_sign_requiredbooleanrequiredSigners and approvers sign with Swedish BankID instead of a typed or drawn signature. The BankID app shows the document's title and SHA-256; BankID's signature and OCSP response are kept as evidence and their hashes recorded in the audit trail. Each completed BankID signature is billed as metered usage (4.49 kr). Needs a tier with BankID (402 otherwise) and BankID configured on the server (503
bankid_unavailableotherwise).bankid_identification_requiredbooleanrequiredEvery recipient identifies with BankID before the document is shown to them at all. Same requirements as
bankid_sign_required.metadataobject | array of valuesnullablerequiredFacts recorded while processing the file. Sent as an empty array
[]when there are none.created_atstring(date-time)requiredupdated_atstring(date-time)requiredownerobjectnullableWho uploaded it (for an upload through the API, the member the key acts as); null if that account no longer exists. Detail responses only.
recipientsarray of objectsEveryone it has been sent to, in signing order. Detail and send responses only.
active_sendobjectnullableThe signature request currently in play, or null if it was never sent. Detail and send responses only. Full detail:
GET /signatures/{id}.tagsarray of objectsDetail responses only.
engagementobjectHow much a document has been read. Detail responses only.
- 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.Errors
- 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs:Errors
featurenames the capability (the operation'sx-feature) andrequired_tierthe cheapest tier that grants it. Every operation answers this with"feature": "core.api"when no module the organization holds is on a tier that includes the API. - 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (Errors
module_seat_required— an administrator assigns seats under Settings → Users), or the organization is suspended. - 422The request body or query failed validation.Errors
- 429The key has made more requests this minute than its limit allows.Errors
Retrieve a document
/documents/{document}The document with everything around it: who uploaded it (owner), its recipients, the signature request currently in play (active_send), its tags and a reading summary (engagement).
This is the call to poll for status. lifecycle_status says where the document stands; active_send.status says the same for the signature request (sent, partially_signed, completed, declined, expired, canceled), and each recipient's status shows who has opened, signed or declined.
recipients lists everyone the document has been sent to, including recipients of an earlier signature request that a later send replaced; active_send tells you which request is current.
curl 'https://api.closepact.com/v1/api/documents/01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47' \
-H "Authorization: Bearer $CLOSEPACT_API_KEY"{
"data": {
"id": "01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47",
"title": "Serviceavtal 2027 – Byggbolaget i Mälardalen AB",
"type": "pdf",
"mime_type": "application/pdf",
"size_bytes": 284516,
"page_count": 6,
"page_images_status": "ready",
"status": "ready",
"lifecycle_status": "opened",
"value_cents": 18500000,
"currency": "SEK",
"sent_at": "2026-10-12T08:14:03+00:00",
"expires_at": "2026-11-11T22:59:59+00:00",
"days_left": 24,
"checksum_sha256": "9f2c4b7e1d0a8c3f5e6b2d9a7c4e1f0b3d8a6c2e5f9b1d7a4c0e3f6b8d2a5c9e",
"signer_otp_required": true,
"signer_verification_channel": "email",
"bankid_sign_required": false,
"bankid_identification_required": false,
"metadata": {
"original_name": "Serviceavtal_2027_Byggbolaget.pdf",
"has_text": true,
"text_length": 18234,
"processed_at": "2026-10-12T08:12:41+00:00"
},
"created_at": "2026-10-12T08:12:37+00:00",
"updated_at": "2026-10-13T06:51:19+00:00",
"owner": {
"id": "01a14eb3-c634-7238-bc76-18b09b287f98",
"name": "Anna Lindqvist"
},
"recipients": [
{
"id": "01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03",
"name": "Erik Johansson",
"email": "erik.johansson@byggbolaget.se",
"role": "signer",
"status": "opened",
"order_index": 0,
"opened_at": "2026-10-13T06:48:02+00:00",
"signed_at": null,
"declined_at": null,
"decline_reason": null,
"view_count": 2,
"total_time_ms": 412000,
"last_activity_at": "2026-10-13T06:51:19+00:00",
"reminder_count": 0,
"last_reminded_at": null,
"phone_number": null,
"bankid_required": false,
"personal_number_masked": null
},
{
"id": "01a15a31-0c2e-71aa-9c3d-6b1e8f2a4d59",
"name": "Maria Svensson",
"email": "maria.svensson@byggbolaget.se",
"role": "cc",
"status": "pending",
"order_index": 1,
"opened_at": null,
"signed_at": null,
"declined_at": null,
"decline_reason": null,
"view_count": 0,
"total_time_ms": 0,
"last_activity_at": null,
"reminder_count": 0,
"last_reminded_at": null,
"phone_number": null,
"bankid_required": false,
"personal_number_masked": null
}
],
"active_send": {
"id": "01a15a31-0c1f-7b90-a2e4-3d7c9b5f8e16",
"status": "sent",
"signing_order": "parallel",
"sent_at": "2026-10-12T08:14:03+00:00",
"expires_at": "2026-11-11T22:59:59+00:00"
},
"tags": [
{
"id": "01a14f88-2b1a-7c3e-8d4f-9e0a1b2c3d4e",
"slug": "ramavtal",
"label": "Ramavtal",
"color": "brand"
}
],
"engagement": {
"views": 2,
"unique_readers": 1,
"unique_sessions": 2,
"total_time_ms": 412000,
"first_opened_at": "2026-10-13T06:48:02+00:00",
"last_activity_at": "2026-10-13T06:51:19+00:00",
"event_counts": {
"document.opened": 2,
"page.viewed": 9,
"time.spent": 9
}
}
}
}Path parameters
documentstring(uuid)requiredThe document's id.
Example
01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47
Responses
Headers
X-RateLimit-Limit— Requests this key may make per minute.X-RateLimit-Remaining— Requests left in the current minute.
dataobjectrequiredA file in Closepact, and where it stands with its recipients.
idstring(uuid)requiredtitlestringrequiredtypestringrequiredDerived from the detected MIME type on upload.
One of
pdfdocximagevideofilemime_typestringnullablerequiredsize_bytesintegerrequiredpage_countintegernullablerequiredKnown once a PDF has been processed.
page_images_statusstringnullablerequiredPage images back the signing view and per-page statistics.
unavailablefor anything that is not a PDF.One of
pendingreadyunavailablestatusstringrequiredThe file:
processingafter upload, thenready, orfailedwhen the virus scan rejects it or processing gives up.quarantinedis reserved.One of
processingreadyfailedquarantinedlifecycle_statusstringrequiredThe document with its recipients.
draft— never sent.sent— sent to recipients.opened— a recipient has opened it.signed— every signer and approver has signed (thesignature.completedwebhook fires).expired—expires_atpassed before it was signed.canceled— its signature request was canceled.declinedis reserved: a decline is recorded on the recipient and onactive_send.status.One of
draftsentopenedsigneddeclinedexpiredcanceledvalue_centsintegernullablerequiredWhat the document is worth, in the currency's minor unit (öre for SEK).
currencystringrequiredISO 4217 code.
Example
"SEK"sent_atstring(date-time)nullablerequiredWhen it was first sent.
expires_atstring(date-time)nullablerequireddays_leftintegernullablerequiredWhole days until
expires_at; negative once overdue; null without a deadline.checksum_sha256stringnullablerequiredSHA-256 of the uploaded file, hex.
signer_otp_requiredbooleanrequiredSigners confirm who they are with a six-digit code (valid 10 minutes, 5 tries) before they can sign or decline; the code being sent and entered are recorded in the audit trail. Sent by email, or by SMS when
signer_verification_channelissms. On by default for new documents; change it withPATCH /documents/{document}.signer_verification_channelstringrequiredWhere the one-time code goes.
smssends it to the recipient'sphone_number; a recipient without one, or a server without an SMS provider, gets it by email instead, and the signing page says so. Each SMS is billed as metered usage (0.89 kr).One of
emailsmsbankid_sign_requiredbooleanrequiredSigners and approvers sign with Swedish BankID instead of a typed or drawn signature. The BankID app shows the document's title and SHA-256; BankID's signature and OCSP response are kept as evidence and their hashes recorded in the audit trail. Each completed BankID signature is billed as metered usage (4.49 kr). Needs a tier with BankID (402 otherwise) and BankID configured on the server (503
bankid_unavailableotherwise).bankid_identification_requiredbooleanrequiredEvery recipient identifies with BankID before the document is shown to them at all. Same requirements as
bankid_sign_required.metadataobject | array of valuesnullablerequiredFacts recorded while processing the file. Sent as an empty array
[]when there are none.created_atstring(date-time)requiredupdated_atstring(date-time)requiredownerobjectnullableWho uploaded it (for an upload through the API, the member the key acts as); null if that account no longer exists. Detail responses only.
recipientsarray of objectsEveryone it has been sent to, in signing order. Detail and send responses only.
active_sendobjectnullableThe signature request currently in play, or null if it was never sent. Detail and send responses only. Full detail:
GET /signatures/{id}.tagsarray of objectsDetail responses only.
engagementobjectHow much a document has been read. Detail responses only.
- 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.Errors
- 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs:Errors
featurenames the capability (the operation'sx-feature) andrequired_tierthe cheapest tier that grants it. Every operation answers this with"feature": "core.api"when no module the organization holds is on a tier that includes the API. - 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (Errors
module_seat_required— an administrator assigns seats under Settings → Users), or the organization is suspended. - 404No such record in this organization.Errors
- 429The key has made more requests this minute than its limit allows.Errors
Update a document
/documents/{document}write Permission documents.updateChanges the document's details. Send only the fields to change.
expires_at is the document's own deadline: once it passes, an hourly sweep sets lifecycle_status to expired and expires its open signature request, after which the signing links stop working. Changing it here does not change the deadline of a signature request that was already sent — expires_at on POST /documents/{document}/send sets both.
tag_ids replaces the document's tags; ids that are not tags of this organization are ignored.
curl -X PATCH 'https://api.closepact.com/v1/api/documents/01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47' \
-H "Authorization: Bearer $CLOSEPACT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Serviceavtal 2027 – Byggbolaget i Mälardalen AB (rev. 2)",
"value_cents": 18500000,
"currency": "SEK",
"expires_at": "2026-11-30T22:59:59+00:00",
"tag_ids": [
"01a14f88-2b1a-7c3e-8d4f-9e0a1b2c3d4e"
]
}'{
"data": {
"id": "01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40",
"title": "Quarterly window cleaning",
"type": "pdf",
"mime_type": "string",
"size_bytes": 1,
"page_count": 1,
"page_images_status": "pending",
"status": "processing",
"lifecycle_status": "draft",
"value_cents": 125000,
"currency": "SEK",
"sent_at": "2026-10-18T09:30:00Z",
"expires_at": "2026-10-18T09:30:00Z",
"days_left": 1,
"checksum_sha256": "string",
"signer_otp_required": true,
"signer_verification_channel": "email",
"bankid_sign_required": true,
"bankid_identification_required": true,
"metadata": {
"original_name": "string",
"has_text": true,
"text_length": 1,
"processed_at": "2026-10-18T09:30:00Z",
"infected": true
},
"created_at": "2026-10-18T09:30:00Z",
"updated_at": "2026-10-18T09:30:00Z",
"owner": {
"id": "01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40",
"name": "Anna Lindqvist"
},
"recipients": [
{
"id": "01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40",
"name": "Anna Lindqvist",
"email": "anna@nordiskfastighet.se",
"role": "signer",
"status": "pending",
"order_index": 1,
"opened_at": "2026-10-18T09:30:00Z",
"signed_at": "2026-10-18T09:30:00Z",
"declined_at": "2026-10-18T09:30:00Z",
"decline_reason": "string",
"view_count": 1,
"total_time_ms": 1,
"last_activity_at": "2026-10-18T09:30:00Z",
"reminder_count": 1,
"last_reminded_at": "2026-10-18T09:30:00Z",
"phone_number": "+46701234567",
"bankid_required": true,
"personal_number_masked": "••••••••-1234"
}
],
"active_send": {
"id": "01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40",
"status": "draft",
"signing_order": "parallel",
"sent_at": "2026-10-18T09:30:00Z",
"expires_at": "2026-10-18T09:30:00Z"
},
"tags": [
{
"id": "01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40",
"slug": "string",
"label": "string",
"color": "#4a3aff"
}
],
"engagement": {
"views": 1,
"unique_readers": 1,
"unique_sessions": 1,
"total_time_ms": 1,
"first_opened_at": "2026-10-18T09:30:00Z",
"last_activity_at": "2026-10-18T09:30:00Z",
"event_counts": {
"key": 1
}
}
}
}Path parameters
documentstring(uuid)requiredThe document's id.
Example
01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47
Request body
application/json · required
titlestring1–255 characters
value_centsintegernullableWhat the document is worth, in the currency's minor unit (öre for SEK).
0–99,999,999,999,999
currencystringISO 4217 code; stored upper-case.
pattern ^[A-Za-z]{3}$
expires_atstring(date-time)nullablesigner_otp_requiredbooleanWhether signers must enter a one-time code before they can sign or decline. Applies to recipients who have not signed yet.
signer_verification_channelstringWhere the code goes.
smsneeds aphone_numberon the recipient and an SMS provider on the server; without either the code is emailed and the signing page says why.One of
emailsmsbankid_sign_requiredbooleanSigners sign with BankID. Switching it on answers
402on a tier without BankID and503(bankid_unavailable) when BankID is not configured on the server.bankid_identification_requiredbooleanRecipients identify with BankID before the document is shown to them. Same
402/503asbankid_sign_required.tag_idsarray of strings(uuid)The complete new set of tags.
[]removes them all.at most 20 items
Responses
Headers
X-RateLimit-Limit— Requests this key may make per minute.X-RateLimit-Remaining— Requests left in the current minute.
dataobjectrequiredA file in Closepact, and where it stands with its recipients.
idstring(uuid)requiredtitlestringrequiredtypestringrequiredDerived from the detected MIME type on upload.
One of
pdfdocximagevideofilemime_typestringnullablerequiredsize_bytesintegerrequiredpage_countintegernullablerequiredKnown once a PDF has been processed.
page_images_statusstringnullablerequiredPage images back the signing view and per-page statistics.
unavailablefor anything that is not a PDF.One of
pendingreadyunavailablestatusstringrequiredThe file:
processingafter upload, thenready, orfailedwhen the virus scan rejects it or processing gives up.quarantinedis reserved.One of
processingreadyfailedquarantinedlifecycle_statusstringrequiredThe document with its recipients.
draft— never sent.sent— sent to recipients.opened— a recipient has opened it.signed— every signer and approver has signed (thesignature.completedwebhook fires).expired—expires_atpassed before it was signed.canceled— its signature request was canceled.declinedis reserved: a decline is recorded on the recipient and onactive_send.status.One of
draftsentopenedsigneddeclinedexpiredcanceledvalue_centsintegernullablerequiredWhat the document is worth, in the currency's minor unit (öre for SEK).
currencystringrequiredISO 4217 code.
Example
"SEK"sent_atstring(date-time)nullablerequiredWhen it was first sent.
expires_atstring(date-time)nullablerequireddays_leftintegernullablerequiredWhole days until
expires_at; negative once overdue; null without a deadline.checksum_sha256stringnullablerequiredSHA-256 of the uploaded file, hex.
signer_otp_requiredbooleanrequiredSigners confirm who they are with a six-digit code (valid 10 minutes, 5 tries) before they can sign or decline; the code being sent and entered are recorded in the audit trail. Sent by email, or by SMS when
signer_verification_channelissms. On by default for new documents; change it withPATCH /documents/{document}.signer_verification_channelstringrequiredWhere the one-time code goes.
smssends it to the recipient'sphone_number; a recipient without one, or a server without an SMS provider, gets it by email instead, and the signing page says so. Each SMS is billed as metered usage (0.89 kr).One of
emailsmsbankid_sign_requiredbooleanrequiredSigners and approvers sign with Swedish BankID instead of a typed or drawn signature. The BankID app shows the document's title and SHA-256; BankID's signature and OCSP response are kept as evidence and their hashes recorded in the audit trail. Each completed BankID signature is billed as metered usage (4.49 kr). Needs a tier with BankID (402 otherwise) and BankID configured on the server (503
bankid_unavailableotherwise).bankid_identification_requiredbooleanrequiredEvery recipient identifies with BankID before the document is shown to them at all. Same requirements as
bankid_sign_required.metadataobject | array of valuesnullablerequiredFacts recorded while processing the file. Sent as an empty array
[]when there are none.created_atstring(date-time)requiredupdated_atstring(date-time)requiredownerobjectnullableWho uploaded it (for an upload through the API, the member the key acts as); null if that account no longer exists. Detail responses only.
recipientsarray of objectsEveryone it has been sent to, in signing order. Detail and send responses only.
active_sendobjectnullableThe signature request currently in play, or null if it was never sent. Detail and send responses only. Full detail:
GET /signatures/{id}.tagsarray of objectsDetail responses only.
engagementobjectHow much a document has been read. Detail responses only.
- 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.Errors
- 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs:Errors
featurenames the capability (the operation'sx-feature) andrequired_tierthe cheapest tier that grants it. Every operation answers this with"feature": "core.api"when no module the organization holds is on a tier that includes the API. - 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (Errors
module_seat_required— an administrator assigns seats under Settings → Users), or the organization is suspended. - 404No such record in this organization.Errors
- 422The request body or query failed validation.Errors
- 429The key has made more requests this minute than its limit allows.Errors
- 503BankID was asked for — on the document, or for a recipient — and this server has no working BankID relying-party certificate. Nothing was changed or sent.Errors
bankid_unavailableis the onlyerrorhere; it is not something a retry fixes.
Delete a document
/documents/{document}write Permission documents.deleteDeletes the document: it leaves the organization's lists and its id answers 404 from then on. Its signature requests and their audit trail are kept. To withdraw a document that is out for signing, cancel its signature request first (POST /signatures/{signature}/cancel, with the id from active_send) so the recipients' links stop working. Deleting cannot be undone through the API.
curl -X DELETE 'https://api.closepact.com/v1/api/documents/01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47' \
-H "Authorization: Bearer $CLOSEPACT_API_KEY"{
"deleted": true
}Path parameters
documentstring(uuid)requiredThe document's id.
Example
01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47
Responses
Headers
X-RateLimit-Limit— Requests this key may make per minute.X-RateLimit-Remaining— Requests left in the current minute.
deletedbooleanrequiredOne of
true
- 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.Errors
- 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs:Errors
featurenames the capability (the operation'sx-feature) andrequired_tierthe cheapest tier that grants it. Every operation answers this with"feature": "core.api"when no module the organization holds is on a tier that includes the API. - 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (Errors
module_seat_required— an administrator assigns seats under Settings → Users), or the organization is suspended. - 404No such record in this organization.Errors
- 429The key has made more requests this minute than its limit allows.Errors
Get a download link
/documents/{document}/download-linkread Permission documents.read.anyReturns a signed URL that downloads the document's file. The file is not streamed through the API itself.
- The URL works for 15 minutes (
expires_in_seconds). Ask for a new one each time you download rather than storing it. - Fetch it with a plain
GETand without theAuthorizationheader: the signature in the query string is the credential, so treat the URL like a password — do not log it or put it in front of anyone who should not have the file. - The response is the file itself, with its
Content-Typeand the document's title as the file name. An expired or altered URL answers403.
What you download is the file as uploaded. Closepact does not rewrite the PDF when it is signed: the signatures, with time, IP address and signature type, are recorded in the signature request's tamper-evident audit trail — GET /signatures/{signature} → events, each event hash-chained to the one before. Keep that alongside the file as the evidence of signing.
The member the key acts as needs documents.read.any.
curl 'https://api.closepact.com/v1/api/documents/01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47/download-link' \
-H "Authorization: Bearer $CLOSEPACT_API_KEY"{
"data": {
"url": "https://api.closepact.com/v1/documents/01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47/download?expires=1792310400&signature=3b9f6c1e0d2a7f4b8c5e9d1a6f3b0c7e2d8a5f1b9c4e7d0a3f6b2c8e5d1a9f4b",
"expires_in_seconds": 900
}
}Path parameters
documentstring(uuid)requiredThe document's id.
Example
01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47
Responses
Headers
X-RateLimit-Limit— Requests this key may make per minute.X-RateLimit-Remaining— Requests left in the current minute.
dataobjectrequiredurlstring(uri)requiredSigned URL to
GETwithout anAuthorizationheader. Works forexpires_in_seconds.expires_in_secondsintegerrequiredHow long the URL works: 900 (15 minutes).
- 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.Errors
- 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs:Errors
featurenames the capability (the operation'sx-feature) andrequired_tierthe cheapest tier that grants it. Every operation answers this with"feature": "core.api"when no module the organization holds is on a tier that includes the API. - 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (Errors
module_seat_required— an administrator assigns seats under Settings → Users), or the organization is suspended. - 404No such record in this organization.Errors
- 429The key has made more requests this minute than its limit allows.Errors
Get a document's history
/documents/{document}/timelineEverything that happened to the document, newest first: what recipients did (source: engagement — document.opened, page.viewed, time.spent, document.download) merged with the signature audit trail (source: signature — signature.requested, recipient.added, recipient.removed, signer.opened, signer.code_sent, signer.verified, signer.signed, signer.declined, signature.refused, reminder.sent, signature.completed, signature.sealed, signature.canceled). At most the 200 most recent events.
by_recipient summarises reading per recipient.
integrity is the audit chain of every signature request the document was sent in, recomputed on this request: ok when every chain verifies, broken when any does (see each request's broken_at), empty when the document was never sent. The chain itself is on GET /signatures/{signature}.
curl 'https://api.closepact.com/v1/api/documents/01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47/timeline' \
-H "Authorization: Bearer $CLOSEPACT_API_KEY"{
"data": {
"events": [
{
"id": "01a15b07-6e21-7c4a-9b3d-0f8e2a5c1d76",
"source": "engagement",
"event_type": "document.opened",
"actor": {
"type": "recipient",
"id": "01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03",
"name": "Erik Johansson"
},
"page_number": null,
"value": [],
"occurred_at": "2026-10-13T06:48:02+00:00"
},
{
"id": "01a15a31-0c30-7d18-8e2f-4a6c9b1d3e57",
"source": "signature",
"event_type": "signature.requested",
"actor": {
"type": "user",
"id": "01a14eb3-c634-7238-bc76-18b09b287f98",
"name": "Anna Lindqvist"
},
"page_number": null,
"value": {
"document_id": "01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47",
"signer_count": 2
},
"occurred_at": "2026-10-12T08:14:03+00:00"
}
],
"by_recipient": [
{
"signer_id": "01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03",
"name": "Erik Johansson",
"event_count": 20,
"first_seen": "2026-10-13T06:48:02+00:00",
"last_seen": "2026-10-13T06:51:19+00:00"
}
],
"integrity": {
"status": "ok",
"checked_at": "2026-10-13T07:02:11+00:00",
"requests": [
{
"signature_request_id": "01a15a31-0c1f-7b90-a2e4-3d7c9b5f8e16",
"status": "ok",
"format": "current",
"event_count": 2,
"head": "1d0e7c4b9a2f5e8d3c6b1a0f9e4d7c2b5a8f3e6d1c4b7a0e9f2d5c8b1a4e7f3d",
"broken_at": null
}
]
}
}
}Path parameters
documentstring(uuid)requiredThe document's id.
Example
01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47
Responses
Headers
X-RateLimit-Limit— Requests this key may make per minute.X-RateLimit-Remaining— Requests left in the current minute.
dataobjectrequiredeventsarray of objectsrequiredNewest first; at most 200.
by_recipientarray of objectsrequiredintegrityobjectrequiredWhether the signature audit chains behind this document still verify.
- 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.Errors
- 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs:Errors
featurenames the capability (the operation'sx-feature) andrequired_tierthe cheapest tier that grants it. Every operation answers this with"feature": "core.api"when no module the organization holds is on a tier that includes the API. - 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (Errors
module_seat_required— an administrator assigns seats under Settings → Users), or the organization is suspended. - 404No such record in this organization.Errors
- 429The key has made more requests this minute than its limit allows.Errors
Get reading statistics
/documents/{document}/engagementHow the document has been read: a summary, time and views per page (from page 1 to the last page, gaps included) and the same per recipient. Page statistics need page images, so they are only filled in for PDFs.
curl 'https://api.closepact.com/v1/api/documents/01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47/engagement' \
-H "Authorization: Bearer $CLOSEPACT_API_KEY"{
"data": {
"summary": {
"views": 2,
"unique_readers": 1,
"unique_sessions": 2,
"total_time_ms": 412000,
"first_opened_at": "2026-10-13T06:48:02+00:00",
"last_activity_at": "2026-10-13T06:51:19+00:00",
"event_counts": {
"document.opened": 2,
"page.viewed": 9,
"time.spent": 9
}
},
"pages": [
{
"page_number": 1,
"duration_ms": 38000,
"views": 2
},
{
"page_number": 2,
"duration_ms": 0,
"views": 0
},
{
"page_number": 3,
"duration_ms": 214000,
"views": 3
}
],
"recipients": [
{
"recipient_id": "01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03",
"name": "Erik Johansson",
"email": "erik.johansson@byggbolaget.se",
"role": "signer",
"status": "opened",
"views": 2,
"total_time_ms": 412000,
"last_activity_at": "2026-10-13T06:51:19+00:00"
}
],
"event_counts": {
"document.opened": 2,
"page.viewed": 9,
"time.spent": 9
}
}
}Path parameters
documentstring(uuid)requiredThe document's id.
Example
01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47
Responses
Headers
X-RateLimit-Limit— Requests this key may make per minute.X-RateLimit-Remaining— Requests left in the current minute.
dataobjectrequiredsummaryobjectrequiredHow much a document has been read. Detail responses only.
pagesarray of objectsrequiredOne entry per page, from page 1. Time per page is included from the Väx tier of proposals (
tracking.pages); without it this is an empty array.recipientsarray of objectsrequiredevent_countsobject | array of valuesrequiredEvent type → count. Sent as an empty array
[]when there are none.
- 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.Errors
- 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs:Errors
featurenames the capability (the operation'sx-feature) andrequired_tierthe cheapest tier that grants it. Every operation answers this with"feature": "core.api"when no module the organization holds is on a tier that includes the API. - 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (Errors
module_seat_required— an administrator assigns seats under Settings → Users), or the organization is suspended. - 404No such record in this organization.Errors
- 429The key has made more requests this minute than its limit allows.Errors
List recipients
/documents/{document}/recipientsEveryone the document has been sent to, in signing order — including recipients of earlier signature requests that a later send replaced. Not paginated.
curl 'https://api.closepact.com/v1/api/documents/01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47/recipients' \
-H "Authorization: Bearer $CLOSEPACT_API_KEY"{
"data": [
{
"id": "01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03",
"name": "Erik Johansson",
"email": "erik.johansson@byggbolaget.se",
"role": "signer",
"status": "signed",
"order_index": 0,
"opened_at": "2026-10-13T06:48:02+00:00",
"signed_at": "2026-10-13T06:52:44+00:00",
"declined_at": null,
"decline_reason": null,
"view_count": 2,
"total_time_ms": 412000,
"last_activity_at": "2026-10-13T06:52:44+00:00",
"reminder_count": 1,
"last_reminded_at": "2026-10-12T15:00:00+00:00",
"phone_number": null,
"bankid_required": false,
"personal_number_masked": null
}
]
}Path parameters
documentstring(uuid)requiredThe document's id.
Example
01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47
Responses
Headers
X-RateLimit-Limit— Requests this key may make per minute.X-RateLimit-Remaining— Requests left in the current minute.
dataarray of objectsrequiredidstring(uuid)requirednamestringrequiredemailstring(email)requiredrolestringrequiredsignerandapprovermust sign;ccandvieweronly read.One of
signerapproverccviewerstatusstringrequiredpendinguntil they open their link, thenopened, thensignedordeclined.One of
pendingopenedsigneddeclinedorder_indexintegerrequiredPosition in a sequential signing order, from 0.
opened_atstring(date-time)nullablerequiredsigned_atstring(date-time)nullablerequireddeclined_atstring(date-time)nullablerequireddecline_reasonstringnullablerequiredview_countintegerrequiredtotal_time_msintegerrequiredTime spent reading, in milliseconds.
last_activity_atstring(date-time)nullablerequiredreminder_countintegerrequiredlast_reminded_atstring(date-time)nullablerequiredphone_numberstringnullablerequiredE.164, for the one-time code by SMS.
Example
"+46701234567"bankid_requiredbooleanrequiredThis recipient signs with BankID even if the document does not ask it of everybody.
personal_number_maskedstringnullablerequiredThe last four digits of the personal identity number BankID has to report for this recipient; any other person's BankID is refused. The full number is never returned.
Example
"••••••••-1234"
- 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.Errors
- 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs:Errors
featurenames the capability (the operation'sx-feature) andrequired_tierthe cheapest tier that grants it. Every operation answers this with"feature": "core.api"when no module the organization holds is on a tier that includes the API. - 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (Errors
module_seat_required— an administrator assigns seats under Settings → Users), or the organization is suspended. - 404No such record in this organization.Errors
- 429The key has made more requests this minute than its limit allows.Errors
Add a recipient
/documents/{document}/recipientswrite Permission documents.updateAdds one recipient.
On a document that has not been sent, this sends it. The first recipient creates the signature request (the signature.requested webhook fires), gets an email with their link straight away (whatever send_invite says), and the document moves to sent. To send to several people at once, use POST /documents/{document}/send instead.
On a document that has been sent, the recipient joins the current signature request — by default after the last recipient (order_index) — and is emailed their link unless send_invite is false.
role: signer and approver must sign before the request completes; cc and viewer get a read-only link and never hold it open.
Set include_link to get the recipient's private link back, for example to deliver it through your own channel. Treat it like a password: it opens the document as that person, without logging in.
curl -X POST 'https://api.closepact.com/v1/api/documents/01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47/recipients' \
-H "Authorization: Bearer $CLOSEPACT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "maria.svensson@byggbolaget.se",
"name": "Maria Svensson",
"role": "approver",
"message": "Hej Maria! Här är avtalet för godkännande.",
"include_link": true
}'{
"data": {
"id": "01a15a31-0c2e-71aa-9c3d-6b1e8f2a4d59",
"name": "Maria Svensson",
"email": "maria.svensson@byggbolaget.se",
"role": "approver",
"status": "pending",
"order_index": 1,
"opened_at": null,
"signed_at": null,
"declined_at": null,
"decline_reason": null,
"view_count": 0,
"total_time_ms": 0,
"last_activity_at": null,
"reminder_count": 0,
"last_reminded_at": null,
"phone_number": null,
"bankid_required": false,
"personal_number_masked": null
},
"link": {
"url": "https://app.closepact.com/sign/Xk3pQ9vB2mR7tY4wN8cL1zF6hJ0sD5gA2eK9uP3oW7iV4bM1"
}
}Path parameters
documentstring(uuid)requiredThe document's id.
Example
01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47
Request body
application/json · required
emailstring(email)requiredat most 255 characters
namestringrequiredat most 255 characters
rolestringOne of
signerapproverccviewerDefault
"signer"order_indexintegerPosition in a sequential signing order. Defaults to after the last recipient. Ignored for the first recipient.
0–100
messagestringnullableA personal note in the invitation email.
at most 2,000 characters
send_invitebooleanEmail the recipient their link. Only applies once the document has been sent; the first recipient is always emailed.
Default
truephone_numberstringnullableFor the one-time code by SMS. International form (
+46 70 123 45 67) or a Swedish number starting with 0; stored as E.164.at most 32 characters
bankid_requiredbooleanThis recipient signs with BankID even when the document does not ask it of everybody.
Default
falsepersonal_numberstringnullableThe Swedish personal identity number (personnummer, 10 or 12 digits) BankID has to report for this recipient; anybody else's BankID is refused. Implies
bankid_required. Stored encrypted and never returned; responses showpersonal_number_masked.Example
"19811218-9876"include_linkbooleanReturn the recipient's private link in
link. May also be sent as a query parameter.Default
false
Responses
Headers
X-RateLimit-Limit— Requests this key may make per minute.X-RateLimit-Remaining— Requests left in the current minute.
dataobjectrequiredSomebody a document was sent to.
idstring(uuid)requirednamestringrequiredemailstring(email)requiredrolestringrequiredsignerandapprovermust sign;ccandvieweronly read.One of
signerapproverccviewerstatusstringrequiredpendinguntil they open their link, thenopened, thensignedordeclined.One of
pendingopenedsigneddeclinedorder_indexintegerrequiredPosition in a sequential signing order, from 0.
opened_atstring(date-time)nullablerequiredsigned_atstring(date-time)nullablerequireddeclined_atstring(date-time)nullablerequireddecline_reasonstringnullablerequiredview_countintegerrequiredtotal_time_msintegerrequiredTime spent reading, in milliseconds.
last_activity_atstring(date-time)nullablerequiredreminder_countintegerrequiredlast_reminded_atstring(date-time)nullablerequiredphone_numberstringnullablerequiredE.164, for the one-time code by SMS.
Example
"+46701234567"bankid_requiredbooleanrequiredThis recipient signs with BankID even if the document does not ask it of everybody.
personal_number_maskedstringnullablerequiredThe last four digits of the personal identity number BankID has to report for this recipient; any other person's BankID is refused. The full number is never returned.
Example
"••••••••-1234"
linkobjectnullablerequiredOnly when
include_linkwas set; otherwise null.
- 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.Errors
- 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs:Errors
featurenames the capability (the operation'sx-feature) andrequired_tierthe cheapest tier that grants it. Every operation answers this with"feature": "core.api"when no module the organization holds is on a tier that includes the API. - 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (Errors
module_seat_required— an administrator assigns seats under Settings → Users), or the organization is suspended. - 404No such record in this organization.Errors
- 422The request body or query failed validation.Errors
- 429The key has made more requests this minute than its limit allows.Errors
- 503BankID was asked for — on the document, or for a recipient — and this server has no working BankID relying-party certificate. Nothing was changed or sent.Errors
bankid_unavailableis the onlyerrorhere; it is not something a retry fixes.
Remove a recipient
/documents/{document}/recipients/{recipient}write Permission documents.updateTakes a recipient off the document. Their link stops working at once; nobody is emailed. The removal is recorded in the audit trail (recipient.removed).
A recipient who has already signed cannot be removed — their signature is part of the audit trail — and the answer is 409.
curl -X DELETE 'https://api.closepact.com/v1/api/documents/01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47/recipients/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40' \
-H "Authorization: Bearer $CLOSEPACT_API_KEY"204 — no response bodyPath parameters
documentstring(uuid)requiredThe document's id.
Example
01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47recipientstring(uuid)requiredThe recipient's id, from
recipientson the document.
Responses
Headers
X-RateLimit-Limit— Requests this key may make per minute.X-RateLimit-Remaining— Requests left in the current minute.
- 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.Errors
- 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs:Errors
featurenames the capability (the operation'sx-feature) andrequired_tierthe cheapest tier that grants it. Every operation answers this with"feature": "core.api"when no module the organization holds is on a tier that includes the API. - 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (Errors
module_seat_required— an administrator assigns seats under Settings → Users), or the organization is suspended. - 404No such record in this organization.Errors
- 409The document is not in a state that allows this.Errors
errorsays which:document_not_sent— it has not been sent yet;recipient_not_on_document— the recipient is not on its current signature request;recipient_already_signed— the recipient has already signed and cannot be removed.messagesays the same for a person. - 429The key has made more requests this minute than its limit allows.Errors
Send for signing
/documents/{document}/sendwrite Permission documents.sendSends the document to its recipients — the main call for e-signing.
Creates a signature request, emails every recipient their own private link, records signature.requested in the audit trail, fires the signature.requested webhook and moves the document to sent. Recipients sign in the browser, with a typed or drawn signature; no Closepact account is needed. When signer_otp_required is on (the default for new documents) they first enter a one-time code, by email or — with signer_verification_channel sms — by SMS. With bankid_sign_required (or bankid_required on a recipient) they sign with BankID instead, and with bankid_identification_required they identify with BankID before the document is shown. The SHA-256 of the file is taken now and recorded; if the file changes afterwards, signing is refused.
Sending again replaces the previous request. Its links stop working and it is canceled (Superseded by a new send., with a signature.canceled webhook), so the recipients you send now are the whole list. The document's lifecycle_status starts over at sent — whether the last round ended opened, expired or canceled — and sent_at keeps the first send. A request that has already been completed is never voided, and a signed document stays signed.
Signing order. parallel (default): everyone may sign at once. sequential: recipients sign in the order listed; a recipient cannot sign before everyone earlier in the list who must sign has done so.
What happens next. When a recipient opens their link the document.viewed webhook fires and lifecycle_status moves to opened. Each signature fires signer.signed. When every signer and approver has signed, the request is completed, the document signed, and the signature.completed webhook fires with the signature request's id and the document_id; a completion certificate is issued and every recipient and the sender are emailed links to the signed document and the certificate. A recipient who declines must give a reason; the request becomes declined, the document declined, and the sender is emailed. If expires_at passes first, the request and the document become expired. Unsigned recipients can be nudged with POST /documents/{document}/remind.
Set include_links to get each recipient's private link back.
curl -X POST 'https://api.closepact.com/v1/api/documents/01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47/send' \
-H "Authorization: Bearer $CLOSEPACT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipients": [
{
"email": "erik.johansson@byggbolaget.se",
"name": "Erik Johansson",
"role": "signer"
},
{
"email": "maria.svensson@byggbolaget.se",
"name": "Maria Svensson",
"role": "cc"
}
],
"message": "Hej Erik! Här kommer serviceavtalet för 2027 enligt vårt samtal. Hör av dig om något är oklart.",
"signing_order": "parallel",
"expires_at": "2026-11-11T22:59:59+00:00"
}'{
"data": {
"id": "01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47",
"title": "Serviceavtal 2027 – Byggbolaget i Mälardalen AB",
"type": "pdf",
"mime_type": "application/pdf",
"size_bytes": 284516,
"page_count": 6,
"page_images_status": "ready",
"status": "ready",
"lifecycle_status": "sent",
"value_cents": 18500000,
"currency": "SEK",
"sent_at": "2026-10-12T08:14:03+00:00",
"expires_at": "2026-11-11T22:59:59+00:00",
"days_left": 30,
"checksum_sha256": "9f2c4b7e1d0a8c3f5e6b2d9a7c4e1f0b3d8a6c2e5f9b1d7a4c0e3f6b8d2a5c9e",
"signer_otp_required": true,
"signer_verification_channel": "email",
"bankid_sign_required": false,
"bankid_identification_required": false,
"metadata": {
"original_name": "Serviceavtal_2027_Byggbolaget.pdf",
"has_text": true,
"text_length": 18234,
"processed_at": "2026-10-12T08:12:41+00:00"
},
"created_at": "2026-10-12T08:12:37+00:00",
"updated_at": "2026-10-12T08:14:03+00:00",
"recipients": [
{
"id": "01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03",
"name": "Erik Johansson",
"email": "erik.johansson@byggbolaget.se",
"role": "signer",
"status": "pending",
"order_index": 0,
"opened_at": null,
"signed_at": null,
"declined_at": null,
"decline_reason": null,
"view_count": 0,
"total_time_ms": 0,
"last_activity_at": null,
"reminder_count": 0,
"last_reminded_at": null,
"phone_number": null,
"bankid_required": false,
"personal_number_masked": null
}
],
"active_send": {
"id": "01a15a31-0c1f-7b90-a2e4-3d7c9b5f8e16",
"status": "sent",
"signing_order": "parallel",
"sent_at": "2026-10-12T08:14:03+00:00",
"expires_at": "2026-11-11T22:59:59+00:00"
}
},
"links": null
}Path parameters
documentstring(uuid)requiredThe document's id.
Example
01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47
Request body
application/json · required
recipientsarray of objectsrequired1–20 items
titlestringSubject of the signature request. Defaults to the document's title.
at most 255 characters
messagestringnullableA personal note in every recipient's email.
at most 5,000 characters
signing_orderstringOne of
parallelsequentialDefault
"parallel"expires_atstring(date-time)nullableSigning deadline; must be in the future. Also becomes the document's
expires_at. Defaults to the document'sexpires_at.deal_room_idstring(uuid)nullableLink the signature request to a deal room of this organization. Any other id, including another organization's room, fails validation (
422).include_linksbooleanReturn each recipient's private link in
links. May also be sent as a query parameter.Default
false
Responses
Headers
X-RateLimit-Limit— Requests this key may make per minute.X-RateLimit-Remaining— Requests left in the current minute.
dataobjectrequiredA file in Closepact, and where it stands with its recipients.
idstring(uuid)requiredtitlestringrequiredtypestringrequiredDerived from the detected MIME type on upload.
One of
pdfdocximagevideofilemime_typestringnullablerequiredsize_bytesintegerrequiredpage_countintegernullablerequiredKnown once a PDF has been processed.
page_images_statusstringnullablerequiredPage images back the signing view and per-page statistics.
unavailablefor anything that is not a PDF.One of
pendingreadyunavailablestatusstringrequiredThe file:
processingafter upload, thenready, orfailedwhen the virus scan rejects it or processing gives up.quarantinedis reserved.One of
processingreadyfailedquarantinedlifecycle_statusstringrequiredThe document with its recipients.
draft— never sent.sent— sent to recipients.opened— a recipient has opened it.signed— every signer and approver has signed (thesignature.completedwebhook fires).expired—expires_atpassed before it was signed.canceled— its signature request was canceled.declinedis reserved: a decline is recorded on the recipient and onactive_send.status.One of
draftsentopenedsigneddeclinedexpiredcanceledvalue_centsintegernullablerequiredWhat the document is worth, in the currency's minor unit (öre for SEK).
currencystringrequiredISO 4217 code.
Example
"SEK"sent_atstring(date-time)nullablerequiredWhen it was first sent.
expires_atstring(date-time)nullablerequireddays_leftintegernullablerequiredWhole days until
expires_at; negative once overdue; null without a deadline.checksum_sha256stringnullablerequiredSHA-256 of the uploaded file, hex.
signer_otp_requiredbooleanrequiredSigners confirm who they are with a six-digit code (valid 10 minutes, 5 tries) before they can sign or decline; the code being sent and entered are recorded in the audit trail. Sent by email, or by SMS when
signer_verification_channelissms. On by default for new documents; change it withPATCH /documents/{document}.signer_verification_channelstringrequiredWhere the one-time code goes.
smssends it to the recipient'sphone_number; a recipient without one, or a server without an SMS provider, gets it by email instead, and the signing page says so. Each SMS is billed as metered usage (0.89 kr).One of
emailsmsbankid_sign_requiredbooleanrequiredSigners and approvers sign with Swedish BankID instead of a typed or drawn signature. The BankID app shows the document's title and SHA-256; BankID's signature and OCSP response are kept as evidence and their hashes recorded in the audit trail. Each completed BankID signature is billed as metered usage (4.49 kr). Needs a tier with BankID (402 otherwise) and BankID configured on the server (503
bankid_unavailableotherwise).bankid_identification_requiredbooleanrequiredEvery recipient identifies with BankID before the document is shown to them at all. Same requirements as
bankid_sign_required.metadataobject | array of valuesnullablerequiredFacts recorded while processing the file. Sent as an empty array
[]when there are none.created_atstring(date-time)requiredupdated_atstring(date-time)requiredownerobjectnullableWho uploaded it (for an upload through the API, the member the key acts as); null if that account no longer exists. Detail responses only.
recipientsarray of objectsEveryone it has been sent to, in signing order. Detail and send responses only.
active_sendobjectnullableThe signature request currently in play, or null if it was never sent. Detail and send responses only. Full detail:
GET /signatures/{id}.tagsarray of objectsDetail responses only.
engagementobjectHow much a document has been read. Detail responses only.
linksarray of objectsnullablerequiredOnly when
include_linkswas set; otherwise null. One entry per recipient of this send.
- 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.Errors
- 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs:Errors
featurenames the capability (the operation'sx-feature) andrequired_tierthe cheapest tier that grants it. Every operation answers this with"feature": "core.api"when no module the organization holds is on a tier that includes the API. - 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (Errors
module_seat_required— an administrator assigns seats under Settings → Users), or the organization is suspended. - 404No such record in this organization.Errors
- 422The request body or query failed validation.Errors
- 429The key has made more requests this minute than its limit allows.Errors
- 503BankID was asked for — on the document, or for a recipient — and this server has no working BankID relying-party certificate. Nothing was changed or sent.Errors
bankid_unavailableis the onlyerrorhere; it is not something a retry fixes.
Remind recipients
/documents/{document}/remindwrite Permission documents.sendEmails a reminder, with their existing link, to the recipients of the current signature request who have not finished — or only to recipient_id.
Recipients are skipped, and listed in skipped with the reason, when they have already signed (already_signed) or declined (declined), were reminded less than 60 minutes ago (cooldown), or the document has expired (document_expired). Each reminder is recorded in the audit trail (reminder.sent) and counted in the recipient's reminder_count.
Answers 409 when the document has not been sent yet, or recipient_id is not on its current signature request.
curl -X POST 'https://api.closepact.com/v1/api/documents/01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47/remind' \
-H "Authorization: Bearer $CLOSEPACT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "Hej! En påminnelse om serviceavtalet — det går ut den 11 november."
}'{
"data": {
"sent": 1,
"skipped": [
{
"recipient_id": "01a15a31-0c2e-71aa-9c3d-6b1e8f2a4d59",
"reason": "already_signed"
}
]
}
}Path parameters
documentstring(uuid)requiredThe document's id.
Example
01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47
Request body
application/json
recipient_idstring(uuid)nullableRemind only this recipient. Omit to remind everyone who has not finished.
messagestringnullableA personal note in the reminder email.
at most 2,000 characters
Responses
Headers
X-RateLimit-Limit— Requests this key may make per minute.X-RateLimit-Remaining— Requests left in the current minute.
dataobjectrequiredsentintegerrequiredReminders emailed.
skippedarray of objectsrequired
- 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.Errors
- 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs:Errors
featurenames the capability (the operation'sx-feature) andrequired_tierthe cheapest tier that grants it. Every operation answers this with"feature": "core.api"when no module the organization holds is on a tier that includes the API. - 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (Errors
module_seat_required— an administrator assigns seats under Settings → Users), or the organization is suspended. - 404No such record in this organization.Errors
- 409The document is not in a state that allows this.Errors
errorsays which:document_not_sent— it has not been sent yet;recipient_not_on_document— the recipient is not on its current signature request;recipient_already_signed— the recipient has already signed and cannot be removed.messagesays the same for a person. - 422The request body or query failed validation.Errors
- 429The key has made more requests this minute than its limit allows.Errors