Skip to content

Signing API

The Signing API (also called the Integration API) lets your app embed signing in its own workflow. You provide the document and identify the signer; esigna has the signer approve the signature with their electronic ID from Auðkenni and returns the signed document to you.

Scope required

The signing endpoints on this page require the api/signatures scope. See Authorization for how to get a token.

How it works

Signing is asynchronous: the signer approves the request on their phone, which takes as long as it takes. The flow is:

  1. (Optional) Create a busy signal if several signers will sign the same file.
  2. Start a signing request. esigna asks the signer to approve it and returns a pollingId.
  3. Poll with the pollingId until the signed document comes back or the request ends.

Step 1: Create a busy signal

POST /api/v1/signing/busy-signal

If several signers sign the same file one after another, use a busy signal so that two signings can't run on the file at once: a second signature started before the first is applied would invalidate it.

Create one busy signal per document and pass its ID with every signing request for that document. A signing request holds the signal from its start call until polling for it ends (signed or failed), and for at most 5 minutes.

You don't need one if each signer signs their own copy of the file.

Response

1
2
3
{
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

If a signing request starts while the signal is held, the start call answers 423 Locked:

1
2
3
4
5
{
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "blockedBy": "user-id-from-your-system",
    "blockedUntil": "2026-10-09T12:05:00Z"
}

A busy signal ID that doesn't exist gives 404 Not Found.


Step 2: Start a signing request

POST /api/v1/signing

Request body

{
    "file": {
        "type": "Pdf",
        "name": "contract.pdf",
        "contentB64": "JVBERi0xLjQK..."
    },
    "signer": {
        "kennitala": "0101302989",
        "signingMethod": "AudkenniApp"
    },
    "signatureImage": {
        "alignment": "BottomLeft",
        "positioning": "Auto"
    }
}

Response: 200 OK

1
2
3
4
5
6
{
    "pollingId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "expiresIn": 300,
    "interval": 2,
    "verificationCode": null
}

With AudkenniApp, as here, verificationCode is null: the code arrives in a poll response. With AudkenniSim it is in this response. See Verification code.

Field Type Description
pollingId string (UUID) Use this in step 3.
expiresIn int Seconds until the signing request expires.
interval int Minimum seconds between poll requests.
verificationCode string | null 4-digit code to show the signer. May be null; see Verification code.

File

Field Type Required Description
file.type string Yes Pdf or Xml.
file.name string Yes File name, at most 256 characters. The Auðkenni app shows its first 60 characters to the signer.
file.contentB64 string Yes The file, encoded in Base64.

Signer

Field Type Required Description
signer.kennitala string Yes The signer's kennitala.
signer.signingMethod string Yes AudkenniApp or AudkenniSim; see Signing methods.
signer.phone string With AudkenniSim The signer's phone number. Leaving it out with AudkenniSim gives a 400.
signer.country string No Where the signer is signing from, at most 64 characters. Stored in the signature.
signer.city string No Where the signer is signing from, at most 64 characters. Stored in the signature.
signer.reason string No Reason for signing, at most 64 characters. Stored in the signature and shown on the stamp. Defaults to Undirritun.

Signing methods

Value Description
AudkenniApp The signer approves in the Auðkenni app.
AudkenniSim The signer approves with the electronic ID on their phone's SIM card. Requires signer.phone.

Signature image

For PDFs you can show the signature as a stamp on the page. Without signatureImage the signature is still in the document and valid, but nothing is drawn.

The simplest form lets esigna do everything: set positioning to Auto and leave out the image. esigna then draws its standard stamp (the signer's name from their certificate, kennitala, signing time and reason) and puts it in the first free spot on the page, starting from the chosen corner and avoiding existing signatures and the page's content. With several signers, each stamp lands in the next free spot.

Field Type Required Description
signatureImage.alignment string Yes The corner the stamp is measured from: TopLeft, TopRight, BottomLeft or BottomRight.
signatureImage.positioning string No Manual (default): place the stamp at originX/originY. Auto: esigna finds the first free spot.
signatureImage.originX, originY number With Manual The offset from the corner, in PDF points (1/72 inch; an A4 page is 595 × 842).
signatureImage.page int No The page, counting from 1. Defaults to 1. The page must exist.
signatureImage.imageB64 string No Your own image, in Base64. If left out, esigna draws its standard stamp.
signatureImage.imageType string With imageB64 Png or Jpeg.
signatureImage.width, height number With imageB64 The stamp's size in PDF points. The standard stamp is about 117 × 55.
signatureImage.zoom int No Percentage to scale the image inside the box. Defaults to 100.
signatureImage.scaling string No How the image fits the box: Stretch (default) fills it, ZoomAndCenter zooms to the nearer side and centres, Center centres without scaling.

The offsets are measured on the page as it is displayed, so a page rotated in the PDF is handled for you and the stamp is drawn upright. With Manual positioning, stamps on the same page must not overlap.

Advanced signature settings

These control the signature's cryptographic properties. Most integrations leave them out.

Field Type Description
advancedSignatureSettings.signatureLevel string B, T, Lt or Lta; see below. Defaults to Lt.
advancedSignatureSettings.signaturePacking string Enveloped (default), Enveloping or InternallyDetached (XML only).
Level Description
B Basic signature: valid while the signing certificate is valid and not revoked.
T Adds a timestamp proving the signature existed at a point in time.
Lt Also embeds the certificate and revocation data needed to validate the signature in the long term.
Lta Also timestamps that validation data, for long-term archiving.

Busy signal

Field Type Required Description
busySignal.id string (UUID) Yes The ID from POST /busy-signal.
busySignal.userId string No An ID from your system for whoever holds the signal, at most 512 characters. Returned in the 423 response to other requests.

Errors

Status When
400 with errors A field is missing or not valid, for example signer.phone with AudkenniSim, or originX with Manual.
400 "No free signature slot" Auto found no free spot on the page. Choose another page or position the stamp yourself.
400 "Signature page does not exist" signatureImage.page is not a page of the document.
400 "Document could not be read" The file is not a readable PDF. This is checked when signatureImage is sent; without it, an unreadable file fails later in the signing.
400 with errorCode Auðkenni refused the request at once, for example because no user has that kennitala. No pollingId is issued. See below.
404 The busy signal doesn't exist.
423 The busy signal is held by another signing request.

An Auðkenni refusal looks like this. errorCode is esigna's code; errorDescription carries Auðkenni's own code:

1
2
3
4
5
{
    "error": "access_denied",
    "errorCode": "UserNotFound",
    "errorDescription": "audkenni_404"
}

Step 3: Poll for the signed document

GET /api/v1/signing/{pollingId}

Poll until the signer finishes or the request ends. Wait at least interval seconds between polls.

Signed: 200 OK

1
2
3
{
    "signedFileB64": "JVBERi0xLjQK..."
}

signedFileB64 is the signed document in Base64. Polling is finished.

Not finished, or failed: 400 Bad Request

1
2
3
4
5
6
{
    "error": "authorization_pending",
    "errorCode": "AuthorizationPending",
    "errorDescription": "The end user has not authenticated.",
    "verificationCode": "1234"
}
Field Description
errorCode esigna's code. Drive your polling with this; see Polling.
error The OAuth error from Auðkenni, such as authorization_pending or access_denied.
errorDescription Auðkenni's description of the error, passed on as it comes: sometimes a code such as audkenni_404, sometimes a sentence. For information only; use errorCode in your logic.
verificationCode The 4-digit code to show the signer; see below.

Gone: 404 Not Found

A pollingId answers 404 once its request has finished (signed or failed). An abandoned one is cleared by the next start call made after it is 15 minutes old; until then, polling it gives a 400 such as ExpiredToken.

Verification code

The signer's phone shows a 4-digit code that your app must also show, so the signer can check they are approving the right request.

  • AudkenniSim: the code is in the start response (step 2).
  • AudkenniApp: the code only exists once the signer's phone answers, so it appears in a poll response.

Check both: show the start response's code if it isn't null, otherwise show the code as soon as a poll response has one.

Polling

errorCode What to do
AuthorizationPending Keep polling at the same interval.
SlowDown Keep polling, with the interval at least 5 seconds longer from now on.
anything else Stop: the request has ended. See Error codes.
(a 200) Stop: the signed document has arrived.

Warning

Never poll faster than interval.


Error codes

errorCode is esigna's code for what Auðkenni answered. It can be null when Auðkenni gives an error esigna doesn't know.

Error code Meaning
AuthorizationPending Still waiting for the signer. Keep polling.
SlowDown Still waiting; poll less often.
ExpiredToken The request expired. Start a new one.
AccessDenied The signer refused, or the request was denied.
TimeoutNoAnswer The signer didn't answer in time.
UserCancelled The signer cancelled.
UserNotFound Auðkenni has no user with that kennitala.
UserHasNoPhone The signer has no phone number registered with Auðkenni.
WrongVerificationCodeSelected The signer chose the wrong verification code in the app.
RequestInProgress Another request for this signer is in progress. Wait and try again.
PinBlocked The signer's PIN is blocked.
CardBlocked The signer's SIM electronic ID is blocked.
KeyNotFound No signing key was found for the signer.
CertificateNotFound No certificate was found for the signer.
CertificateIsRevoked The signer's certificate is revoked.
CertificateValidationError The signer's certificate could not be validated.
SignatureVerificationError The signature could not be verified.
InternalErrorCheckApp The Auðkenni app reported an error. The signer should check the app, for example update it.
CommunicationError Communication with the signer's phone failed. Try again.
SystemUnderMaintenance Auðkenni is under maintenance. Try again later.
InternalError Auðkenni had an internal error. Try again later.
AuthorizationPartyUnknown Auðkenni didn't recognise esigna's client. Contact esigna.
InvalidRequest, InvalidClient, InvalidGrant, UnauthorizedClient, UnsupportedGrantType Auðkenni refused the request itself. Contact esigna if it keeps happening.
IncorrectParameter, MissingParameter, WrongDataLength, UnableToHandleGivenData, IncompatibleVersion, UnsupportedProfile Auðkenni couldn't process the request. Contact esigna if it keeps happening.

Testing in the sandbox

The sandbox, https://sandbox-api.esigna.is, runs the Signing API without Auðkenni. Nobody signs with a real electronic ID: you create sandbox users to stand in for signers, and choose how each one answers.

Sandbox credentials are separate from production ones. Contact us and we email you a client ID and secret for the sandbox. Get tokens from the sandbox itself, POST https://sandbox-api.esigna.is/api/v1/auth/token, as described in Authorization.

Documents signed in the sandbox carry self-signed test certificates, so no validator accepts their signatures.

Sandbox users

These endpoints exist only in the sandbox: production answers 404 Not Found and leaves them out of its API reference. They take the same token as the signing endpoints and need no scope of their own.

POST /api/v1/sandbox/users

Request body

1
2
3
4
5
6
7
{
    "fullName": "Jón Jónsson",
    "kennitala": "0101302989",
    "phone": "6101234",
    "delayInSecondsBeforeAction": 0,
    "action": "Success"
}

Response: 200 OK

{
    "uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "fullName": "Jón Jónsson",
    "kennitala": "0101302989",
    "phone": "6101234",
    "delayInSecondsBeforeAction": 0,
    "action": "Success",
    "createdAt": "2026-10-12T12:00:00Z",
    "updatedAt": "2026-10-12T12:00:00Z"
}
Field Type Required Description
fullName string Yes At most 64 characters. The user's test certificates are made in this name, and it is the name on the signature stamp.
kennitala string Yes At most 10 characters. AudkenniApp requests find the user by this.
phone string Yes At most 16 characters. AudkenniSim requests find the user by this, so write it exactly as you send signer.phone.
delayInSecondsBeforeAction int No How long the user waits before answering, 0 to 300 seconds. Defaults to 0.
action string No How the user answers: Success (default) signs, Cancel refuses.

Each of your sandbox users needs its own kennitala and phone number.

GET /api/v1/sandbox/users lists your sandbox users, with the same fields except createdAt and updatedAt.

PUT /api/v1/sandbox/users/{uuid} changes one and returns 204 No Content. Send only the fields you want to change; a field left out or null keeps its value. The test certificates keep the name the user was created with, so a new fullName doesn't change the name on the stamp.

Status When
400 with errors A field is missing, too long or not one of the listed values, or the delay is outside 0–300.
404 PUT to a sandbox user that isn't yours.
422 "Sandbox user already exists" Another of your sandbox users has that kennitala or phone number.

How a sandbox user answers

Start and poll as in step 2 and step 3. The sandbox picks the sandbox user by signer.kennitala with AudkenniApp, and by signer.phone with AudkenniSim; with AudkenniSim, signer.kennitala is not compared with the user's. The verification code is always 1234.

With no delay and action: Success, the calls answer:

Call AudkenniApp AudkenniSim
Start 200, verificationCode is null 200, verificationCode is "1234"
1st poll 400 AuthorizationPending, verificationCode is "1234" 200 with signedFileB64
2nd poll 200 with signedFileB64 —

With AudkenniApp the first poll always answers AuthorizationPending, and the code first appears there, as in production.

The user's settings change what comes after the start call:

  • Delay: until delayInSecondsBeforeAction seconds have passed since the start call, polls answer AuthorizationPending. Use it to test your polling loop and how your app shows the code.
  • Cancel: once the delay has passed, the poll answers 400 with UserCancelled. Polling is over; the next poll answers 404.
1
2
3
4
5
6
{
    "error": "access_denied",
    "errorCode": "UserCancelled",
    "errorDescription": "mssp_401",
    "verificationCode": null
}

If none of your sandbox users matches, the request ends with UserNotFound, from the start call or the first poll. No other errorCode occurs in the sandbox.