Signing Worker Protocol

Errors and retries

SWP/1 error codes, refusing a request, when SignatureAPI retries, and the idempotency rules that stop a second signature

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.

Networks fail, and providers have bad days. This page covers how your worker reports a failure, what SignatureAPI does next, and how to make retries safe. The rule behind all of it: a retry must never produce a second signature.

Your worker…Answers withExample
Handled the request, and the act didn’t succeed200 and "status": "failed"The signer refused in the provider’s app
Refused the request itselfA non-200 statusThe token didn’t verify

Failed outcomes

{
    "status": "failed",
    "error": "access_denied",
    "error_description": "The signer refused in the app",
    "retryable": false
}
  • error is one of the codes below. If you need another one, prefix it with x_.
  • error_description is readable text. SignatureAPI keeps up to 500 characters of it.
  • retryable says whether trying the same act again can help. It defaults to false. Set it to true only when the act certainly didn’t happen.

Error codes

errorMeaningretryable
invalid_requestThe body or its fields are malformed or inconsistentfalse
access_deniedThe signer refused, or the provider denied the signerfalse
authorization_expiredThe approval or the waiting window ran outfalse
credential_unavailableNo credential can be found for this signerfalse
credential_expiredA short-lived certificate expired before signingfalse
digest_rejectedThe provider refused the digests: count, algorithm or policyfalse
idempotency_conflictThe key was reused with a different body, or is in flightfalse
temporarily_unavailableThe provider is down or throttlingtrue
internal_errorThe worker failedtrue

Refusing a request

Use a non-200 status only when you refuse the request itself, with a JSON body that has error and error_description:

StatusWhen
400The request is malformed, or the Idempotency-Key is missing
401The token doesn’t verify, or its connection token isn’t allowed. See Authentication.
404Unknown path
409An idempotency conflict
429You are throttling
5xxA worker fault
{ "error": "invalid_request", "error_description": "missing Idempotency-Key" }

Of the 4xx statuses, SignatureAPI retries only 429. On any other, the session fails with your error and error_description, and the signer can retry from the ceremony.

Retries and timeouts

credentialauthorizesign
Maximum silence on the connection, per HTTP attempt30 s30 s60 s
HTTP attempts on network errors, timeouts, 429 and 5xxUp to 3Up to 3Up to 3
Calls on failed with retryable: trueUp to 3Up to 31, never retried

The timeouts limit how long the connection may stay silent, not the total time of a call. A worker that keeps sending bytes doesn’t time out.

sign is never retried on a retryable outcome. A wrong retryable: true on credential costs a redundant call. On sign, it could cost a second signature.

SignatureAPI never follows redirects from your worker, and it rejects responses larger than 1 MiB. If your worker stays unavailable after the retries, the session fails and the signer can retry from the ceremony. Every retry carries a new token and the same Idempotency-Key.

Idempotency

Two values protect your worker from repeats:

  • The token’s jti changes on every HTTP attempt. It stops replays.
  • The Idempotency-Key stays the same for one act. It stops double execution.

The key is <session.id>/<operation>/<session.attempt>, for example sgs_YFobK1NDktAuz0wzplR4OP/sign/1. A request that resumes after a redirect is a separate act, so its key ends in /resume.

Idempotent handling is required for sign and recommended for credential and authorize. Follow four rules:

  1. Store only final outcomes. Store completed, or failed with retryable: false. Never store pending or a retryable failure: a repeat of that key runs again.
  2. Handle repeats. If an outcome is stored for the key, return it. If the key is running right now, or arrives with a different body, answer 409 with idempotency_conflict. Otherwise reserve the key, run the act, store the outcome if it is final, then release the key.
  3. Don’t start an act twice. Answer a repeat of an act you already started from your provider’s current state.
  4. Store before you release. Otherwise a retry that arrives between the two finds neither a stored outcome nor a reservation, and signs again.

To detect “same key, different body”, store the body’s SHA-256 next to the outcome. You already computed it while verifying the token.

The case these rules exist for: sign times out after your provider has already signed. The retry carries the same key, you return the stored signatures, and nothing is signed twice.

sequenceDiagram
    participant P as SignatureAPI
    participant W as Signing worker
    participant T as Trust service provider
    P->>W: POST /v1/sign (Idempotency-Key K)
    W->>T: Sign the digest
    T-->>W: Signature
    Note over W: Store the outcome under K
    W--xP: Response lost (timeout)
    P->>W: POST /v1/sign (same K, new token)
    Note over W: K has a stored outcome
    W-->>P: completed (stored signatures)