Menu
Cookbooks
API reference
- Overview
- Companies
- Capabilities
- Measures
- Packages
- Unexplained
- Liquidity forecast
- Portfolio
- Customers
- Invoices
- Collections
- Articles
- Set-offs
- Suppliers
- Supplier invoices
- Supplier payment files
- Bank payments
- Expense claims
- Transactions
- Reconciliation
- Bank accounts
- POS sales
- Webshop orders
- Journal entries
- Voucher gap explanations
- Fiscal periods
- Accounts
- Accrual schedules
- Agreements and commitments
- Tax loss carry-forwards
- Fixed assets
- Documents
- Signing requests
- Company registers
- Capital events
- Owner tax
- Inbox items
- Dimensions
- Employees
- Salary runs
- Reports
- Budgets
- Imports
- Compliance check
- Skatteverket
- Peppol
- Webhooks
- Operations
- Pending operations
- Health
- API key
- Service accounts
Signing requests
BankID signing of archived PDFs: send a document to its signers (one link each, mailed when they have an address), read who has signed, remind a pending signer, cancel. Once everyone has signed, the signed PDF and the evidence package are archive documents. No endpoint signs: each signer signs in person with BankID.
Endpoints
GET/api/v1/companies/:companyId/signing-requests: List the company's BankID signing requests, newest first, with every signer's status.GET/api/v1/companies/:companyId/signing-requests/:id: Read one BankID signing request: every signer's status and, once completed, the signed PDF and the evidence package.POST/api/v1/companies/:companyId/signing-requests: Send an archived PDF for BankID signing: one link per signer, mailed when they have an address.POST/api/v1/companies/:companyId/signing-requests/:id/cancel: Cancel an open signing request: its links stop working; signatures already made stay.POST/api/v1/companies/:companyId/signing-requests/:id/signers/:signerId/remind: Mail a pending signer again, once a day at most; the mail carries a new link and the old one stops working.
GET /api/v1/companies/:companyId/signing-requests
signing-requests.list · scope documents:read
List the company's BankID signing requests, newest first, with every signer's status.
Filter by status (open, completed, declined, cancelled) or document_id (the signed PDF, its signed copy or its evidence package). An open request past its expiry reads as state expired.
Use when: You need to know which documents wait for signatures, or find the request behind a document.
Don't use for: One request in detail (GET /signing-requests/{id}).
Pitfalls
- status filters the stored status; an expired request still has status open and reads state expired.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
status | "open" | "completed" | "declined" | "cancelled" | no | Only requests with this status. |
document_id | string | no | Only requests covering this document. |
limit | number | no | At most this many (default 50). |
Response fields
| Name | Type | Description |
|---|---|---|
signing_requests[].signing_request_id | string | |
signing_requests[].title | string | |
signing_requests[].message | string | null | |
signing_requests[].state | "open" | "completed" | "declined" | "cancelled" | "expired" | |
signing_requests[].document_id | string | The PDF that is signed (an archive document). |
signing_requests[].document_sha256 | string | Its SHA-256 when the request was made; every BankID signature carries it. |
signing_requests[].annual_report_version_id | string | null | |
signing_requests[].created_by | string | |
signing_requests[].created_via | "app" | "api" | "mcp" | |
signing_requests[].created_at | string | |
signing_requests[].expires_at | string | |
signing_requests[].completed_at | string | null | |
signing_requests[].declined_at | string | null | |
signing_requests[].cancelled_at | string | null | |
signing_requests[].signed_document_id | string | null | Once completed: the signed PDF with its Signeringsbevis page (GET /documents/{id}/download). |
signing_requests[].evidence_document_id | string | null | Once completed: Bevispaket.zip, the original, each BankID signature and OCSP response, and a manifest. |
signing_requests[].signers | object[] |
Example response
{
"data": {
"signing_requests": [
{
"signing_request_id": "8e1d…",
"title": "Hyresavtal kontor",
"message": null,
"state": "open",
"document_id": "4f1c…",
"document_sha256": "9b2e…",
"annual_report_version_id": null,
"created_by": "a3f0…",
"created_via": "mcp",
"created_at": "2026-10-25T09:00:00.000Z",
"expires_at": "2026-11-08T09:00:00.000Z",
"completed_at": null,
"declined_at": null,
"cancelled_at": null,
"signed_document_id": null,
"evidence_document_id": null,
"signers": [
{
"signer_id": "2c7b…",
"position": 1,
"name": "Anna Svensson",
"email": "anna@example.se",
"role_label": "Hyresvärd",
"user_id": null,
"annual_report_signature_id": null,
"lock": "none",
"status": "pending",
"signed_at": null,
"bankid_name": null,
"declined_at": null,
"decline_reason": null,
"last_error_code": null,
"link_sent_at": "2026-10-25T09:00:01.000Z",
"last_reminded_at": null
}
]
}
]
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
GET /api/v1/companies/:companyId/signing-requests/:id
signing-requests.get · scope documents:read
Read one BankID signing request: every signer's status and, once completed, the signed PDF and the evidence package.
signed_document_id is the signed PDF (the original plus a Signeringsbevis page with each signer's BankID name, time and masked personnummer) and evidence_document_id is Bevispaket.zip (the original, every BankID signature with its OCSP response, and a manifest). Download either through GET /documents/{id}/download.
Use when: You sent a document for signing and need to know who has signed, or want the signed copy.
Don't use for: Listing requests (GET /signing-requests).
Pitfalls
- A completed request may show null copies for a moment while they are sealed; read it again.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Response fields
| Name | Type | Description |
|---|---|---|
signing_request_id | string | |
title | string | |
message | string | null | |
state | "open" | "completed" | "declined" | "cancelled" | "expired" | |
document_id | string | The PDF that is signed (an archive document). |
document_sha256 | string | Its SHA-256 when the request was made; every BankID signature carries it. |
annual_report_version_id | string | null | |
created_by | string | |
created_via | "app" | "api" | "mcp" | |
created_at | string | |
expires_at | string | |
completed_at | string | null | |
declined_at | string | null | |
cancelled_at | string | null | |
signed_document_id | string | null | Once completed: the signed PDF with its Signeringsbevis page (GET /documents/{id}/download). |
evidence_document_id | string | null | Once completed: Bevispaket.zip, the original, each BankID signature and OCSP response, and a manifest. |
signers | object[] |
Example response
{
"data": {
"signing_request_id": "8e1d…",
"title": "Hyresavtal kontor",
"message": null,
"state": "open",
"document_id": "4f1c…",
"document_sha256": "9b2e…",
"annual_report_version_id": null,
"created_by": "a3f0…",
"created_via": "mcp",
"created_at": "2026-10-25T09:00:00.000Z",
"expires_at": "2026-11-08T09:00:00.000Z",
"completed_at": null,
"declined_at": null,
"cancelled_at": null,
"signed_document_id": null,
"evidence_document_id": null,
"signers": [
{
"signer_id": "2c7b…",
"position": 1,
"name": "Anna Svensson",
"email": "anna@example.se",
"role_label": "Hyresvärd",
"user_id": null,
"annual_report_signature_id": null,
"lock": "none",
"status": "pending",
"signed_at": null,
"bankid_name": null,
"declined_at": null,
"decline_reason": null,
"last_error_code": null,
"link_sent_at": "2026-10-25T09:00:01.000Z",
"last_reminded_at": null
}
]
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/signing-requests
signing-requests.create · scope documents:write
Send an archived PDF for BankID signing: one link per signer, mailed when they have an address.
Freezes the document by its SHA-256 and creates one link per signer to a page at /signera/<token> where they read the PDF and sign it with BankID (an advanced electronic signature under eIDAS). Links are valid expires_in_days (default 14, at most 90). Signers with an email get a mail; the answer carries the url only for a signer no mail reached. A member of the company (user_id) can also sign in the app. When everyone has signed, the archive gets the signed PDF (with a Signeringsbevis page) and Bevispaket.zip, and every signer with an email gets the signed PDF; read them from GET /signing-requests/{id} or the signing.completed webhook. Dry-runnable: ?dry_run=true answers what would be sent and mints nothing.
Use when: An agreement, minutes, a policy or any other PDF in the archive needs signatures from people inside or outside the company.
Don't use for: Signing bank payments (POST /payments/signing-links), the fastställelseintyg (signed at Bolagsverket), or documents that are not PDFs.
Pitfalls
- The url is the credential: hand it to that signer only, once, and do not store it.
- SIGNING_DOCUMENT_UNSUPPORTED: only the current version of a PDF of at most 10 MB without a password can be signed.
- SIGNING_SIGNERS_INVALID: a name is required, an email or member appears once, and user_id must be a member of the company (details.reason says which).
- A personnummer cannot be sent here; locking a signer to one person is done in the app.
- SIGNING_UNAVAILABLE: BankID signing is not switched on for this company.
- SIGNING_CAPABILITY_BLOCKED: signing is part of the paid plan and the trial; the company has neither.
Risk: medium · Idempotent: yes · Reversible: no · Dry-run supported: yes · Stage for approval: yes (?stage=true)
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
stage | string | no | true stages the write for approval instead of committing it: 202 with the pending operation to approve via POST /pending-operations/{id}/approve. Absent or false commits; other values are 400. A reason in the body (why you propose it, at most 500 characters) is shown to the approver as agent_reason and kept with the decision. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
document_id | string | yes | The PDF in the archive (document_id). |
title | string | yes | What the signers sign, shown in the mail, on the page and in the BankID app. |
message | string | null | no | A message to the signers, shown in the mail and on the page. |
expires_in_days | number | null | no | How long the links work. Default 14. |
send_email | boolean | no | Mail each signer with an email (default true). |
signers | object[] | yes | 1-20 signers; everyone signs, in any order. |
Response fields
| Name | Type |
|---|---|
signing_request_id | string |
status | "open" |
title | string |
document_id | string |
document_sha256 | string |
expires_at | string |
signers | object[] |
Example request
{
"document_id": "4f1c…",
"title": "Hyresavtal kontor",
"signers": [
{
"name": "Anna Svensson",
"email": "anna@example.se",
"role_label": "Hyresvärd"
}
]
}
Example response
{
"data": {
"signing_request_id": "8e1d…",
"status": "open",
"title": "Hyresavtal kontor",
"document_id": "4f1c…",
"document_sha256": "9b2e…",
"expires_at": "2026-11-08T09:00:00.000Z",
"signers": [
{
"signer_id": "2c7b…",
"position": 1,
"name": "Anna Svensson",
"email": "anna@example.se",
"annual_report_signature_id": null,
"lock": "none",
"email_sent": true,
"url": null
}
]
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/signing-requests/:id/cancel
signing-requests.cancel · scope documents:write
Cancel an open signing request: its links stop working; signatures already made stay.
The sender, an owner or an admin. Cancelling twice answers the same. A completed or declined request cannot be cancelled. Dry-runnable.
Use when: The document changed or was sent to the wrong people.
Don't use for: Removing a signature (a signature is evidence and is kept).
Pitfalls
- SIGNING_REQUEST_CLOSED: the request completed or was declined.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Response fields
| Name | Type |
|---|---|
signing_request_id | string |
cancelled | true |
Example response
{
"data": {
"signing_request_id": "8e1d…",
"cancelled": true
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/signing-requests/:id/signers/:signerId/remind
signing-requests.remind · scope documents:write
Mail a pending signer again, once a day at most; the mail carries a new link and the old one stops working.
For an open request and a signer who has not signed and has an email. Refused while the signer is in a BankID signing right now. The answer carries the new link only when no mail went out. Dry-runnable.
Use when: A signer has not signed after a while.
Don't use for: A signer without an email (hand them the link you got at creation) or a closed request.
Pitfalls
- SIGNING_REMIND_TOO_SOON: one mail per signer per 24 hours, the first mail included.
- SIGNING_SESSION_ACTIVE: the signer started BankID in the last few minutes; try later.
- The old link stops working: only the newest link signs.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: yes · Stage for approval: yes (?stage=true)
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
stage | string | no | true stages the write for approval instead of committing it: 202 with the pending operation to approve via POST /pending-operations/{id}/approve. Absent or false commits; other values are 400. A reason in the body (why you propose it, at most 500 characters) is shown to the approver as agent_reason and kept with the decision. |
Response fields
| Name | Type |
|---|---|
signing_request_id | string |
signer_id | string |
email_sent | boolean |
url | string | null |
Example response
{
"data": {
"signing_request_id": "8e1d…",
"signer_id": "2c7b…",
"email_sent": true,
"url": null
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}