Portal API¶
The Portal API lets your app create and manage signing processes. You provide the document and the list of participants; esigna sends the invitation emails, collects the signatures through its own signing flow, and tells your app what happened through webhooks.
Scopes required
Endpoints on this page require the portal/read or portal/write scope. See Authorization for how to get a token.
All paths below are relative to https://api.esigna.is.
How it works¶
- Your app creates a signing process with a document and a list of participants.
- esigna emails an invitation to each participant, with a link to the document.
- Participants sign in esigna's signing page with their electronic ID.
- esigna notifies your app of each event (a participant signed or declined, the process completed) through webhooks.
- When everyone has signed, your app downloads the signed document.
The Portal API works with the signing processes your account creates through the API, with any of its API clients. Documents your staff send from the esigna web app are not visible through the API and send no webhooks.
Creating a signing process¶
POST /api/v1/portal/signing-processes (requires portal/write)
Example request¶
Example response: 201 Created¶
The Location header holds the new process's address, /api/v1/portal/signing-processes/{signingProcessId}. The body is the process as in Getting a process's status: its participants, each with their participantId and the signingUrl you can send them yourself.
Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
file.name |
string | Yes | The file name, ending in .pdf: at most 100 characters, no / or \. It is shown in the invitation emails. |
file.contentBase64 |
string | Yes | The document, a PDF, encoded in Base64. |
participants |
array | Yes | At least one participant must have the Signer role. See below. |
signatureLocation |
string | Yes | The corner where signature stamps go: TopLeft, TopRight, BottomLeft or BottomRight. |
signaturePage |
integer | No | The page the stamps go on, counting from 1. Defaults to 1. The page must exist in the document. |
The whole request can be up to about 30 MB. Because the document is Base64 inside JSON, that allows a PDF of up to about 22 MB.
Participants¶
Each participant gets an invitation email from esigna with a link to review the document, and to sign it if they are a signer.
| Field | Type | Required | Description |
|---|---|---|---|
participants[].name |
string | Yes | Full name, at most 256 characters. |
participants[].email |
string | Yes | Email address the invitation is sent to, at most 256 characters. Each participant needs their own address. |
participants[].role |
string | Yes | Signer or Viewer. |
participants[].kennitala |
string | No | Icelandic national ID, 10 digits without a hyphen. If set, only the person with this kennitala can sign: esigna compares it with the kennitala from the signer's electronic ID and refuses to sign if they differ. |
participants[].phoneNumber |
string | No | Phone number, at most 7 digits, digits only. Stored and returned in webhook events; not used for signing. |
| Role | Description |
|---|---|
Signer |
Signs the document. |
Viewer |
Gets the document to read but does not sign. Viewers don't affect the process status. |
Signature stamps¶
Each signer's stamp goes in the corner you choose, on the page you choose. Later stamps are placed next to earlier ones, avoiding existing signatures and the page's content. A page has room for a limited number of stamps; if there are more signers than the page can hold, the request is refused and nothing is created. Viewers get no stamp and don't count.
Errors¶
| Status | When |
|---|---|
400 with errors |
A field is missing, too long, not valid Base64, not an email address, not a plain .pdf file name, or not one of the listed values; two participants share an email address; a kennitala isn't 10 digits; no participant has the Signer role. The response is a validation problem naming the fields. |
400 "Document could not be read" |
The file is not a readable PDF, or it is encrypted. |
400 "Signature page does not exist" |
signaturePage is not a page of the document. |
400 "Too many participants for the page" |
The page has no room for that many stamps. |
400 "Payment method required" |
The account has no payment method, and its free signatures don't cover this process's signers. |
400 "Storage quota exceeded" |
The account's document storage is full. |
401 / 403 |
See Authorization. |
The titled 400s are problem details with title and detail.
Getting a process's status¶
GET /api/v1/portal/signing-processes/{signingProcessId} (requires portal/read)
Example response¶
Status values¶
The process's status is worked out from its signers; viewers don't count.
| Status | Meaning |
|---|---|
Waiting |
At least one signer has not signed yet. |
Completed |
Every signer has signed. The signed document can be downloaded. |
Declined |
At least one signer declined to sign. |
Expired |
A signer's invitation is more than 7 days old and the process isn't finished. |
A participant's own status uses the same values, and is None for viewers.
A process that has been deleted, that belongs to another account, or that was sent from the esigna web app returns 404 Not Found.
Sending people their link yourself¶
Each participant's signingUrl is the link in their invitation email. You can send it to them yourself, for example by taking them straight there from your app, instead of waiting for them to open the email. They open it, log in with their electronic ID, and sign (a viewer reads the document).
- Set the participant's
kennitala. Whoever opens the link and logs in first takes the participant's place. With a kennitala, only that person can. - Treat the link like a password: give it only to that participant, and don't log it.
- An unopened link is valid for 7 days. Once the participant has logged in through it,
signingUrlis their page without the one-time token, and they log in to reach it. It isnullafter they sign or decline, and if the invitation expired unopened. - esigna still sends the invitation email.
- In the sandbox the link leads nowhere: there is no signing page. Use the sandbox endpoints to sign or decline.
Downloading the document¶
GET /api/v1/portal/signing-processes/{signingProcessId}/document (requires portal/read)
Returns the document as it is now: once the status is Completed, that is the signed document with every signature.
Example response¶
Deleting a signing process¶
DELETE /api/v1/portal/signing-processes/{signingProcessId} (requires portal/write)
Participants can no longer open or sign the document, and the process is no longer returned by the API. This can't be undone.
Returns 204 No Content, or 404 Not Found if there is no such process.
Receiving events¶
Rather than polling the status, subscribe to webhooks and esigna will call your endpoint when something happens:
| Event | When |
|---|---|
participant.signed |
A participant signed the document. |
participant.declined |
A participant declined to sign. |
process.completed |
Every signer has signed. Download the signed document when you get it. |
An expired process has no event; check its status if you need to know.
esigna emails the participants (the invitation, and a link to the signed document when everyone has signed), but nobody at your account: for a process created through the API, the webhooks and the status are how your app learns that a participant declined or that the process completed.
Testing in the sandbox¶
The sandbox, https://sandbox-api.esigna.is, works like production with your sandbox credentials, with two differences: nobody signs with a real electronic ID, and no emails are sent: no invitations, no completion emails and no webhook alerts to your admins. Instead, your app answers for the participants, and everything that follows (the status, the signed document, the webhooks) is the same as in production.
- Create a sandbox user for each identity you want to sign with:
POST /api/v1/sandbox/userswithfullName,kennitalaandphone. Each of your sandbox users needs its own kennitala and phone number. The response'suuididentifies the user. See Sandbox users for all the fields. - Create a signing process as above, and get the participants'
participantIds from its status. - Sign or decline as a participant:
| Endpoint | Body | Result |
|---|---|---|
POST /api/v1/sandbox/portal/signing-processes/{signingProcessId}/participants/{participantId}/sign |
{ "sandboxUserId": "..." } |
The participant signs with that sandbox user's identity. |
POST /api/v1/sandbox/portal/signing-processes/{signingProcessId}/participants/{participantId}/decline |
{ "reason": "..." } |
The participant declines, with that reason (at most 256 characters). |
Both need portal/write and return 204 No Content. They return 404 for a process, participant or sandbox user that isn't yours, and 400 when:
- the participant is a viewer, or has already signed or declined;
- the participant was created with a
kennitalaand the sandbox user has a different one; - the sandbox user is set to refuse (
action: Cancel) or to wait before answering (delayInSecondsBeforeAction): use one withaction: Successand no delay.
Signing answers 423 Locked while another participant's signature on the same document is in progress; try again when it has finished.