Signing Worker Protocol

Signing Worker Protocol (SWP/1)

How SignatureAPI and a signing worker produce one qualified signature, the shape every request and response shares, and what SignatureAPI checks

Private beta. Qualified signatures are available on request. Request access and we enable them for your account. SWP/1 is in beta and may change.

The Signing Worker Protocol (SWP/1) is the contract between SignatureAPI and a signing worker. A worker serves four endpoints under its base URL:

EndpointPurpose
POST /v1/credentialReturn the signer’s certificate chain
POST /v1/authorizeGet the signer’s approval for exactly these digests
POST /v1/signReturn one signature per digest
GET /v1/healthReport status and list the accounts the worker accepts

Every POST is authenticated with a token that SignatureAPI signs for that one request.

Lifecycle of a signature

One signer’s turn to sign is a signing session. In a session, SignatureAPI calls the three operations in this order:

  1. credential. Which certificate will this signer sign with?
  2. authorize. The signer approves signing exactly these digests.
  3. sign. Produce the signature values.

The order follows from how PDF signatures work. A PAdES signature (the standard format for signatures inside a PDF) embeds a hash of the signing certificate, so SignatureAPI needs the certificate before it can compute the digest. A qualified remote signature binds the signer’s approval to the exact digests, so the digests are final before authorize.

Each operation answers completed, pending or failed. A pending answer means the signer must do something at the provider first, such as identify or approve. See Pending steps.

sequenceDiagram
    participant B as Signer's browser
    participant P as SignatureAPI
    participant W as Signing worker
    participant T as Trust service provider
    B->>P: Finish the ceremony
    P->>W: POST /v1/credential
    W-->>P: pending (action_url)
    P-->>B: Redirect to action_url
    B->>T: Identify
    T-->>B: Redirect to callback_url?code=...
    B->>P: GET callback_url
    P->>W: POST /v1/credential (callback.code)
    W-->>P: completed (certificate chain)
    Note over P: Compute the digest
    P->>W: POST /v1/authorize (digests)
    W-->>P: completed (authorization reference)
    P->>W: POST /v1/sign
    W->>T: Sign the digest
    W-->>P: completed (signatures)
    Note over P: Verify, then embed in the PDF

A worker that needs nothing from the signer answers completed three times in a row, and the browser never leaves the ceremony. If any step fails, the session fails, and the signer can retry from the ceremony in a new session.

Common request fields

Every POST body carries these fields, plus the operation’s own. Binary values are standard base64 with padding. Timestamps are RFC 3339.

{
    "protocol": "swp/1",
    "session": { "id": "sgs_YFobK1NDktAuz0wzplR4OP", "attempt": 1 },
    "scheme": "eu_eidas",
    "signer": {
        "id": "re_00ZeZjRaXCgT30n1eBx53N",
        "name": "Jane Doe",
        "email": "jane.doe@example.com"
    },
    "callback_url": "https://api.signatureapi.com/v1/signing-sessions/callback?state=..."
}
FieldDescription
protocolAlways swp/1.
session.idThe signing session. Key anything you store by it. A retry from the ceremony starts a new session with a new ID.
session.attemptStarts at 1. Part of the Idempotency-Key.
schemeThe legal framework of the signature. Always eu_eidas today.
signer.idThe recipient ID. The token’s sub claim carries the same value.
signer.nameThe signer’s name, as set on the envelope.
signer.emailThe signer’s email address, or "" when the recipient has none.
callback_urlWhere you send the signer’s browser when an interactive step ends. Treat it as opaque.
callback.codePresent only when the request resumes after a redirect. The single-use code your worker added to callback_url.

The signer’s name and email are personal data. Log IDs, not request bodies.

Responses

Answer every operation with HTTP 200 and one of three bodies:

{ "status": "completed", ...result }

{ "status": "pending", "action_url": "https://signer.example.com/consent?ticket=...", "expires_at": "2026-08-24T16:45:00Z" }

{ "status": "failed", "error": "access_denied", "error_description": "The signer refused in the app", "retryable": false }
  • completed carries the operation’s result, described on each endpoint page.
  • pending is allowed on credential and authorize only. See Pending steps.
  • failed carries an error code.

Use a non-200 status only when you refuse the request itself.

What SignatureAPI checks

Before a signature reaches the deliverable, SignatureAPI runs three checks. If one fails, the session fails with that check’s name.

  • signature. The signature verifies over the exact digest SignatureAPI sent, with the public key of the signer certificate.
  • chain. Every certificate in the chain was valid at signing time, and each one is signed by the next.
  • scheme. The session’s scheme is eu_eidas.

SignatureAPI always verifies against the chain it saved when it computed the digest: the one from credential, or the connection’s static certificate. It never reads certificates from the sign response.

SignatureAPI does not check that the certificate is qualified. Making sure your worker signs with qualified certificates from a qualified provider is the worker owner’s responsibility.

Requirements at a glance

  • Public HTTPS. Serve the worker on port 443 with a certificate from a public certificate authority. SignatureAPI connects only to public addresses.
  • No redirects. Answer on the URL SignatureAPI called. SignatureAPI never follows a redirect.
  • Verify every token, including the body hash against the raw bytes. See Authentication.
  • Accept only listed accounts. List your accounts’ connection tokens in /v1/health and refuse tokens for any other account.
  • Single-use codes. Any code you add to callback_url works once.
  • Sign once. Never sign twice for one Idempotency-Key.
  • Small responses. SignatureAPI rejects responses larger than 1 MiB.