Hosted and external signing session orchestration.
GET/signing/client-sessionsRequires authentication
Find a participant's client signing session by contract
For hosts that know the contract (and maybe the participant) but not the
session id. Returns the participant's newest open session, else its
newest session of any status.
- **Signer token**: pinned to its own contract and participant;
`participantId` may be omitted. Any other contract or participant returns `403`.
- **Machine key** (`contracts:read`): when `participantId` is omitted and
the contract has exactly one pending `integrator_managed` signer, that
signer is used; otherwise `409 PARTICIPANT_REQUIRED`.
Parameters
| Name | In | Type | Required |
|---|
| contractId | query | string | Yes |
|---|
| participantId | query | string | No |
|---|
| X-SDK-Platform | header | string | No |
|---|
| X-SDK-Version | header | string | No |
|---|
Responses
200Session detailsdefaultStandardized API error
POST/signing/client-sessionsRequires authentication
Create client-side signing session (advanced/enterprise)
Parameters
| Name | In | Type | Required |
|---|
| Idempotency-Key | header | string | No |
|---|
Request body
contractIdstringrequiredparticipantIdstringrequiredproviderKeystringrequiredproviderTypestringchannelstringsessionTtlMinutesinteger
Responses
201Session createddefaultStandardized API error
GET/signing/client-sessions/{sessionId}Requires authentication
Get client signing session (advanced/enterprise)
Callable with a machine key or with the signer token that the access
route issued for **this** session (SigningKit). A signer token for any
other session gets `403 AUTH_SIGNER_TOKEN_SESSION_MISMATCH`; once the
session has ended, or its contract or participant can no longer sign,
the token gets `410 SESSION_EXPIRED`. Signer tokens are
limited to 60 requests/min each. SDK callers send `X-SDK-Platform` and
`X-SDK-Version`; an SDK older than the organization minimum gets
`426 SDK_OUTDATED`. Every response carries `X-Request-Id`.
**Encrypted channel (SigningKit v2).** For signer tokens this route, `package`,
`identity-session[/complete]`, `identity-checks`, `media`, `registration-request`,
`complete`, `cancel` and `provider-submission` are sealed routes: after
`POST .../handshake` they take and return sealed envelopes. Under the
organization policy `required`, this route answers metadata only
(`id`, `status`, `contractId`, `participantId`, `expiresAt`, `secureChannel`,
`sdk.channelKeys`) before a handshake, and plain requests on the other sealed
routes get `426 CHANNEL_REQUIRED`. Machine keys are never sealed.
Parameters
| Name | In | Type | Required |
|---|
| sessionId | path | string | Yes |
|---|
| X-SDK-Platform | header | string | No |
|---|
| X-SDK-Version | header | string | No |
|---|
Responses
200Session detailsdefaultStandardized API error
POST/signing/client-sessions/{sessionId}/cancelRequires authentication
Cancel client signing session (advanced/enterprise)
Callable with a machine key or with the signer token that the access
route issued for **this** session (SigningKit). A signer token for any
other session gets `403 AUTH_SIGNER_TOKEN_SESSION_MISMATCH`; once the
session has ended, or its contract or participant can no longer sign,
the token gets `410 SESSION_EXPIRED`. Signer tokens are
limited to 60 requests/min each. SDK callers send `X-SDK-Platform` and
`X-SDK-Version`; an SDK older than the organization minimum gets
`426 SDK_OUTDATED`. Every response carries `X-Request-Id`.
Parameters
| Name | In | Type | Required |
|---|
| sessionId | path | string | Yes |
|---|
| X-SDK-Platform | header | string | No |
|---|
| X-SDK-Version | header | string | No |
|---|
| Idempotency-Key | header | string | No |
|---|
Request body
reasonstringstagestringJourney step the signer cancelled at (SigningKit); stored as `cancelStage` and published in the webhook.
Responses
200Session cancelleddefaultStandardized API error
POST/signing/client-sessions/{sessionId}/completeRequires authentication
Submit signed artifact to complete client signing (advanced/enterprise)
Callable with a machine key or with the signer token that the access
route issued for **this** session (SigningKit). A signer token for any
other session gets `403 AUTH_SIGNER_TOKEN_SESSION_MISMATCH`; once the
session has ended, or its contract or participant can no longer sign,
the token gets `410 SESSION_EXPIRED`. Signer tokens are
limited to 60 requests/min each. SDK callers send `X-SDK-Platform` and
`X-SDK-Version`; an SDK older than the organization minimum gets
`426 SDK_OUTDATED`. Every response carries `X-Request-Id`.
Parameters
| Name | In | Type | Required |
|---|
| sessionId | path | string | Yes |
|---|
| X-SDK-Platform | header | string | No |
|---|
| X-SDK-Version | header | string | No |
|---|
| Idempotency-Key | header | string | No |
|---|
Request body
sessionNoncestringrequiredinputHashEchostringrequiredidentityCheckIdsarrayIdentity checks the device considers part of this completion (SigningKit). The server gates on its own records; while a required kind has no passed record the call answers `409 IDENTITY_REQUIRED`.journeyIdstringSDK journey id, stored on the session for the debugger (SigningKit).artifactobjectrequiredproviderReceiptobjectWhen the session has a signing-provider registration (SigningKit v2), must carry `transactionId` and `tcspTransactionId` from the provider; the signed PDF's PAdES signature is then verified (against the configured certification-provider trust anchors when present).registrationRefstringRequired when the session has a signing-provider registration (SigningKit v2); the `registrationRef` of the registration request the device used.
Responses
200Completion accepted400`REQUEST_BODY_INVALID` — the body is not parseable JSON or multipart413`PAYLOAD_TOO_LARGE` — the request body exceeds the route limitdefaultStandardized API error
POST/signing/client-sessions/{sessionId}/eventsRequires authentication
Ship SDK telemetry events for the session (SigningKit)
Batch of up to 100 events / 512 KB validated against the SDK's
`events.schema.json`. A batch is never refused for one bad event: the
`202` response reports `accepted` and `dropped[]` (reasons `invalid`,
`context_mismatch`, `budget_exceeded`, `write_failed`). `eventId` is
idempotent. Signer tokens get 600 events per minute and every
organization has a daily budget; both limits answer `429` with
`Retry-After`. Events are redacted server-side (allow-listed attributes,
200-char strings, no base64, no tokens) and expire after the
organization's telemetry retention.
Parameters
| Name | In | Type | Required |
|---|
| sessionId | path | string | Yes |
|---|
| X-SDK-Platform | header | string | No |
|---|
| X-SDK-Version | header | string | No |
|---|
| Idempotency-Key | header | string | No |
|---|
Responses
202Batch accepted (`data.accepted`, `data.dropped[]`)400`REQUEST_BODY_INVALID` — the body is not parseable JSON or multipart413`PAYLOAD_TOO_LARGE` — the request body exceeds the route limitdefaultStandardized API error
POST/signing/client-sessions/{sessionId}/handshakeRequires authentication
Encrypted channel key agreement (SigningKit v2)
Signer token only (machine keys never use the channel). The SDK sends an
ephemeral P-256 public key and a 32-byte nonce; the server answers its own
ephemeral key, nonce and an Ed25519 signature over the transcript with the
key published at `GET /api/public/sdk/keys`. Both sides derive per-direction
AES-256-GCM keys with HKDF-SHA256. Afterwards the sealed routes take and
return `application/vnd.platform.sealed+json` envelopes
(`{ v, kid, seq, nonce, ct }`; bodyless and multipart requests carry the
envelope in `X-Channel-Envelope`). Byte-level rules and known-answer
vectors: the SigningKit API contract §14 and `test-vectors/channel-v1.json`.
The organization's `sealedChannel` policy decides whether plain requests are
still accepted (`required` gives `426 CHANNEL_REQUIRED`). 10 handshakes per token
per minute. A new handshake replaces the keys.
Parameters
| Name | In | Type | Required |
|---|
| sessionId | path | string | Yes |
|---|
| X-SDK-Platform | header | string | No |
|---|
| X-SDK-Version | header | string | No |
|---|
| Idempotency-Key | header | string | No |
|---|
Request body
clientPubstringrequiredBase64 of the 65-byte uncompressed P-256 ephemeral public key.clientNoncestringrequiredBase64 of 32 random bytes.sdkobject
Responses
201Handshake accepted (`data.handshake = { kid, serverPub, serverNonce, sig, expiresAt }`)defaultStandardized API error
POST/signing/client-sessions/{sessionId}/identity-checksRequires authentication
Record an identity check for the session's signer (SigningKit)
Posted by the mobile SDK after each identity step (`otp`, `document`,
`biometrics`), or by a machine key on the signer's behalf. The server
assigns `attempt`, `receivedAt`, `integrity` and `ipAddress`, re-runs the
document field rules (it may force `outcome: failed` with
`serverDecision.code` `DOCUMENT_REJECTED` or `IDENTITY_MISMATCH`), encrypts
the payload at rest and returns the participant's updated `identity`
block. Records are append-only. Send `Idempotency-Key`
(`<journeyId>:<step>:<attempt>`) so a retried post replays the first record.
Parameters
| Name | In | Type | Required |
|---|
| sessionId | path | string | Yes |
|---|
| X-SDK-Platform | header | string | No |
|---|
| X-SDK-Version | header | string | No |
|---|
| Idempotency-Key | header | string | No |
|---|
Request body
kindstringrequiredproviderobjectrequiredreferenceIdstringrequiredoutcomestringrequiredsourcestringrecordedAtstringrequiredtimezonestringjourneyIdstringdeviceobjectDevice snapshot (platform, osVersion, model, deviceId, appId, appVersion, sdkVersion, providerSdkVersions, locale, networkType, rooted, emulator).locationobjectmediaarrayruleResultsarrayerrorobjectpayloadobjectrequiredProvider result (≤ 256 KB). Encrypted at rest; only its SHA-256 (`integrity`) is stored in clear.
Responses
201Identity check recorded (`data.identityCheck`)400`REQUEST_BODY_INVALID` — the body is not parseable JSON or multipart413`PAYLOAD_TOO_LARGE` — the request body exceeds the route limitdefaultStandardized API error
POST/signing/client-sessions/{sessionId}/identity-sessionRequires authentication
Start (or resume) the provider-run identity session (SigningKit v2)
Signer token only; a sealed route. The server creates the eKYC provider
session server-to-server for the participant's person record and returns
only what the provider's mobile SDK needs for this journey
(`baseUrl`, `bundleKey`, `sessionToken`, `language`, `documentType`,
`userReferenceId`) as `data.identitySession`. Idempotent per signing
session: an open identity session is returned again (200); once the
participant is verified the answer is 200 with `status: completed` and no
hand-off, so the SDK skips the step. The device never receives the
provider's server credentials and never sends identity data back.
Parameters
| Name | In | Type | Required |
|---|
| sessionId | path | string | Yes |
|---|
| X-SDK-Platform | header | string | No |
|---|
| X-SDK-Version | header | string | No |
|---|
| Idempotency-Key | header | string | No |
|---|
Responses
200Existing identity session (or already verified)201Identity session created (`data.identitySession`)defaultStandardized API error
POST/signing/client-sessions/{sessionId}/identity-session/completeRequires authentication
Report that the device finished the provider flow (SigningKit v2)
Signer token only; a sealed route. `outcome` is what the provider's SDK
reported (`approved`, `rejected`, `exited`, `error`) and decides nothing:
the server fetches the result and the images from the provider,
evaluates its rules (a document number different from the participant's
registered one is a hard block) and records the identity checks. `exited`
cancels the identity session. Answers `202` with
`data.identitySession = { id, status, decision, rejection }`; the SDK then
polls the session until `identity.status` is `completed` or `failed`.
Parameters
| Name | In | Type | Required |
|---|
| sessionId | path | string | Yes |
|---|
| X-SDK-Platform | header | string | No |
|---|
| X-SDK-Version | header | string | No |
|---|
| Idempotency-Key | header | string | No |
|---|
Request body
identitySessionIdstringoutcomestringrequireddeviceIDstringproviderCodestringflowLogarray
Responses
202Report accepted (`data.identitySession`)defaultStandardized API error
POST/signing/client-sessions/{sessionId}/mediaRequires authentication
Upload identity media for the next identity check (SigningKit)
Multipart upload. Field names are the media kinds: `id_front`, `id_back`,
`selfie` (JPEG/PNG/WebP, 2 MB each) and `liveness_frames` (repeated; up
to 60 files, 100 KB each, 6 MB in total). Frames are rendered server-side
into one animated WebP returned as `liveness_animation`. The response
lists opaque `storageRef`s to reference from the identity check; media is
stored encrypted and is never returned to the device. Signer tokens are
limited to 20 uploads per minute (`429` with `Retry-After`).
Parameters
| Name | In | Type | Required |
|---|
| sessionId | path | string | Yes |
|---|
| X-SDK-Platform | header | string | No |
|---|
| X-SDK-Version | header | string | No |
|---|
| Idempotency-Key | header | string | No |
|---|
Responses
201Stored media (`data.media[]`)400`REQUEST_BODY_INVALID` — the body is not parseable JSON or multipart413`PAYLOAD_TOO_LARGE` — the request body exceeds the route limitdefaultStandardized API error
GET/signing/client-sessions/{sessionId}/packageRequires authentication
Retrieve source signing package (advanced/enterprise)
Callable with a machine key or with the signer token that the access
route issued for **this** session (SigningKit). A signer token for any
other session gets `403 AUTH_SIGNER_TOKEN_SESSION_MISMATCH`; once the
session has ended, or its contract or participant can no longer sign,
the token gets `410 SESSION_EXPIRED`. Signer tokens are
limited to 60 requests/min each. SDK callers send `X-SDK-Platform` and
`X-SDK-Version`; an SDK older than the organization minimum gets
`426 SDK_OUTDATED`. Every response carries `X-Request-Id`.
When the participant has identity fields (SigningKit v2), the package
waits for identity verification (`409 IDENTITY_SESSION_NOT_COMPLETED`)
and then carries the document re-rendered with the verified values;
`inputHash` is that render's hash.
Parameters
| Name | In | Type | Required |
|---|
| sessionId | path | string | Yes |
|---|
| X-SDK-Platform | header | string | No |
|---|
| X-SDK-Version | header | string | No |
|---|
| mode | query | string | No |
|---|
Responses
200Signing source packagedefaultStandardized API error
POST/signing/client-sessions/{sessionId}/provider-submissionRequires authentication
Report the signed contract's filing with the certification provider (SigningKit v2)
Signer token only; a sealed route; the one write a completed session
accepts. After `/complete` the session's `providerSubmission.status` is
`pending`; the device reports `submitted` or `failed` (with the
provider's code) after the registration SDK's contract submission.
`submitted` is terminal and idempotent; a receipt that differs from the
one accepted at completion is refused. Webhooks
`contract.provider_submission.pending|completed|failed`.
Parameters
| Name | In | Type | Required |
|---|
| sessionId | path | string | Yes |
|---|
| X-SDK-Platform | header | string | No |
|---|
| X-SDK-Version | header | string | No |
|---|
| Idempotency-Key | header | string | No |
|---|
Request body
registrationRefstringrequiredtransactionIdstringrequiredtcspTransactionIdstringrequiredsubmittedAtstringrequiredstatusstringrequirederrorobject
Responses
200Recorded (`data.providerSubmission = { status, attempts, submittedAt }`)defaultStandardized API error
POST/signing/client-sessions/{sessionId}/registration-requestRequires authentication
Signing-provider registration request (SigningKit v2)
Signer token only; a sealed route; 5 per minute per token. Requires the
organization's external signing provider to be enabled, the journey's app
registered and active (`X-SDK-App-Id`, `X-SDK-App-Name`, read by the SDK
from its host package), an approved identity session, an allowed ID type
and the signer's mobile number. The server mints the `TransactionID`
per attempt, builds the provider payload from the server-held identity
profile and images, e-seals it (`signedRequestData`, CAdES-B-B over the
canonical JSON) and returns `data.registrationRequest` with the
registration SDK's 20 parameters (`registration`), the certification
provider block (`tcsp`), the signing service block (`signing`, including
the OTP `channelId`) and `signer.nationalId`. Credentials are for one
journey, in memory only. `?purpose=submission` (completed session with an
open provider submission) returns only `registrationRef`, `tcsp` and
`expiresAt`. `501 SIGNING_PROVIDER_REGISTRATION_NOT_CONFIGURED` only when
no e-seal certificate is active; `503 SIGNING_PROVIDER_CERT_EXPIRED`
(with `Retry-After`) when it expired.
Parameters
| Name | In | Type | Required |
|---|
| sessionId | path | string | Yes |
|---|
| X-SDK-Platform | header | string | No |
|---|
| X-SDK-Version | header | string | No |
|---|
| X-SDK-App-Id | header | string | No |
|---|
| X-SDK-App-Name | header | string | No |
|---|
| purpose | query | string | No |
|---|
| Idempotency-Key | header | string | No |
|---|
Responses
201Registration request built (`data.registrationRequest`)defaultStandardized API error
POST/signing/contracts/{contractId}/external/dispatchRequires authentication
Trigger external organization signing dispatch (advanced)
Parameters
| Name | In | Type | Required |
|---|
| contractId | path | string | Yes |
|---|
| Idempotency-Key | header | string | No |
|---|
Responses
200Dispatch resultdefaultStandardized API error
GET/signing/contracts/{contractId}/external/requestsRequires authentication
List external signing requests for contract (advanced)
Parameters
| Name | In | Type | Required |
|---|
| contractId | path | string | Yes |
|---|
Responses
200Request listdefaultStandardized API error
POST/signing/contracts/{contractId}/participants/{participantId}/accessRequires authentication
Create signer access (hosted link, or client session + signer token)
Mints access for one signer participant (`action="sign"`). What you get
depends on the participant's `executionChannel`:
- **`platform_hosted`** — the hosted signing URL and link token
(`signingUrl`, `token`). Unchanged; the request body is ignored.
- **`integrator_managed`** (SigningKit / mobile SDK) — opens, or reuses
the open, client signing session for the participant and returns
`sessionId` + `signerToken`. Hand both to your app: the signer token
authenticates the device on
`/signing/client-sessions/{sessionId}`, `/package`, `/complete` and
`/cancel` for **that session only** (`signerBearer`). It expires at
`expiresAt` (default 120 min, max 24 h, never after the session). Once
the session completes, is cancelled or fails the token stays valid for
reads and telemetry only (the SDK replays `complete` and ships its final
events after the fact); expiry revokes it. Calling again returns the same
session with a fresh token.
- any other channel returns `409 SIGNING_EXECUTION_CHANNEL_MISMATCH`.
**Requires the `contracts:send` scope.** It previously accepted
`contracts:read` OR `contracts:send`, so a read-only key could mint a
signing link. The token this returns authenticates the participant and
produces a binding signature — issuing it is not a read. A key without
`contracts:send` receives `403 PERMISSION_DENIED`. A signer token cannot
call this route.
Parameters
| Name | In | Type | Required |
|---|
| contractId | path | string | Yes |
|---|
| participantId | path | string | Yes |
|---|
| Idempotency-Key | header | string | No |
|---|
Request body
providerKeystringClient signing provider for the session. Defaults to `generic_pdf_signer`.sessionTtlMinutesintegertokenTtlMinutesintegerSigner token lifetime. Defaults to 120; never extends past the session's expiry.
Responses
200Signer access. `data.access` is `HostedSignerAccess` for a
`platform_hosted` participant, `ClientSignerAccess` for an
`integrator_managed` one.defaultStandardized API error
GET/signing/external/providersRequires authentication
List external signing providers
Responses
200External provider listdefaultStandardized API error
GET/signing/external/requests/{requestId}Requires authentication
Get external signing request details (advanced)
Parameters
| Name | In | Type | Required |
|---|
| requestId | path | string | Yes |
|---|
Responses
200Request detailsdefaultStandardized API error
POST/signing/external/requests/{requestId}/retryRequires authentication
Retry external signing request dispatch (advanced)
Parameters
| Name | In | Type | Required |
|---|
| requestId | path | string | Yes |
|---|
| Idempotency-Key | header | string | No |
|---|
Responses
200Retry resultdefaultStandardized API error
GET/signing/providersRequires authentication
List client signing providers from registry
Parameters
| Name | In | Type | Required |
|---|
| providerType | query | string | No |
|---|
Responses
200Provider listdefaultStandardized API error
POST/signing/sdk-eventsRequires authentication
Ship pre-session SDK telemetry events (SigningKit)
Events emitted before the SDK has resolved its session (security gate,
journey start) carry `sessionId: null` and are posted here with the
contract and participant. They are attached to the participant's session
once it exists (24 hour window). A signer token may only post about its
own contract and participant; a machine key about its own contracts.
Same limits and response shape as the session route.
Parameters
| Name | In | Type | Required |
|---|
| X-SDK-Platform | header | string | No |
|---|
| X-SDK-Version | header | string | No |
|---|
| Idempotency-Key | header | string | No |
|---|
Request body
contractIdstringrequiredparticipantIdstringeventsarrayrequired
Responses
202Batch accepted (`data.accepted`, `data.dropped[]`)defaultStandardized API error