Tvarka ATK API Signings API
Create and follow a signing.
Create and follow a signing.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/tvarka-signings-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Tvarka Sign Signings API
version: 1.2.1
x-status: 'Live and publicly reachable at https://sign-api.tvarka.pro since 2026-08-27, on both production boxes and behind Cloudflare. The first production tenant was onboarded 2026-08-28 and the launch gate ceremony ran the same day: a production signing ordered through this API was signed with a real eID (Mobile-ID) and validated QUALIFIED_VALID, and webhook delivery was exercised the same day against an external production receiver with the HMAC signature and idempotency key verified on arrival.'
description: 'Machine channel for Tvarka Sign: post a document and a list of signers,
and Tvarka runs its ordinary qualified signing ceremony for each of them.'
contact:
name: Tvarka
email: info@tvarka.pro
url: https://tvarka.pro
servers:
- url: https://sign-api.tvarka.pro
description: Production and sandbox (selected by the key)
security:
- bearerAuth: []
tags:
- name: Signings
description: Create and follow a signing.
paths:
/v1/signings:
get:
tags:
- Signings
operationId: listSignings
summary: List signings
parameters:
- name: status
in: query
schema:
$ref: '#/components/schemas/SigningStatus'
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 50
- name: startingAfter
in: query
schema:
type: string
format: uuid
responses:
'429':
$ref: '#/components/responses/Problem'
'200':
description: A newest-first page of signings.
content:
application/json:
schema:
$ref: '#/components/schemas/SigningList'
'400':
$ref: '#/components/responses/Problem'
'401':
$ref: '#/components/responses/Problem'
post:
tags:
- Signings
operationId: createSigning
summary: Create a signing
description: 'Stores the document, opens one ceremony per signer, and returns the
ceremony URLs (or emails them, with `delivery: email`).
All signers sign the same document and are unordered: whoever opens
their link first signs first, and each subsequent signer signs the
output the previous one produced. Two signers never sign at the same
moment - the ceremony serializes them.'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateSigningRequest'
responses:
'201':
description: The signing was created and every signer has a live ceremony.
content:
application/json:
schema:
$ref: '#/components/schemas/Signing'
'400':
$ref: '#/components/responses/Problem'
'401':
$ref: '#/components/responses/Problem'
'402':
description: 'The tenant cannot pay for the ceremony: no payment rail, an
overdue invoice, or a tripped usage ceiling. Nothing was
created and nobody was invited. `recoveryUrl` names the web
page where the tenant can fix it.
'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
'403':
$ref: '#/components/responses/Problem'
'422':
description: 'A well-formed request for a container that cannot take another
signature - unsigned, already finalized, or structurally
unsound. Not billed.
'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
'429':
$ref: '#/components/responses/Problem'
'503':
$ref: '#/components/responses/Problem'
/v1/signings/{signingId}:
get:
tags:
- Signings
operationId: getSigning
summary: Get a signing
description: Current state of the signing and each of its signers.
parameters:
- $ref: '#/components/parameters/SigningId'
responses:
'401':
$ref: '#/components/responses/Problem'
'429':
$ref: '#/components/responses/Problem'
'200':
description: The signing.
content:
application/json:
schema:
$ref: '#/components/schemas/Signing'
'404':
$ref: '#/components/responses/Problem'
patch:
tags:
- Signings
operationId: updateSigning
summary: Update a live signing deadline
parameters:
- $ref: '#/components/parameters/SigningId'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
expiresInDays:
type: integer
minimum: 1
maximum: 30
required:
- expiresInDays
additionalProperties: false
responses:
'401':
$ref: '#/components/responses/Problem'
'429':
$ref: '#/components/responses/Problem'
'200':
description: The updated signing.
content:
application/json:
schema:
$ref: '#/components/schemas/Signing'
'400':
$ref: '#/components/responses/Problem'
'404':
$ref: '#/components/responses/Problem'
'409':
$ref: '#/components/responses/Problem'
delete:
tags:
- Signings
operationId: eraseSigning
summary: Erase one signing
description: 'Cancels a live signing first, then purges captured identity and
caller-supplied personal data. For a document uploaded through this
API, its bytes and vault record are also deleted. If the MCP server
opened the ceremony on a pre-existing vault document, that shared
document and its bytes are preserved while the dedicated ceremony is
removed. A de-identified audit row remains. Idempotent.'
parameters:
- $ref: '#/components/parameters/SigningId'
responses:
'401':
$ref: '#/components/responses/Problem'
'429':
$ref: '#/components/responses/Problem'
'200':
description: Erasure result.
content:
application/json:
schema:
type: object
properties:
signingId:
type: string
format: uuid
deleted:
type: boolean
const: true
changed:
type: boolean
required:
- signingId
- deleted
- changed
additionalProperties: false
'404':
$ref: '#/components/responses/Problem'
'503':
$ref: '#/components/responses/Problem'
/v1/signings/{signingId}/archive:
post:
tags:
- Signings
operationId: archiveSigning
summary: Raise the signed output to a long-term level
description: 'PDF goes PAdES-B-T to B-LT, ASiC-E goes XAdES-T to XAdES-LT: the
certificate chain and fresh revocation evidence are embedded so the
signature can still be validated once the signing certificate has
expired.
ADOC is refused by name. ADOC-V1.0 specifies XAdES-T and its
validators expect that level, so "upgrading" one would be a
conformance risk dressed as a favour.
Already-archived output comes back unchanged with `upgraded: false`,
so calling this twice costs a parse and not a second signature. The
pre-archive version is kept - an upgrade adds a version, never
replaces one.'
parameters:
- $ref: '#/components/parameters/SigningId'
responses:
'401':
$ref: '#/components/responses/Problem'
'429':
$ref: '#/components/responses/Problem'
'200':
description: The level now held, and whether this call raised it.
content:
application/json:
schema:
type: object
properties:
signingId:
type: string
format: uuid
level:
type: string
example: PAdES-B-LT
upgraded:
type: boolean
document:
$ref: '#/components/schemas/SignedDocument'
'404':
$ref: '#/components/responses/Problem'
'409':
$ref: '#/components/responses/Problem'
'422':
$ref: '#/components/responses/Problem'
/v1/signings/{signingId}/comments:
get:
tags:
- Signings
operationId: listComments
summary: Read the notes on a signing
parameters:
- $ref: '#/components/parameters/SigningId'
responses:
'401':
$ref: '#/components/responses/Problem'
'429':
$ref: '#/components/responses/Problem'
'200':
description: The comment thread, oldest first.
content:
application/json:
schema:
type: object
properties:
signingId:
type: string
format: uuid
comments:
type: array
items:
$ref: '#/components/schemas/Comment'
'404':
$ref: '#/components/responses/Problem'
post:
tags:
- Signings
operationId: addComment
summary: Add a note to a signing
description: 'Your own thread, for your own audit trail. Comments are **not shown
to signers**: the ceremony''s copy is platform-controlled, so a
signer-visible comment would need a surface that does not exist, and
one written in the belief that a signer will read it is worse than
none.'
parameters:
- $ref: '#/components/parameters/SigningId'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
body:
type: string
maxLength: 4000
authorLabel:
type: string
maxLength: 120
description: Your own name for whoever wrote it. Echoed back.
required:
- body
additionalProperties: false
responses:
'401':
$ref: '#/components/responses/Problem'
'429':
$ref: '#/components/responses/Problem'
'201':
description: The stored comment.
content:
application/json:
schema:
$ref: '#/components/schemas/Comment'
'400':
$ref: '#/components/responses/Problem'
'404':
$ref: '#/components/responses/Problem'
/v1/erasure:
post:
tags:
- Signings
operationId: eraseSignings
summary: Erase terminal signings in bulk
description: 'Purges the client''s terminal signing records and any order-owned
uploads. Pre-existing vault documents used through MCP are preserved.
In-flight signings are left untouched; erase one directly if
cancellation is intended.'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
before:
type: string
format: date-time
additionalProperties: false
responses:
'401':
$ref: '#/components/responses/Problem'
'429':
$ref: '#/components/responses/Problem'
'200':
description: Number purged and number that must be retried.
content:
application/json:
schema:
type: object
properties:
purged:
type: integer
failed:
type: integer
required:
- purged
- failed
additionalProperties: false
'400':
$ref: '#/components/responses/Problem'
/v1/signings/{signingId}/cancel:
post:
tags:
- Signings
operationId: cancelSigning
summary: Cancel a signing
description: 'Retracts every invitation that has not been used: the ceremony link
dies immediately, and the identity data the ceremony had captured
about those signers is erased.
Signatures already collected are untouched. A qualified signature
exists once it is made, and this endpoint does not pretend otherwise
- the signed document remains downloadable.'
parameters:
- $ref: '#/components/parameters/SigningId'
responses:
'401':
$ref: '#/components/responses/Problem'
'429':
$ref: '#/components/responses/Problem'
'200':
description: The signing is cancelled.
content:
application/json:
schema:
$ref: '#/components/schemas/Signing'
'404':
$ref: '#/components/responses/Problem'
'409':
description: The signing had already ended.
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
/v1/signings/{signingId}/document:
get:
tags:
- Signings
operationId: downloadSignedDocument
summary: Download the signed document
description: 'The newest signed output. Available as soon as one signer has
signed, so a partially signed document can be fetched; the
`X-Tvarka-Document-Sha256` header carries its digest. On a sandbox
simulation this streams the unsigned artifact instead, marked by an
`X-Tvarka-Sandbox` header.'
parameters:
- $ref: '#/components/parameters/SigningId'
responses:
'401':
$ref: '#/components/responses/Problem'
'429':
$ref: '#/components/responses/Problem'
'200':
description: The signed document.
headers:
X-Tvarka-Document-Sha256:
schema:
type: string
description: SHA-256 of the returned bytes.
X-Tvarka-Sandbox:
schema:
type: string
description: 'Present only on a sandbox artifact
(`simulated-artifact-no-signature`). The bytes carry no
signature.
'
content:
application/octet-stream:
schema:
type: string
format: binary
'404':
$ref: '#/components/responses/Problem'
'409':
description: Nobody has signed yet.
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
/v1/signings/{signingId}/signers:
post:
tags:
- Signings
operationId: addSigner
summary: Add a signer to a live signing
description: 'On a `sequential` signing the newcomer joins the END of the queue:
they come back `waiting`, their link stays dead until their turn,
and no delivery goes out before it. Only a parallel signing hands
back a live ceremony URL immediately.'
parameters:
- $ref: '#/components/parameters/SigningId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SignerRequest'
responses:
'401':
$ref: '#/components/responses/Problem'
'429':
$ref: '#/components/responses/Problem'
'201':
description: The signer and their ceremony.
content:
application/json:
schema:
$ref: '#/components/schemas/Signer'
'400':
$ref: '#/components/responses/Problem'
'402':
$ref: '#/components/responses/Problem'
'404':
$ref: '#/components/responses/Problem'
'409':
$ref: '#/components/responses/Problem'
/v1/signings/{signingId}/signers/{signerId}:
patch:
tags:
- Signings
operationId: updateSignerMethods
summary: Update a pending signer's allowed methods
parameters:
- $ref: '#/components/parameters/SigningId'
- $ref: '#/components/parameters/SignerId'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
methods:
type: array
minItems: 1
uniqueItems: true
items:
$ref: '#/components/schemas/SigningMethod'
required:
- methods
additionalProperties: false
responses:
'401':
$ref: '#/components/responses/Problem'
'429':
$ref: '#/components/responses/Problem'
'200':
description: The updated signer.
content:
application/json:
schema:
$ref: '#/components/schemas/Signer'
'400':
$ref: '#/components/responses/Problem'
'404':
$ref: '#/components/responses/Problem'
'409':
$ref: '#/components/responses/Problem'
delete:
tags:
- Signings
operationId: removeSigner
summary: Remove a pending signer
description: Retracts the ceremony link before removing the signer.
parameters:
- $ref: '#/components/parameters/SigningId'
- $ref: '#/components/parameters/SignerId'
responses:
'401':
$ref: '#/components/responses/Problem'
'429':
$ref: '#/components/responses/Problem'
'204':
description: The signer was removed.
'404':
$ref: '#/components/responses/Problem'
'409':
$ref: '#/components/responses/Problem'
/v1/signings/{signingId}/signers/{signerId}/remind:
post:
tags:
- Signings
operationId: remindSigner
summary: Remind one signer
description: Re-sends the invitation email. One reminder per signer per hour.
parameters:
- $ref: '#/components/parameters/SigningId'
- $ref: '#/components/parameters/SignerId'
responses:
'401':
$ref: '#/components/responses/Problem'
'200':
description: The reminder was sent.
content:
application/json:
schema:
$ref: '#/components/schemas/Signer'
'404':
$ref: '#/components/responses/Problem'
'409':
$ref: '#/components/responses/Problem'
'429':
description: A reminder was sent to this signer less than an hour ago.
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
/v1/signings/{signingId}/simulate:
post:
tags:
- Signings
operationId: simulateSigning
summary: Drive a sandbox signing to an outcome
description: 'Sandbox keys only. Moves a signing to `completed`, `declined` or
`expired` so an integrator can exercise each terminal path without
waiting. `complete` simulates the lifecycle: signer status is
`simulated`, completion mode is `sandbox_simulation`, and no
signature is created. It does return a downloadable artifact so the
success path is testable, flagged `sandbox: true`. The sandbox adds
no signature: an unsigned PDF comes back stamped and unsigned,
anything already signed comes back byte-identical.'
parameters:
- $ref: '#/components/parameters/SigningId'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
action:
type: string
enum:
- complete
- decline
- expire
signerEmail:
type: string
format: email
description: Which signer declines. Defaults to the first.
reason:
type: string
required:
- action
additionalProperties: false
responses:
'401':
$ref: '#/components/responses/Problem'
'429':
$ref: '#/components/responses/Problem'
'200':
description: The signing after the simulated outcome.
content:
application/json:
schema:
$ref: '#/components/schemas/Signing'
'400':
$ref: '#/components/responses/Problem'
'403':
description: The key is a production key.
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
'404':
$ref: '#/components/responses/Problem'
'409':
description: 'The signing is already terminal, so there is no outcome left to
simulate.
'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
components:
parameters:
SignerId:
name: signerId
in: path
required: true
schema:
type: string
format: uuid
SigningId:
name: signingId
in: path
required: true
schema:
type: string
format: uuid
schemas:
Problem:
type: object
description: RFC 9457.
properties:
type:
type: string
format: uri
title:
type: string
status:
type: integer
detail:
type: string
errors:
type: array
description: 'Present on `validation-failed`, and on the single-fault problems
that name a field. Request-body validation collects rather than
failing fast, so a caller who got three fields wrong learns all
three from one response.
'
items:
$ref: '#/components/schemas/ProblemFieldError'
recoveryUrl:
type: string
format: uri-reference
description: 'Present on the payment-required refusal when a web page exists
that fixes it: the tenant''s payment setup, reached with an
ordinary browser login. Show it to your operator; it is not an
API endpoint.
'
ProblemFieldError:
type: object
description: One rejected field.
properties:
pointer:
type: string
description: 'RFC 6901 JSON Pointer into the request body, for example
`/signers/0/email` or `/container/files/2/content`.
'
detail:
type: string
required:
- pointer
- detail
Comment:
type: object
properties:
commentId:
type: string
format: uuid
author:
type: string
enum:
- client
- tvarka
authorLabel:
type: string
nullable: true
body:
type: string
createdAt:
type: string
format: date-time
SignerStatus:
type: string
enum:
- pending
- signed
- declined
- cancelled
- expired
- simulated
- waiting
- recorded
description: '`waiting` - a sequential signer whose turn has not come; their
ceremony link answers 404 until it does.
`recorded` - a viewer. Never invited, never blocks completion.
'
SignerRequest:
type: object
properties:
role:
type: string
enum:
- signer
- viewer
default: signer
description: 'A `viewer` is recorded but never invited, never gets a ceremony
link, and never holds up completion. Tvarka sends them nothing.
At least one signer is required.
'
email:
type: string
format: email
name:
type: string
maxLength: 500
language:
type: string
enum:
- lt
- en
default: lt
description: Per signer, for their email and ceremony page.
personalCode:
type: string
pattern: ^[0-9]{11}$
description: 'Optional Lithuanian identity lock. Tvarka stores a keyed hash
and encrypted value, never returns the code, and refuses a
different authenticated signer.
'
methods:
type: array
minItems: 1
uniqueItems: true
default:
- nfc
- smart_id
- mobile_id
items:
$ref: '#/components/schemas/SigningMethod'
description: 'Per-signer allow-list. `nfc` uses the Tvarka Sign mobile app;
Smart-ID and Mobile-ID use the hosted page or mobile app.
'
required:
- email
additionalProperties: false
Signing:
type: object
properties:
signingId:
type: string
format: uuid
status:
$ref: '#/components/schemas/SigningStatus'
title:
type: string
delivery:
type: string
enum:
- link
- email
signingOrder:
type: string
enum:
- parallel
- sequential
description: '`sequential` releases one signer at a time in the order they were
posted. The signers who are waiting hold a ceremony link that
answers 404 until their turn, so the ordering is enforced rather
than merely unsent. A decline stops the workflow at the
decliner''s turn: the signing settles `declined` and the signers
still waiting are cancelled.
'
softDeadlineAt:
type: string
format: date-time
nullable: true
description: 'The date the signing is wanted by, as opposed to `expiresAt`,
which is the date it dies on. Nothing expires here; it is when the
one automatic reminder goes out.
'
externalId:
type: string
nullable: true
createdAt:
type: string
format: date-time
expiresAt:
type: string
format: date-time
nullable: true
completedAt:
type: string
format: date-time
nullable: true
completionMode:
type: string
enum:
- qes_ceremony
- sandbox_simulation
nullable: true
description: '`qes_ceremony` means human eID signing. `sandbox_simulation`
exercises lifecycle/webhook integration and creates no
signature; its artifact is flagged `sandbox: true`.
'
erasedAt:
type: string
format: date-time
nullable: true
detail:
type: string
description: Present when the status needs a sentence of explanation.
signers:
type: array
items:
$ref: '#/components/schemas/Signer'
document:
$ref: '#/components/schemas/SignedDocument'
additionalProperties: false
CreateSigningRequest:
type: object
properties:
title:
type: string
maxLength: 255
description: Shown to signers on the ceremony page and in their email.
document:
type: object
properties:
name:
type: string
description: 'Filename with extension. The extension selects the family:
`.pdf`; `.asice`/`.sce`/`.bdoc` to countersign an existing
appendable ASiC-E; `.adoc`/`.bedoc`/`.cedoc`/`.gedoc`/
`.ggedoc` to countersign a signed ADOC-family container.
For containers the extension only decides that we look
inside - the container''s own mimetype member decides what
it is. `.mdoc` is refused: an MDOC takes a first signature
only.
'
content:
type: string
format: byte
description: 'Base64-encoded bytes, up to 15 MiB decoded. Provide exactly
one of `content` or `fileToken`.
'
fileToken:
type: string
format: uuid
description: 'A reusable upload from `POST /v1/files`, referenced instead
of carrying the bytes again. The bytes are copied into the
signing at creation, so purging the file later never
reaches into a signing that already used it. Provide
exactly one of `content` or `fileToken`.
'
flatten:
type: boolean
default: false
description: 'PDF only. Draw every form field''s appearance into page
content and drop the AcroForm, so what a reader sees cannot
depend on a viewer''s form handling. Fails closed: if any
widget has no appearance stream Tvarka can draw faithfully,
nothing is flattened and the response names those fields.
Signing a live AcroForm without this is perfectly valid -
the signature still detects any later edit.
'
required:
- name
additionalProperties: false
container:
type: object
description: 'Compose a NEW container from several files and sign that, instead
of posting one finished document. Mutually exclusive with
`document`; exactly one of the two is required.
'
properties:
format:
type: string
enum:
- asice
- adoc
description: '`asice` takes PDFs only. `adoc` is the Lithuanian ADOC-V1.0
package and takes `.pdf` and `.xlsx`. The first file is the
main document; the rest are appendices.
'
files:
type: array
minItems: 1
maxItems: 20
items:
type: object
properties:
name:
type: string
content:
type: string
format: byte
required:
- name
- content
additionalProperties: false
required:
- format
- files
additionalProperties: false
signers:
type: array
minItems: 1
maxItems: 20
description: 'Everyone who must sign. Unordered, and one email may appear
once - a person signs a document once.
'
items:
$ref: '#/components/schemas/SignerRequest'
delivery:
type: string
enum:
- link
- email
default: link
description: '`link` returns a `ceremonyUrl` per signer for you to deliver.
`email` has Tvarka email each signer in your entity''s name.
'
webhookUrl:
type: string
format: uri
description: Public HTTPS URL for signing events. Optional.
externalId:
type: string
maxLength: 120
description: Your own reference, echoed on every response and webhook.
signingOrder:
# --- truncated at 32 KB (37 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/tvarka/refs/heads/main/openapi/tvarka-signings-api-openapi.yml