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

  1. POST /documents — upload the PDF (multipart). It is processed in the background; status goes from processing to ready.
  2. POST /documents/{document}/send — name the recipients. Each gets an email with a private link; the document's lifecycle_status becomes sent.
  3. Wait for the signature.completed webhook (or poll GET /documents/{document} until lifecycle_status is signed).
  4. 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.

List documents

GET/documents

Every 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"
Response · 200
{
  "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

  • qstring

    Case-insensitive match anywhere in the title.

    Example Serviceavtal

  • filter[type]string

    Only documents of this file type. Repeat as filter[type][] for several.

    One ofpdfdocximagevideofile

  • filter[status]string

    Only documents whose file is in this processing status. Repeat as filter[status][] for several.

    One ofprocessingreadyfailedquarantined

  • filter[lifecycle_status]string

    Only documents at this point with their recipients. Repeat as filter[lifecycle_status][] for several.

    One ofdraftsentopenedsigneddeclinedexpiredcanceled

  • limitinteger

    Items per page. Values outside 1–100 are clamped.

    Default 251–100

  • cursorstring

    Opaque cursor from meta.next_cursor or meta.prev_cursor of 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 objectsrequired
      • idstring(uuid)required
      • titlestringrequired
      • typestringrequired

        Derived from the detected MIME type on upload.

        One ofpdfdocximagevideofile

      • mime_typestringnullablerequired
      • size_bytesintegerrequired
      • page_countintegernullablerequired

        Known once a PDF has been processed.

      • page_images_statusstringnullablerequired

        Page images back the signing view and per-page statistics. unavailable for anything that is not a PDF.

        One ofpendingreadyunavailable

      • statusstringrequired

        The file: processing after upload, then ready, or failed when the virus scan rejects it or processing gives up. quarantined is reserved.

        One ofprocessingreadyfailedquarantined

      • lifecycle_statusstringrequired

        The document with its recipients. draft — never sent. sent — sent to recipients. opened — a recipient has opened it. signed — every signer and approver has signed (the signature.completed webhook fires). expired — expires_at passed before it was signed. canceled — its signature request was canceled. declined is reserved: a decline is recorded on the recipient and on active_send.status.

        One ofdraftsentopenedsigneddeclinedexpiredcanceled

      • value_centsintegernullablerequired

        What the document is worth, in the currency's minor unit (öre for SEK).

      • currencystringrequired

        ISO 4217 code.

        Example "SEK"

      • sent_atstring(date-time)nullablerequired

        When it was first sent.

      • expires_atstring(date-time)nullablerequired
      • days_leftintegernullablerequired

        Whole days until expires_at; negative once overdue; null without a deadline.

      • checksum_sha256stringnullablerequired

        SHA-256 of the uploaded file, hex.

      • signer_otp_requiredbooleanrequired

        Signers 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_channel is sms. On by default for new documents; change it with PATCH /documents/{document}.

      • signer_verification_channelstringrequired

        Where the one-time code goes. sms sends it to the recipient's phone_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 ofemailsms

      • bankid_sign_requiredbooleanrequired

        Signers 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_unavailable otherwise).

      • bankid_identification_requiredbooleanrequired

        Every recipient identifies with BankID before the document is shown to them at all. Same requirements as bankid_sign_required.

      • metadataobject | array of valuesnullablerequired

        Facts recorded while processing the file. Sent as an empty array [] when there are none.

      • created_atstring(date-time)required
      • updated_atstring(date-time)required
      • ownerobjectnullable

        Who 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 objects

        Everyone it has been sent to, in signing order. Detail and send responses only.

      • active_sendobjectnullable

        The signature request currently in play, or null if it was never sent. Detail and send responses only. Full detail: GET /signatures/{id}.

      • tagsarray of objects

        Detail responses only.

      • engagementobject

        How much a document has been read. Detail responses only.

    • linksobjectrequired
    • metaobjectrequired
  • 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: feature names the capability (the operation's x-feature) and required_tier the 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.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Upload a document

POST/documents
Scope write Permission documents.upload

Uploads 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'
Response · 201
{
  "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)required

    The file. At most 51,200 KB (50 MiB); see Size above.

  • titlestring

    Display 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.
    • dataobjectrequired

      A file in Closepact, and where it stands with its recipients.

      • idstring(uuid)required
      • titlestringrequired
      • typestringrequired

        Derived from the detected MIME type on upload.

        One ofpdfdocximagevideofile

      • mime_typestringnullablerequired
      • size_bytesintegerrequired
      • page_countintegernullablerequired

        Known once a PDF has been processed.

      • page_images_statusstringnullablerequired

        Page images back the signing view and per-page statistics. unavailable for anything that is not a PDF.

        One ofpendingreadyunavailable

      • statusstringrequired

        The file: processing after upload, then ready, or failed when the virus scan rejects it or processing gives up. quarantined is reserved.

        One ofprocessingreadyfailedquarantined

      • lifecycle_statusstringrequired

        The document with its recipients. draft — never sent. sent — sent to recipients. opened — a recipient has opened it. signed — every signer and approver has signed (the signature.completed webhook fires). expired — expires_at passed before it was signed. canceled — its signature request was canceled. declined is reserved: a decline is recorded on the recipient and on active_send.status.

        One ofdraftsentopenedsigneddeclinedexpiredcanceled

      • value_centsintegernullablerequired

        What the document is worth, in the currency's minor unit (öre for SEK).

      • currencystringrequired

        ISO 4217 code.

        Example "SEK"

      • sent_atstring(date-time)nullablerequired

        When it was first sent.

      • expires_atstring(date-time)nullablerequired
      • days_leftintegernullablerequired

        Whole days until expires_at; negative once overdue; null without a deadline.

      • checksum_sha256stringnullablerequired

        SHA-256 of the uploaded file, hex.

      • signer_otp_requiredbooleanrequired

        Signers 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_channel is sms. On by default for new documents; change it with PATCH /documents/{document}.

      • signer_verification_channelstringrequired

        Where the one-time code goes. sms sends it to the recipient's phone_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 ofemailsms

      • bankid_sign_requiredbooleanrequired

        Signers 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_unavailable otherwise).

      • bankid_identification_requiredbooleanrequired

        Every recipient identifies with BankID before the document is shown to them at all. Same requirements as bankid_sign_required.

      • metadataobject | array of valuesnullablerequired

        Facts recorded while processing the file. Sent as an empty array [] when there are none.

      • created_atstring(date-time)required
      • updated_atstring(date-time)required
      • ownerobjectnullable

        Who 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 objects

        Everyone it has been sent to, in signing order. Detail and send responses only.

      • active_sendobjectnullable

        The signature request currently in play, or null if it was never sent. Detail and send responses only. Full detail: GET /signatures/{id}.

      • tagsarray of objects

        Detail responses only.

      • engagementobject

        How 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: feature names the capability (the operation's x-feature) and required_tier the 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.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 422The request body or query failed validation.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Retrieve a document

GET/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"
Response · 200
{
  "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)required

    The 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.
    • dataobjectrequired

      A file in Closepact, and where it stands with its recipients.

      • idstring(uuid)required
      • titlestringrequired
      • typestringrequired

        Derived from the detected MIME type on upload.

        One ofpdfdocximagevideofile

      • mime_typestringnullablerequired
      • size_bytesintegerrequired
      • page_countintegernullablerequired

        Known once a PDF has been processed.

      • page_images_statusstringnullablerequired

        Page images back the signing view and per-page statistics. unavailable for anything that is not a PDF.

        One ofpendingreadyunavailable

      • statusstringrequired

        The file: processing after upload, then ready, or failed when the virus scan rejects it or processing gives up. quarantined is reserved.

        One ofprocessingreadyfailedquarantined

      • lifecycle_statusstringrequired

        The document with its recipients. draft — never sent. sent — sent to recipients. opened — a recipient has opened it. signed — every signer and approver has signed (the signature.completed webhook fires). expired — expires_at passed before it was signed. canceled — its signature request was canceled. declined is reserved: a decline is recorded on the recipient and on active_send.status.

        One ofdraftsentopenedsigneddeclinedexpiredcanceled

      • value_centsintegernullablerequired

        What the document is worth, in the currency's minor unit (öre for SEK).

      • currencystringrequired

        ISO 4217 code.

        Example "SEK"

      • sent_atstring(date-time)nullablerequired

        When it was first sent.

      • expires_atstring(date-time)nullablerequired
      • days_leftintegernullablerequired

        Whole days until expires_at; negative once overdue; null without a deadline.

      • checksum_sha256stringnullablerequired

        SHA-256 of the uploaded file, hex.

      • signer_otp_requiredbooleanrequired

        Signers 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_channel is sms. On by default for new documents; change it with PATCH /documents/{document}.

      • signer_verification_channelstringrequired

        Where the one-time code goes. sms sends it to the recipient's phone_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 ofemailsms

      • bankid_sign_requiredbooleanrequired

        Signers 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_unavailable otherwise).

      • bankid_identification_requiredbooleanrequired

        Every recipient identifies with BankID before the document is shown to them at all. Same requirements as bankid_sign_required.

      • metadataobject | array of valuesnullablerequired

        Facts recorded while processing the file. Sent as an empty array [] when there are none.

      • created_atstring(date-time)required
      • updated_atstring(date-time)required
      • ownerobjectnullable

        Who 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 objects

        Everyone it has been sent to, in signing order. Detail and send responses only.

      • active_sendobjectnullable

        The signature request currently in play, or null if it was never sent. Detail and send responses only. Full detail: GET /signatures/{id}.

      • tagsarray of objects

        Detail responses only.

      • engagementobject

        How 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: feature names the capability (the operation's x-feature) and required_tier the 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.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Update a document

PATCH/documents/{document}
Scope write Permission documents.update

Changes 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"
  ]
}'
Response · 200
{
  "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)required

    The document's id.

    Example 01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47

Request body

application/json · required

  • titlestring

    1–255 characters

  • value_centsintegernullable

    What the document is worth, in the currency's minor unit (öre for SEK).

    0–99,999,999,999,999

  • currencystring

    ISO 4217 code; stored upper-case.

    pattern ^[A-Za-z]{3}$

  • expires_atstring(date-time)nullable
  • signer_otp_requiredboolean

    Whether signers must enter a one-time code before they can sign or decline. Applies to recipients who have not signed yet.

  • signer_verification_channelstring

    Where the code goes. sms needs a phone_number on the recipient and an SMS provider on the server; without either the code is emailed and the signing page says why.

    One ofemailsms

  • bankid_sign_requiredboolean

    Signers sign with BankID. Switching it on answers 402 on a tier without BankID and 503 (bankid_unavailable) when BankID is not configured on the server.

  • bankid_identification_requiredboolean

    Recipients identify with BankID before the document is shown to them. Same 402/503 as bankid_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.
    • dataobjectrequired

      A file in Closepact, and where it stands with its recipients.

      • idstring(uuid)required
      • titlestringrequired
      • typestringrequired

        Derived from the detected MIME type on upload.

        One ofpdfdocximagevideofile

      • mime_typestringnullablerequired
      • size_bytesintegerrequired
      • page_countintegernullablerequired

        Known once a PDF has been processed.

      • page_images_statusstringnullablerequired

        Page images back the signing view and per-page statistics. unavailable for anything that is not a PDF.

        One ofpendingreadyunavailable

      • statusstringrequired

        The file: processing after upload, then ready, or failed when the virus scan rejects it or processing gives up. quarantined is reserved.

        One ofprocessingreadyfailedquarantined

      • lifecycle_statusstringrequired

        The document with its recipients. draft — never sent. sent — sent to recipients. opened — a recipient has opened it. signed — every signer and approver has signed (the signature.completed webhook fires). expired — expires_at passed before it was signed. canceled — its signature request was canceled. declined is reserved: a decline is recorded on the recipient and on active_send.status.

        One ofdraftsentopenedsigneddeclinedexpiredcanceled

      • value_centsintegernullablerequired

        What the document is worth, in the currency's minor unit (öre for SEK).

      • currencystringrequired

        ISO 4217 code.

        Example "SEK"

      • sent_atstring(date-time)nullablerequired

        When it was first sent.

      • expires_atstring(date-time)nullablerequired
      • days_leftintegernullablerequired

        Whole days until expires_at; negative once overdue; null without a deadline.

      • checksum_sha256stringnullablerequired

        SHA-256 of the uploaded file, hex.

      • signer_otp_requiredbooleanrequired

        Signers 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_channel is sms. On by default for new documents; change it with PATCH /documents/{document}.

      • signer_verification_channelstringrequired

        Where the one-time code goes. sms sends it to the recipient's phone_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 ofemailsms

      • bankid_sign_requiredbooleanrequired

        Signers 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_unavailable otherwise).

      • bankid_identification_requiredbooleanrequired

        Every recipient identifies with BankID before the document is shown to them at all. Same requirements as bankid_sign_required.

      • metadataobject | array of valuesnullablerequired

        Facts recorded while processing the file. Sent as an empty array [] when there are none.

      • created_atstring(date-time)required
      • updated_atstring(date-time)required
      • ownerobjectnullable

        Who 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 objects

        Everyone it has been sent to, in signing order. Detail and send responses only.

      • active_sendobjectnullable

        The signature request currently in play, or null if it was never sent. Detail and send responses only. Full detail: GET /signatures/{id}.

      • tagsarray of objects

        Detail responses only.

      • engagementobject

        How 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: feature names the capability (the operation's x-feature) and required_tier the 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.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 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. bankid_unavailable is the only error here; it is not something a retry fixes.
    Errors

Delete a document

DELETE/documents/{document}
Scope write Permission documents.delete

Deletes 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"
Response · 200
{
  "deleted": true
}

Path parameters

  • documentstring(uuid)required

    The 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.
    • deletedbooleanrequired

      One oftrue

  • 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: feature names the capability (the operation's x-feature) and required_tier the 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.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors
GET/documents/{document}/download-link
Scope read Permission documents.read.any

Returns 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 GET and without the Authorization header: 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-Type and the document's title as the file name. An expired or altered URL answers 403.

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"
Response · 200
{
  "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)required

    The 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.
    • dataobjectrequired
      • urlstring(uri)required

        Signed URL to GET without an Authorization header. Works for expires_in_seconds.

      • expires_in_secondsintegerrequired

        How 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: feature names the capability (the operation's x-feature) and required_tier the 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.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 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

GET/documents/{document}/timeline

Everything 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"
Response · 200
{
  "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)required

    The 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.
    • dataobjectrequired
      • eventsarray of objectsrequired

        Newest first; at most 200.

      • by_recipientarray of objectsrequired
      • integrityobjectrequired

        Whether 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: feature names the capability (the operation's x-feature) and required_tier the 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.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Get reading statistics

GET/documents/{document}/engagement

How 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"
Response · 200
{
  "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)required

    The 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.
    • dataobjectrequired
      • summaryobjectrequired

        How much a document has been read. Detail responses only.

      • pagesarray of objectsrequired

        One 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 objectsrequired
      • event_countsobject | array of valuesrequired

        Event 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: feature names the capability (the operation's x-feature) and required_tier the 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.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

List recipients

GET/documents/{document}/recipients

Everyone 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"
Response · 200
{
  "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)required

    The 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 objectsrequired
      • idstring(uuid)required
      • namestringrequired
      • emailstring(email)required
      • rolestringrequired

        signer and approver must sign; cc and viewer only read.

        One ofsignerapproverccviewer

      • statusstringrequired

        pending until they open their link, then opened, then signed or declined.

        One ofpendingopenedsigneddeclined

      • order_indexintegerrequired

        Position in a sequential signing order, from 0.

      • opened_atstring(date-time)nullablerequired
      • signed_atstring(date-time)nullablerequired
      • declined_atstring(date-time)nullablerequired
      • decline_reasonstringnullablerequired
      • view_countintegerrequired
      • total_time_msintegerrequired

        Time spent reading, in milliseconds.

      • last_activity_atstring(date-time)nullablerequired
      • reminder_countintegerrequired
      • last_reminded_atstring(date-time)nullablerequired
      • phone_numberstringnullablerequired

        E.164, for the one-time code by SMS.

        Example "+46701234567"

      • bankid_requiredbooleanrequired

        This recipient signs with BankID even if the document does not ask it of everybody.

      • personal_number_maskedstringnullablerequired

        The 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: feature names the capability (the operation's x-feature) and required_tier the 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.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Add a recipient

POST/documents/{document}/recipients
Scope write Permission documents.update

Adds 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
}'
Response · 201
{
  "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)required

    The document's id.

    Example 01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47

Request body

application/json · required

  • emailstring(email)required

    at most 255 characters

  • namestringrequired

    at most 255 characters

  • rolestring

    One ofsignerapproverccviewer

    Default "signer"

  • order_indexinteger

    Position in a sequential signing order. Defaults to after the last recipient. Ignored for the first recipient.

    0–100

  • messagestringnullable

    A personal note in the invitation email.

    at most 2,000 characters

  • send_inviteboolean

    Email the recipient their link. Only applies once the document has been sent; the first recipient is always emailed.

    Default true

  • phone_numberstringnullable

    For 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_requiredboolean

    This recipient signs with BankID even when the document does not ask it of everybody.

    Default false

  • personal_numberstringnullable

    The 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 show personal_number_masked.

    Example "19811218-9876"

  • include_linkboolean

    Return 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.
    • dataobjectrequired

      Somebody a document was sent to.

      • idstring(uuid)required
      • namestringrequired
      • emailstring(email)required
      • rolestringrequired

        signer and approver must sign; cc and viewer only read.

        One ofsignerapproverccviewer

      • statusstringrequired

        pending until they open their link, then opened, then signed or declined.

        One ofpendingopenedsigneddeclined

      • order_indexintegerrequired

        Position in a sequential signing order, from 0.

      • opened_atstring(date-time)nullablerequired
      • signed_atstring(date-time)nullablerequired
      • declined_atstring(date-time)nullablerequired
      • decline_reasonstringnullablerequired
      • view_countintegerrequired
      • total_time_msintegerrequired

        Time spent reading, in milliseconds.

      • last_activity_atstring(date-time)nullablerequired
      • reminder_countintegerrequired
      • last_reminded_atstring(date-time)nullablerequired
      • phone_numberstringnullablerequired

        E.164, for the one-time code by SMS.

        Example "+46701234567"

      • bankid_requiredbooleanrequired

        This recipient signs with BankID even if the document does not ask it of everybody.

      • personal_number_maskedstringnullablerequired

        The 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"

    • linkobjectnullablerequired

      Only when include_link was 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: feature names the capability (the operation's x-feature) and required_tier the 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.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 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. bankid_unavailable is the only error here; it is not something a retry fixes.
    Errors

Remove a recipient

DELETE/documents/{document}/recipients/{recipient}
Scope write Permission documents.update

Takes 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"
Response · 204
204 — no response body

Path parameters

  • documentstring(uuid)required

    The document's id.

    Example 01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47

  • recipientstring(uuid)required

    The recipient's id, from recipients on 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: feature names the capability (the operation's x-feature) and required_tier the 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.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • 409The document is not in a state that allows this. error says 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. message says the same for a person.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Send for signing

POST/documents/{document}/send
Scope write Permission documents.send

Sends 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"
}'
Response · 201
{
  "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)required

    The document's id.

    Example 01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47

Request body

application/json · required

  • recipientsarray of objectsrequired

    1–20 items

  • titlestring

    Subject of the signature request. Defaults to the document's title.

    at most 255 characters

  • messagestringnullable

    A personal note in every recipient's email.

    at most 5,000 characters

  • signing_orderstring

    One ofparallelsequential

    Default "parallel"

  • expires_atstring(date-time)nullable

    Signing deadline; must be in the future. Also becomes the document's expires_at. Defaults to the document's expires_at.

  • deal_room_idstring(uuid)nullable

    Link the signature request to a deal room of this organization. Any other id, including another organization's room, fails validation (422).

  • include_linksboolean

    Return 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.
    • dataobjectrequired

      A file in Closepact, and where it stands with its recipients.

      • idstring(uuid)required
      • titlestringrequired
      • typestringrequired

        Derived from the detected MIME type on upload.

        One ofpdfdocximagevideofile

      • mime_typestringnullablerequired
      • size_bytesintegerrequired
      • page_countintegernullablerequired

        Known once a PDF has been processed.

      • page_images_statusstringnullablerequired

        Page images back the signing view and per-page statistics. unavailable for anything that is not a PDF.

        One ofpendingreadyunavailable

      • statusstringrequired

        The file: processing after upload, then ready, or failed when the virus scan rejects it or processing gives up. quarantined is reserved.

        One ofprocessingreadyfailedquarantined

      • lifecycle_statusstringrequired

        The document with its recipients. draft — never sent. sent — sent to recipients. opened — a recipient has opened it. signed — every signer and approver has signed (the signature.completed webhook fires). expired — expires_at passed before it was signed. canceled — its signature request was canceled. declined is reserved: a decline is recorded on the recipient and on active_send.status.

        One ofdraftsentopenedsigneddeclinedexpiredcanceled

      • value_centsintegernullablerequired

        What the document is worth, in the currency's minor unit (öre for SEK).

      • currencystringrequired

        ISO 4217 code.

        Example "SEK"

      • sent_atstring(date-time)nullablerequired

        When it was first sent.

      • expires_atstring(date-time)nullablerequired
      • days_leftintegernullablerequired

        Whole days until expires_at; negative once overdue; null without a deadline.

      • checksum_sha256stringnullablerequired

        SHA-256 of the uploaded file, hex.

      • signer_otp_requiredbooleanrequired

        Signers 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_channel is sms. On by default for new documents; change it with PATCH /documents/{document}.

      • signer_verification_channelstringrequired

        Where the one-time code goes. sms sends it to the recipient's phone_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 ofemailsms

      • bankid_sign_requiredbooleanrequired

        Signers 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_unavailable otherwise).

      • bankid_identification_requiredbooleanrequired

        Every recipient identifies with BankID before the document is shown to them at all. Same requirements as bankid_sign_required.

      • metadataobject | array of valuesnullablerequired

        Facts recorded while processing the file. Sent as an empty array [] when there are none.

      • created_atstring(date-time)required
      • updated_atstring(date-time)required
      • ownerobjectnullable

        Who 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 objects

        Everyone it has been sent to, in signing order. Detail and send responses only.

      • active_sendobjectnullable

        The signature request currently in play, or null if it was never sent. Detail and send responses only. Full detail: GET /signatures/{id}.

      • tagsarray of objects

        Detail responses only.

      • engagementobject

        How much a document has been read. Detail responses only.

    • linksarray of objectsnullablerequired

      Only when include_links was 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: feature names the capability (the operation's x-feature) and required_tier the 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.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 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. bankid_unavailable is the only error here; it is not something a retry fixes.
    Errors

Remind recipients

POST/documents/{document}/remind
Scope write Permission documents.send

Emails 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."
}'
Response · 200
{
  "data": {
    "sent": 1,
    "skipped": [
      {
        "recipient_id": "01a15a31-0c2e-71aa-9c3d-6b1e8f2a4d59",
        "reason": "already_signed"
      }
    ]
  }
}

Path parameters

  • documentstring(uuid)required

    The document's id.

    Example 01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47

Request body

application/json

  • recipient_idstring(uuid)nullable

    Remind only this recipient. Omit to remind everyone who has not finished.

  • messagestringnullable

    A 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.
    • dataobjectrequired
      • sentintegerrequired

        Reminders 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: feature names the capability (the operation's x-feature) and required_tier the 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.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • 409The document is not in a state that allows this. error says 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. message says the same for a person.
    Errors
  • 422The request body or query failed validation.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors