esigner.no

API-dokumentasjon

Bygg BankID-signering inn i ditt eget system. Last opp et dokument, opprett en signeringsforespørsel, og få beskjed via webhook når den er signert.

Kom i gang

  1. Lag en testnøkkel under Konto → Utvikler. Testnøkler (sk_test_…) signerer mot testmiljøet: ekte signeringsflyt, men ingen betaling, ingen kvote og ingen ekte penger.
  2. Last opp PDF-en med POST /v1/documents.
  3. Opprett signeringen med POST /v1/signature_requests.
  4. Registrer et webhook-endepunkt, så sier vi fra når dokumentet er ferdig signert.
  5. Bytt til en live-nøkkel (sk_live_…) når alt virker.

Autentisering

Send nøkkelen som bearer-token. Hver nøkkel har et sett tilganger (scopes) — den kommer ikke til endepunkter den ikke har fått tilgang til, og en nøkkel kan aldri lage en ny nøkkel.

Tilgangene velger du når du lager nøkkelen. Hvert endepunkt under sier hvilken det krever — mangler nøkkelen den, får du 403 insufficient_scope, og WWW-Authenticate sier hva som skulle til.

HTTP

Authorization: Bearer sk_live_...

Idempotens

Send en Idempotency-Key på alle POST-kall. Faller nettet ut midt i et kall, kan du trygt prøve på nytt med samme nøkkel: du får det opprinnelige svaret tilbake i stedet for en ny signeringsforespørsel. Samme nøkkel med et annet innhold gir 422 — det er en feil hos deg, og vi skjuler den ikke.

curl https://esigner.no/api/v1/signature_requests \
  -X POST \
  -H "Authorization: Bearer $ESIGNER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "documentId": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
    "signers": [
      {
        "name": "Ola Nordmann",
        "email": "ola@example.no"
      }
    ],
    "useSubscription": true,
    "deadlineDays": 14
  }'

Documents

POST/api/v1/documents

Upload the PDF(s) to be signed

Step one of every integration. Upload as multipart/form-data; the id you get back is what POST /signature_requests takes. An sk_test_ key produces a test-mode document, and a test document can only ever be signed by a test key.

Krever tilgang: documents:write

Headere

  • Idempotency-Keystring

    Makes the write retry-safe: a retry with the same key replays the original response instead of creating a second signature request. Same key + a different body is a 422.

Forespørsel multipart/form-data

  • ownerEmailstringpåkrevd

    Where the receipt and the signed PDF are sent. Must contain an @.

    post@firma.no

  • filesbinarypåkrevd

    The document. Repeat the field to upload several — they are merged in the order sent. PDF and Word (.docx) are accepted; a Word file is converted to PDF on upload. Older .doc, .odt and .rtf must be saved as .docx or PDF first. Per-file and per-request limits apply (25 MB per file, 10 files, by default).

  • templateSlugstring

    Optional. The /maler template the document was filled from — echoed on the completion surfaces.

    leieavtale

Svar 200Document

The uploaded document

  • iduuidpåkrevd

    Pass this as documentId when you create the signature request.

  • filenamestringpåkrevd

    Name of the first uploaded file.

    avtale.pdf

  • sizeBytesintegerpåkrevd

    Total size across every uploaded file.

    184320

  • attachmentsobject[]påkrevd

    One entry per uploaded PDF.

  • positionintegerpåkrevd

    0-based. Also the order the PDFs are merged in.

  • filenamestringpåkrevd

    vedlegg.pdf

  • sizeBytesintegerpåkrevd

    92160

Feilsvar

  • 400The upload failed validation — error carries the sentence, e.g. pdf or word (.docx) files only, <name> must be saved as .docx or pdf first, ownerEmail required.
  • 401unauthorized — no API key, or it has been revoked.
  • 403insufficient_scope — the key is valid but was not granted the scope this endpoint declares.
  • 409idempotency_key_in_progress — the first request with this Idempotency-Key is still running. Retry.
  • 413A file exceeded the per-file size limit, or converting it to PDF produced a file that does.
  • 422A Word file could not be converted to PDF — it is damaged or password-protected.
  • 429too_many_requests — rate limited. Retry after the Retry-After header.
  • 503Maintenance. New uploads and signature requests are paused; existing ones keep running.
curl https://esigner.no/api/v1/documents \
  -X POST \
  -H "Authorization: Bearer $ESIGNER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F "ownerEmail=post@firma.no" \
  -F "files=@avtale.pdf;type=application/pdf"

Svar

{
  "id": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
  "filename": "avtale.pdf",
  "sizeBytes": 184320,
  "attachments": [
    {
      "position": 0,
      "filename": "vedlegg.pdf",
      "sizeBytes": 92160
    }
  ]
}

Signature requests

POST/api/v1/signature_requests

Create a signature request

Sends the document to each signer for BankID/eID signing and returns the request in its initial state. Each signer is emailed (or texted) their own signing link. Pass useSubscription: true to draw the signature from the account's quota: the invitations go out immediately and no money moves. Without it the request is created in pending_payment and the response carries a payment_url — send the sender there, and the invitations go out once they've paid.

Krever tilgang: orders:write

Headere

  • Idempotency-Keystring

    Makes the write retry-safe: a retry with the same key replays the original response instead of creating a second signature request. Same key + a different body is a 422.

Forespørsel application/json

  • documentIduuidpåkrevd

    From POST /documents. Must belong to the same account as the key.

  • signersobject[]påkrevd

    1–20 signers, invited in the order given.

  • namestring

    Shown in the invitation. The legal identity comes from the eID, not from this.

    Ola Nordmann

  • emailstring

    ola@example.no

  • phonestring

    E.164 or a local number, e.g. +47 912 34 567. Only honoured while SMS delivery is enabled for the account.

  • useSubscriptionboolean

    Draw the signature from the account's quota instead of a payment. 409 quota_exceeded when the quota is spent.

    true

  • mode"merged" | "separate"

    Sign the uploads as one merged PDF (default) or as separate documents.

  • deadlineDaysinteger

    Days until the request expires. Defaults to the account's configured deadline.

    14

  • passwordstring

    Shared secret a signer must enter before the document is shown.

  • includePasswordInEmailboolean

    Whether that password is included in the invitation email. Defaults to true.

  • smsDeliveryboolean

    Also text the signing link. Free on subscription-covered requests, a surcharge on pay-per-use.

Svar 201SignatureRequest

The created signature request

  • iduuidpåkrevd

    The signature request id — pass it to GET /signature_requests/{id}.

  • object"signature_request"påkrevd
  • statusstringpåkrevd

    draft | pending_payment | in_progress | completed | expired | signing_failed. in_progress means the signers have been invited; signing_failed means the eID provider rejected the handoff and the request can be retried.

  • documentobjectpåkrevd

    The document being signed.

  • iduuidpåkrevd
  • filenamestringpåkrevd

    avtale.pdf

  • signersobject[]påkrevd

    In invitation order.

  • namestringpåkrevd| null

    Ola Nordmann

  • emailstringpåkrevd| null

    null for a signer reached only by SMS.

    ola@example.no

  • statusstringpåkrevd

    pending | notified | viewed | signed | declined | expired

  • signed_atstringpåkrevd| null

    ISO 8601, null until this signer has signed.

    2026-07-13T10:24:00.000Z

  • livemodebooleanpåkrevd

    false for signature requests created with an sk_test_ key.

  • createdstringpåkrevd

    ISO 8601.

    2026-07-13T10:12:00.000Z

  • payment_urlstringpåkrevd| null

    Where to send the sender to pay, while status is pending_payment. null on a subscription-covered request, and once the payment has gone through. Safe to hand to a human at any time: it resumes the checkout if the request is still unpaid and lands on the status page if it isn't.

    https://esigner.no/api/orders/3f7c1e42-9b0a-4d7…

Feilsvar

  • 400validation — the request body failed schema validation. issues carries the field-level detail.
  • 401unauthorized — no API key, or it has been revoked.
  • 403insufficient_scope, or cross_owner_document — the document belongs to another account.
  • 404not_found — no such document.
  • 409quota_exceeded, no_subscription, topup_in_progress, topup_cooldown, or idempotency_key_in_progress.
  • 422idempotency_key_reused — the same Idempotency-Key was already used with a different request body.
  • 429too_many_requests — rate limited. Retry after the Retry-After header.
  • 502signing_failed — the eID provider rejected the handoff. The document is left in signing_failed and the request can be retried.
  • 503Maintenance. New uploads and signature requests are paused; existing ones keep running.
curl https://esigner.no/api/v1/signature_requests \
  -X POST \
  -H "Authorization: Bearer $ESIGNER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "documentId": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
    "signers": [
      {
        "name": "Ola Nordmann",
        "email": "ola@example.no"
      }
    ],
    "useSubscription": true,
    "deadlineDays": 14
  }'

Svar

{
  "id": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
  "object": "signature_request",
  "status": "in_progress",
  "document": {
    "id": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
    "filename": "avtale.pdf"
  },
  "signers": [
    {
      "name": "Ola Nordmann",
      "email": "ola@example.no",
      "status": "signed",
      "signed_at": "2026-07-13T10:24:00.000Z"
    }
  ],
  "livemode": true,
  "created": "2026-07-13T10:12:00.000Z",
  "payment_url": "https://esigner.no/api/orders/3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11/pay"
}
GET/api/v1/signature_requests/{id}

Retrieve a signature request

Poll this for status, or subscribe to document.completed and skip the polling.

Krever tilgang: orders:read

Sti-parametere

  • iduuidpåkrevd

    The id returned by POST /signature_requests.

Svar 200SignatureRequest

The signature request

Vis feltene i SignatureRequest
  • iduuidpåkrevd

    The signature request id — pass it to GET /signature_requests/{id}.

  • object"signature_request"påkrevd
  • statusstringpåkrevd

    draft | pending_payment | in_progress | completed | expired | signing_failed. in_progress means the signers have been invited; signing_failed means the eID provider rejected the handoff and the request can be retried.

  • documentobjectpåkrevd

    The document being signed.

  • iduuidpåkrevd
  • filenamestringpåkrevd

    avtale.pdf

  • signersobject[]påkrevd

    In invitation order.

  • namestringpåkrevd| null

    Ola Nordmann

  • emailstringpåkrevd| null

    null for a signer reached only by SMS.

    ola@example.no

  • statusstringpåkrevd

    pending | notified | viewed | signed | declined | expired

  • signed_atstringpåkrevd| null

    ISO 8601, null until this signer has signed.

    2026-07-13T10:24:00.000Z

  • livemodebooleanpåkrevd

    false for signature requests created with an sk_test_ key.

  • createdstringpåkrevd

    ISO 8601.

    2026-07-13T10:12:00.000Z

  • payment_urlstringpåkrevd| null

    Where to send the sender to pay, while status is pending_payment. null on a subscription-covered request, and once the payment has gone through. Safe to hand to a human at any time: it resumes the checkout if the request is still unpaid and lands on the status page if it isn't.

    https://esigner.no/api/orders/3f7c1e42-9b0a-4d7…

Feilsvar

  • 401unauthorized — no API key, or it has been revoked.
  • 403insufficient_scope — the key is valid but was not granted the scope this endpoint declares.
  • 404not_found — no such signature request, or it belongs to another account. The two are deliberately indistinguishable.
  • 429too_many_requests — rate limited. Retry after the Retry-After header.
curl https://esigner.no/api/v1/signature_requests/3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11 \
  -H "Authorization: Bearer $ESIGNER_API_KEY"

Svar

{
  "id": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
  "object": "signature_request",
  "status": "in_progress",
  "document": {
    "id": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
    "filename": "avtale.pdf"
  },
  "signers": [
    {
      "name": "Ola Nordmann",
      "email": "ola@example.no",
      "status": "signed",
      "signed_at": "2026-07-13T10:24:00.000Z"
    }
  ],
  "livemode": true,
  "created": "2026-07-13T10:12:00.000Z",
  "payment_url": "https://esigner.no/api/orders/3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11/pay"
}

Events

GET/api/v1/events

List events

The same events delivered to your webhook endpoints, newest first. Useful for backfilling after downtime.

Krever tilgang: webhooks:readorders:read(én av dem holder)

Spørringsparametere

  • limitinteger

    Page size. Defaults to 25.

  • cursorstring

    Pass the previous page's next_cursor to continue. Returns events strictly older than it.

  • typestring

    Return only this event type.

Svar 200object

A page of events

  • dataEvent[]påkrevd
  • iduuidpåkrevd
  • object"event"påkrevd
  • typestringpåkrevd

    document.completed | document.signing_failed | order.captured

  • livemodebooleanpåkrevd

    false for activity produced by an sk_test_ key.

  • createdstringpåkrevd

    ISO 8601. Also the pagination cursor — pass it as cursor to get the next page.

    2026-07-13T10:24:00.000Z

  • dataobjectpåkrevd

    The same payload the webhook delivery carries for this event type.

  • next_cursorstringpåkrevd| null

    null on the last page. Otherwise pass it back as cursor.

Feilsvar

  • 401unauthorized — no API key, or it has been revoked.
  • 403insufficient_scope — the key is valid but was not granted the scope this endpoint declares.
  • 429too_many_requests — rate limited. Retry after the Retry-After header.
curl https://esigner.no/api/v1/events?limit=25&type=document.completed \
  -H "Authorization: Bearer $ESIGNER_API_KEY"

Svar

{
  "data": [
    {
      "id": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
      "object": "event",
      "type": "document.completed",
      "livemode": true,
      "created": "2026-07-13T10:24:00.000Z",
      "data": {}
    }
  ],
  "next_cursor": "string"
}

Full maskinlesbar spesifikasjon (OpenAPI 3.1): /api/v1/openapi.json

Feil

Alle feil svarer med JSON og en stabil kode i error. Kodene under er de du kan møte — bygg logikken på dem, ikke på teksten.

StatusKodeBetyr
400validationThe request body failed schema validation. issues carries the field-level detail.
401unauthorizedMissing, malformed or revoked API key.
403insufficient_scopeThe key is valid but lacks the scope the endpoint declares. WWW-Authenticate names what was needed.
403cross_owner_documentThe documentId belongs to a different account than the key.
404not_foundNo such resource — or it belongs to another account. Deliberately indistinguishable.
409quota_exceededThe subscription has no signatures left. remaining says how many there were.
409no_subscriptionuseSubscription: true but the account has no active subscription.
409topup_in_progressAn automatic top-up is already running. Retry shortly.
409topup_cooldownA renewal failed recently; automatic top-up is on a 24h cool-down.
409idempotency_key_in_progressThe first request with this Idempotency-Key hasn't finished. Retry.
413A single uploaded file exceeded the size limit.
422idempotency_key_reusedThe same Idempotency-Key was used with a different request body. That's a caller bug, so we surface it.
429too_many_requestsRate limited. Retry-After says how long to wait.
502signing_failedThe eID provider rejected the handoff. The document lands in signing_failed and can be retried.
503Maintenance. New uploads and signature requests are paused; existing ones keep running.

Feil

{
  "error": "quota_exceeded",
  "remaining": 0
}

Webhooks

Vi POSTer hendelser til adressen din og signerer hver leveranse med Esigner-Signature: t=<unix>,v1=<hmac>. HMAC-en er over <t>.<rå body>, så en fanget melding ikke kan spilles av på nytt med et annet tidsstempel. Verifiser alltid før du stoler på innholdet.

Feiler en levering, prøver vi på nytt med økende mellomrom (1 min → 6 t). Svarer endepunktet ikke over tid, deaktiverer vi det og sier fra på e-post — hendelsene går ikke tapt, og du kan sende dem på nytt.

Konvolutten

  • iduuidpåkrevd

    Also sent as the Esigner-Event-Id header. Dedupe on it — a retry reuses the id.

  • typestringpåkrevd

    Also sent as the Esigner-Event-Type header.

    document.completed

  • livemodebooleanpåkrevd
  • createdintegerpåkrevd

    Unix seconds. Note: GET /events returns this field as an ISO string instead.

    1784000000

  • dataobjectpåkrevd

    Per event type — see below.

Node.js

// Verifiser Esigner-Signature: t=<unix>,v1=<hmac>
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, header, secret) {
  const [t, v1] = header.split(",").map(p => p.split("=")[1]);
  const expected = createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest("hex");
  return timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}
document.completed

Every signer has signed. The signed PDF is available until downloadExpiresAt.

data

  • documentIduuidpåkrevd
  • orderIduuidpåkrevd

    The same id as SignatureRequest.id.

  • filenamestringpåkrevd

    avtale.pdf

  • mode"merged" | "separate"påkrevd

    Whether the uploads were signed as one merged PDF or individually.

  • downloadExpiresAtstringpåkrevd

    ISO 8601. Fetch the signed PDF before this.

    2026-08-13T10:24:00.000Z

  • signersobject[]påkrevd
  • namestringpåkrevd| null

    Ola Nordmann

  • emailstringpåkrevd| null

    ola@example.no

  • signedAtstringpåkrevd| null

    ISO 8601.

    2026-07-13T10:24:00.000Z

document.completed

{
  "id": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
  "type": "document.completed",
  "livemode": true,
  "created": 1784000000,
  "data": {
    "documentId": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
    "orderId": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
    "filename": "avtale.pdf",
    "mode": "merged",
    "downloadExpiresAt": "2026-08-13T10:24:00.000Z",
    "signers": [
      {
        "name": "Ola Nordmann",
        "email": "ola@example.no",
        "signedAt": "2026-07-13T10:24:00.000Z"
      }
    ]
  }
}
document.signing_failed

The eID provider rejected the handoff. The sender can retry the document.

data

  • documentIduuidpåkrevd
  • orderIduuidpåkrevd
  • messagestringpåkrevd

    Why the eID provider rejected the handoff.

    signature order rejected

document.signing_failed

{
  "id": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
  "type": "document.signing_failed",
  "livemode": true,
  "created": 1784000000,
  "data": {
    "documentId": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
    "orderId": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
    "message": "signature order rejected"
  }
}
order.captured

A pay-per-use payment was captured. Not emitted for subscription-covered requests.

data

  • orderIduuidpåkrevd
  • documentIduuidpåkrevd
  • amountOreintegerpåkrevd

    Captured amount in minor units (øre), VAT included.

    3625

order.captured

{
  "id": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
  "type": "order.captured",
  "livemode": true,
  "created": 1784000000,
  "data": {
    "orderId": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
    "documentId": "3f7c1e42-9b0a-4d7e-8c31-2a5f6b9d0e11",
    "amountOre": 3625
  }
}

AI-assistenter (MCP)

Skal en AI-assistent opprette signeringene i stedet, har vi en MCP-server: esigner.no er en godkjent app i ChatGPT, og adressen kan legges inn som egen tilkobling i Claude og andre MCP-klienter. Se hvordan du kobler til.

Står du fast? Ta kontakt — vi svarer gjerne på integrasjonsspørsmål.