{"openapi": "3.1.0", "info": {"title": "Tvarka Sign API", "version": "1.9.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,\nand Tvarka runs its ordinary qualified signing ceremony for each of them.\n\n**What this API is.** Orchestration. The ceremony itself is the shipped\nTvarka Sign ceremony a person already uses: the same hosted page, the\nsame signing methods (Lithuanian ID card over NFC in the Tvarka Sign\nmobile app, Smart-ID and Mobile-ID on the hosted page), the same PIN\nrules, the same invitation lifecycle. A signature is always a human act\nwith the signer's own eID, so the orchestration endpoints are about\ngetting a document in front of the right person and finding out what\nthey did. Headless signatures (`/v1/signatures`, v1.6.0) run the same\nSmart-ID and Mobile-ID PIN sequences without the hosted page: your\napplication shows the verification code and the signer confirms on\ntheir phone.\n\n**Signature formats.** This orchestration API accepts PDF, appendable\nASiC-E containers, and the ADOC family - `.adoc`, `.bedoc`, `.cedoc`,\n`.gedoc`, `.ggedoc` - for countersigning one that is already signed.\nA container is signed in place, never converted. MDOC is not accepted\nfor countersigning: its composer writes a first signature only.\n\n**Office files and images (v1.9.0).** A document posted as\n`.docx`, `.doc`, `.odt`, `.rtf`, `.xlsx`, `.xls`, `.ods`, `.pptx`, `.odp`, `.txt`, `.jpg`, `.jpeg` and `.png` is converted to PDF/A-2b and that PDF is signed,\nin every intake: `content`, `url` and `POST /v1/files`. The signed\nfile and its name are the PDF (`Contract.docx` becomes\n`Contract.pdf`); the original is not kept. `POST /v1/conversions`\nreturns the PDF without signing. Conversion costs nothing extra.\nThe complete Tvarka API estate produces every\nRegulation (EU) 2026/248 Annex I family: PAdES, XAdES, CAdES, JAdES and\nASiC. The ATK API is the lower-level card and reader API, including full\nLT ATK NFC, USB/reader and detached CAdES support. The DD API produces\nJAdES. See the format matrix in the developer guide before selecting a\nsurface.\n\n**Signature validation.** Tvarka provides advisory signature\nvalidation results for integration into your document workflows.\n\n**Tenancy and billing.** A tenant is a Tvarka entity with a Sign\nworkspace, and ceremonies are raised in that entity's name, so the\ncounterparty sees who is actually asking. Signatures are billed to that\nworkspace on the ordinary per-signature price ladder; there is no\nseparate API tariff and no subscription.\n\n**Sandbox.** A `tsk_test_` key opens real ceremonies but is exempt from\nthe funding precondition, and can drive a signing to `completed`,\n`declined` or `expired` through `/simulate`. A simulated completion is\nexplicitly marked `sandbox_simulation` and returns a real downloadable\nartifact, so download, hashing and storage can be exercised without a\nreal eID. The sandbox never adds a signature: an unsigned PDF comes back\nas the caller's own pages with a banner saying it is not signed, and\nanything posted already signed comes back byte-identical. Every artifact\nis flagged `sandbox: true`. Production ceremonies produce real qualified electronic signatures. A\nproduction completion is always a real human ceremony.\n\n**Custom PDF stamps (v1.4.0).** Activated for clients with a signed\nagreement. The workspace stamp entitlement covers company logos and exact\nPDF placement in both Tvarka Sign and this API. VIP whitelabel is a\nseparate entitlement for custom or omitted vendor text; standard clients\nkeep the default Tvarka line and do not see the text controls.\nAn active company Vadovas configures the logo and previews the appearance\nin the cloud browser at https://sign.tvarka.pro/sign/app/users/?tab=stamp\n(Users and access -> Parašo išvaizda). Controls stay visible but disabled\nuntil activation. Operators grant both SKUs in Administravimas -> Parašo\nišvaizda. This contract exposes no branding upload, text-edit or\nentitlement-management endpoints; do not send those fields in API requests.\n\nPNG logos: up to 1 MB, longest side 200-2000 px, neither side above 2000 px,\naspect ratio 1:3 to 3:1. Transparency is retained and scaling is proportional.\nVIP vendor text: default, custom (up to 32 printable characters that fit),\nor omitted. Signer identity, time and purpose stay visible.\n\nSet per-signer `visibleSignature` on create, add-signer or batch requests;\nMCP `request_signatures` accepts the same shape for a vault document.\nUse exactly one of `{\"page\":1,\"x\":36,\"y\":36,\"width\":148,\"height\":52}`\nor `{\"field\":\"Sig_Vadovas\"}`. Batch placements must work on every document\nbefore invitations are dispatched. Accepted normalized placement is\nimmutable and returned in signer status. Omission keeps corner/cascade\nplacement with workspace branding. Explicit boxes never silently move;\ncallers must avoid overlaps. PDF signers only, not viewers or containers.\n\nAn API order snapshots branding on acceptance, including later signers,\nretries and idempotent replays. Workspace edits, reset or entitlement\nrevocation do not alter accepted snapshots. New custom placements require\ncurrent stamp entitlement; new snapshots after VIP revocation use the\ndefault vendor line. Regular Sign uses the same branding but a separate\ndocument snapshot and its own per-user Mano parašo vieta editor; that\neditor does not change API-owned placement. Internal electronic signatures\nkeep their non-qualified label and original overlay position.\n\nSandbox completion previews the requested placements and branding on\nunsigned PDFs, including default-corner signers and MCP vault orders.\nAlready-signed input remains byte-identical and receives no stamp preview.\n`403 stamp-controls-not-enabled` means the workspace lacks stamp access;\n`400 validation-failed` names invalid input with JSON pointers;\n`422 stamp-preparation-failed` means sandbox preview preparation failed.\nPlacement and snapshotted logo are revalidated against the locked revision\nduring real signing. A stamp preparation failure makes signer and order\nterminally failed, emits `signing.signer_failed` and `signing.failed`, and\nretracts remaining invitations and pending work while preserving collected\nsignatures. Provider outages alone do not trigger stamp-failure cleanup.\nFull guide: https://sign-api.tvarka.pro/docs#stamps\n\n**Headless signatures (v1.6.0).** `POST /v1/signatures` signs one\ndocument for one signer with Smart-ID or Mobile-ID, with no hosted page,\nno invitation and no email. Your application shows the verification\ncode; the signer confirms on their phone. The PIN sequences are the ones\nthe hosted ceremony runs, unchanged: when Tvarka already knows the\nsigner's signing certificate, one confirmation signs. A configured\ncertificate service can obtain it without a PIN, so first use can also\nneed only the signing PIN. Certificate-capture fallback can require an\nidentity confirmation first, then signing (at most two PINs). Follow\nthe current verification code when `step` moves from\n`authentication` to `signing`, and a `signature.confirmation_required`\nwebhook carries the new code. Which sequence runs is Tvarka's\ndecision, not the caller's. One request signs one document; for many\ndocuments at once, the ATK API signs in bulk with the ID card.\n\nProfiles are named, never free parameters: `cades-detached` signs your\nSHA-256 hash and returns a detached `.p7s` at CAdES-BASELINE-LT (the\ntimestamp, the certificate chains and fresh revocation data are embedded,\nso it validates without online lookups); `pades` signs a PDF, such as the PDF/A-2 that ESPBI renders, and\nreturns it signed at PAdES-BASELINE-LT: an invisible signature added as an\nincremental update, so the document and any earlier signature or timestamp\nstay intact. `renderHash` is required: the digest of what you\nshowed the signer, kept as evidence beside `display.text`, the input\ndigest, the verification codes and the signing certificate. You are\nresponsible for what you display.\n\nProduction workspaces need the headless-signatures entitlement, which\nTvarka enables after the headless addendum is signed; sandbox keys do\nnot. A signature is billed once, on success, as one remote signature\non the workspace's price ladder. Declined, expired, failed and\ncancelled signatures are free, and a completion that arrives after a\ncancellation is discarded unbilled. The sandbox never reaches the\nsigner's phone and never adds a signature: `/simulate` drives the\noutcome. Signed documents are kept for 30 days or until\n`/v1/erasure`; the evidence record stays.\n\n**Compatibility.** Ignore unknown response properties. Requests reject\nunknown fields. Additive response fields may arrive in minor releases;\nremovals and incompatible changes require a new major API contract.\n\n**Limits and recovery.** Documents support up to 100 MB (100,000,000 bytes)\nthrough multipart POST /v1/files and document.fileToken. Inline base64\nremains limited to 15 MiB decoded; the JSON envelope is limited to 21 MiB.\nMCP accepts identifiers, not uploads, and limits requests to 64 KiB.\nInitial invitation email is queued durably; emailStatus reports backend\nacceptance rather than inbox delivery. Webhooks retry transport errors,\n3xx, 401, 403, 404, 408, 425, 429 and 5xx for up to 72 hours with capped\nexponential backoff; other refusals are terminal. Redirects are never\nfollowed. Deliveries may repeat or arrive out of order: deduplicate by\nX-Tvarka-Idempotency-Key. Exhausted retained events can be replayed by\nsupport with the same immutable body and deduplication key. The receiver\nshould acknowledge promptly (10-second HTTP timeout). Preflight 4xx\ncreate refusals before resource creation release the Idempotency-Key;\nretry the identical request after correcting funding or throttling.\nAmbiguous executions retain the key and are never automatically rerun.\n", "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": []}, {"agentOAuth": ["sign.read", "sign.signings"]}], "tags": [{"name": "Signings", "description": "Create and follow a signing."}, {"name": "Batches", "description": "Group independent signings."}, {"name": "Files", "description": "Reusable uploads retained for seven days."}, {"name": "Signatures", "description": "Headless Smart-ID and Mobile-ID signing of one document."}], "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": {"409": {"$ref": "#/components/responses/Problem"}, "402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/Problem"}, "200": {"description": "A newest-first page ordered by creation time then UUID, both descending.", "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\nceremony URLs (or emails them, with `delivery: email`).\n\nAll signers sign the same document and are unordered: whoever opens\ntheir link first signs first, and each subsequent signer signs the\noutput the previous one produced. Two signers never sign at the same\nmoment - the ceremony serializes them.\n", "parameters": [{"$ref": "#/components/parameters/IdempotencyKey"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateSigningRequest"}}}}, "responses": {"413": {"$ref": "#/components/responses/Problem"}, "409": {"description": "The idempotency key conflicts, is pending, or has an erased or expired result.", "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}}}}, "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\noverdue invoice, or a tripped usage ceiling. Nothing was\ncreated and nobody was invited. `recoveryUrl` names the web\npage where the tenant can fix it.\n", "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\nsignature - unsigned, already finalized, or structurally\nunsound. Not billed.\n", "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": {"409": {"$ref": "#/components/responses/Problem"}, "402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "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": {"402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "413": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "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\ncaller-supplied personal data. For a document uploaded through this\nAPI, its bytes and vault record are also deleted. If the MCP server\nopened the ceremony on a pre-existing vault document, that shared\ndocument and its bytes are preserved while the dedicated ceremony is\nremoved. A de-identified audit row remains. Idempotent.\n", "parameters": [{"$ref": "#/components/parameters/SigningId"}], "responses": {"402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "409": {"$ref": "#/components/responses/Problem"}, "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"]}}}}, "404": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}}}}, "/v1/batches": {"post": {"tags": ["Batches"], "operationId": "createBatch", "summary": "Raise one signing per document for one set of parties", "description": "Batch operations group documents for coordinated creation and status\ntracking, with one API call and one batch identifier. Each document\nfollows its own signing ceremony and receives its own signature.\nSigners complete the authentication and signing steps for each document.\n\nCreation is atomic: either every signing in the batch is raised, or\nthe error response means none was.\n", "parameters": [{"$ref": "#/components/parameters/IdempotencyKey"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"title": {"type": "string", "maxLength": 255}, "documents": {"type": "array", "minItems": 1, "maxItems": 20, "items": {"type": "object", "properties": {"name": {"type": "string"}, "content": {"type": "string", "format": "byte"}, "fileToken": {"type": "string", "format": "uuid"}, "flatten": {"type": "boolean"}}, "required": ["name"], "additionalProperties": false}}, "signers": {"type": "array", "minItems": 1, "items": {"$ref": "#/components/schemas/SignerRequest"}}, "delivery": {"type": "string", "enum": ["link", "email"]}, "signingOrder": {"type": "string", "enum": ["parallel", "sequential"]}, "returnUrl": {"type": "string", "format": "uri", "maxLength": 500, "description": "Exact registered HTTPS return URL; requires returnState."}, "returnState": {"type": "string", "minLength": 16, "maxLength": 128, "pattern": "^[A-Za-z0-9_-]+$"}, "webhookUrl": {"type": "string", "format": "uri"}, "externalId": {"type": "string", "maxLength": 120}, "expiresInDays": {"type": "integer", "minimum": 1}, "softDeadlineInDays": {"type": "integer", "minimum": 1, "description": "Applied to every signing the batch raises."}}, "required": ["title", "documents", "signers"], "additionalProperties": false}}}}, "responses": {"403": {"$ref": "#/components/responses/Problem"}, "413": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "409": {"description": "The idempotency key conflicts, is pending, or has an erased or expired result.", "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}}}}, "401": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/Problem"}, "201": {"description": "The batch and every signing it raised.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Batch"}}}}, "400": {"$ref": "#/components/responses/Problem"}, "402": {"$ref": "#/components/responses/Problem"}}}}, "/v1/batches/{batchId}": {"get": {"tags": ["Batches"], "operationId": "getBatch", "summary": "Poll a batch and every signing in it", "parameters": [{"$ref": "#/components/parameters/BatchId"}], "responses": {"409": {"$ref": "#/components/responses/Problem"}, "402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "401": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/Problem"}, "200": {"description": "The batch.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Batch"}}}}, "404": {"$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\ncertificate chain and fresh revocation evidence are embedded so the\nsignature can still be validated once the signing certificate has\nexpired.\n\nADOC is refused by name. ADOC-V1.0 specifies XAdES-T and its\nvalidators expect that level, so \"upgrading\" one would be a\nconformance risk dressed as a favour.\n\nAlready-archived output comes back unchanged with `upgraded: false`,\nso calling this twice costs a parse and not a second signature. The\npre-archive version is kept - an upgrade adds a version, never\nreplaces one.\n", "parameters": [{"$ref": "#/components/parameters/SigningId"}], "responses": {"400": {"$ref": "#/components/responses/Problem"}, "402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "413": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "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/files": {"post": {"tags": ["Files"], "operationId": "uploadFile", "summary": "Store a document once for reuse across signings", "description": "Returns a `fileToken` a later signing may reference instead of\ncarrying base64 again. Multipart accepts one binary part named file,\nup to 100,000,000 bytes; JSON accepts up to 15 MiB decoded. Format\nvalidation is identical for both transports. Office files and\nimages are converted to PDF/A-2b on upload (v1.9.0), so the stored\nfile and its `name` are the PDF.\n\nThe bytes are **copied** into each signing that uses the token, never\nshared by reference: deleting a file, or letting it expire, can never\nreach backwards into a signing that already used it.\n", "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"type": "object", "required": ["file"], "additionalProperties": false, "properties": {"file": {"type": "string", "format": "binary", "description": "One document, at most 100,000,000 bytes. Preserve its filename extension."}}}}, "application/json": {"schema": {"type": "object", "properties": {"name": {"type": "string"}, "content": {"type": "string", "format": "byte"}}, "required": ["name", "content"], "additionalProperties": false}}}}, "responses": {"402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "413": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/Problem"}, "201": {"description": "The stored file.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/StoredFile"}}}}, "400": {"$ref": "#/components/responses/Problem"}, "401": {"$ref": "#/components/responses/Problem"}}}}, "/v1/conversions": {"post": {"tags": ["Files"], "operationId": "convertDocument", "summary": "Convert an Office file or image to PDF/A-2b", "description": "Returns the PDF/A-2b bytes of one `.docx`, `.doc`, `.odt`, `.rtf`, `.xlsx`, `.xls`, `.ods`, `.pptx`, `.odp`, `.txt`, `.jpg`, `.jpeg` and `.png` file, ready to sign\nlocally or to post as a document. Nothing is stored and nothing is\nbilled. Multipart accepts one binary part named file, up to\n100,000,000 bytes; JSON accepts up to 15 MiB decoded. Added in\nv1.9.0.\n", "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"type": "object", "required": ["file"], "additionalProperties": false, "properties": {"file": {"type": "string", "format": "binary", "description": "One file, at most 100,000,000 bytes. Its extension selects the converter."}}}}, "application/json": {"schema": {"type": "object", "properties": {"name": {"type": "string"}, "content": {"type": "string", "format": "byte"}}, "required": ["name", "content"], "additionalProperties": false}}}}, "responses": {"200": {"description": "The converted PDF/A-2b, as an attachment named after the source file with a `.pdf` extension.", "content": {"application/pdf": {"schema": {"type": "string", "format": "binary"}}}}, "400": {"$ref": "#/components/responses/Problem"}, "401": {"$ref": "#/components/responses/Problem"}, "402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "413": {"$ref": "#/components/responses/Problem"}, "422": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}}}}, "/v1/files/{fileToken}": {"get": {"tags": ["Files"], "operationId": "getFile", "summary": "Inspect a stored file", "parameters": [{"$ref": "#/components/parameters/FileToken"}], "responses": {"402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "401": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/Problem"}, "200": {"description": "The stored file.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/StoredFile"}}}}, "404": {"$ref": "#/components/responses/Problem"}}}, "delete": {"tags": ["Files"], "operationId": "deleteFile", "summary": "Purge a stored file", "description": "Drops the bytes. Signings that already referenced the token are\nuntouched, because they hold their own copy.\n", "parameters": [{"$ref": "#/components/parameters/FileToken"}], "responses": {"402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "409": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "401": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/Problem"}, "200": {"description": "The file, now purged.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/StoredFile"}}}}, "404": {"$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"}, {"name": "limit", "in": "query", "schema": {"type": "integer", "minimum": 1, "maximum": 100, "default": 50}}, {"name": "startingAfter", "in": "query", "description": "Comment UUID from nextCursor; ordering is ascending creation time then UUID.", "schema": {"type": "string", "format": "uuid"}}], "responses": {"402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "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"}, "hasMore": {"type": "boolean"}, "nextCursor": {"type": ["string", "null"], "format": "uuid"}, "comments": {"type": "array", "items": {"$ref": "#/components/schemas/Comment"}}}}}}}, "400": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}}}, "post": {"tags": ["Signings"], "operationId": "addComment", "summary": "Add a note to a signing", "description": "Comments provide a private communication record for the integration\nand its audit trail. Access is limited to the integration; signer\ncommunications are handled through the signing ceremony.\n", "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": {"402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "413": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "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\nuploads. Pre-existing vault documents used through MCP are preserved.\nIn-flight signings are left untouched; erase one directly if\ncancellation is intended.\n", "requestBody": {"required": false, "content": {"application/json": {"schema": {"type": "object", "properties": {"before": {"type": "string", "format": "date-time"}}, "additionalProperties": false}}}}, "responses": {"402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "413": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "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"]}}}}, "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\ndies immediately, and the identity data the ceremony had captured\nabout those signers is erased.\n\nCollected signatures remain intact and the signed document remains\ndownloadable after cancellation.\n", "parameters": [{"$ref": "#/components/parameters/SigningId"}], "responses": {"400": {"$ref": "#/components/responses/Problem"}, "402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "413": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "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\nsigned, so a partially signed document can be fetched; the\n`X-Tvarka-Document-Sha256` header carries its digest. On a sandbox\nsimulation this streams the unsigned artifact instead, marked by an\n`X-Tvarka-Sandbox` header.\n", "parameters": [{"$ref": "#/components/parameters/SigningId"}], "responses": {"402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "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\n(`simulated-artifact-no-signature`). The bytes carry no\nsignature.\n"}}, "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:\nthey come back `waiting`, their link stays dead until their turn,\nand no delivery goes out before it. Only a parallel signing hands\nback a live ceremony URL immediately.\n", "parameters": [{"$ref": "#/components/parameters/SigningId"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SignerRequest"}}}}, "responses": {"403": {"$ref": "#/components/responses/Problem"}, "413": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "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": {"402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "413": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "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": {"402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "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": {"502": {"$ref": "#/components/responses/Problem"}, "400": {"$ref": "#/components/responses/Problem"}, "402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "413": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "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. Retry-After gives the remaining cooldown in seconds.", "headers": {"Retry-After": {"schema": {"type": "integer", "minimum": 1, "maximum": 3600}}}, "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\n`expired` so an integrator can exercise each terminal path without\nwaiting. `complete` simulates the lifecycle: signer status is\n`simulated`, completion mode is `sandbox_simulation`, and no\nsignature is created. It does return a downloadable artifact so the\nsuccess path is testable, flagged `sandbox: true`. The sandbox adds\nno signature: an unsigned PDF comes back stamped and unsigned,\nanything already signed comes back byte-identical.\n", "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": {"402": {"$ref": "#/components/responses/Problem"}, "413": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}, "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"}, "422": {"description": "The requested sandbox stamp could not be prepared.", "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}}}}, "409": {"description": "The signing is already terminal, so there is no outcome left to\nsimulate.\n", "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}}}}}}}, "/v1/signatures": {"post": {"tags": ["Signatures"], "operationId": "createSignature", "summary": "Start a headless signature", "description": "Opens the first confirmation on the signer's phone at once and\nanswers `202` with its verification code. Show the code; poll\n`GET /v1/signatures/{signatureId}` or wait for the webhook.\n\nRefusals at create time are never billed: `403\nheadless-not-enabled` (production workspace without the\nentitlement), `402 payment-required` with `recoveryUrl` when the\nworkspace cannot pay for one signature, `409 signer-busy` when the\nsigner already has an active request elsewhere (not queued; retry\nonce it ends), and `400 validation-failed` listing every rejected\nfield with a JSON Pointer.\n", "parameters": [{"$ref": "#/components/parameters/IdempotencyKey"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateSignatureRequest"}}}}, "responses": {"202": {"description": "The first confirmation is on the signer's phone.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Signature"}}}}, "400": {"$ref": "#/components/responses/Problem"}, "401": {"$ref": "#/components/responses/Problem"}, "402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "409": {"description": "The signer already has an active request, or the Idempotency-Key was reused with other input.", "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}}}}, "413": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}}}}, "/v1/signatures/{signatureId}": {"get": {"tags": ["Signatures"], "operationId": "getSignature", "summary": "State, current step and code, result", "parameters": [{"$ref": "#/components/parameters/SignatureId"}], "responses": {"200": {"description": "The signature.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Signature"}}}}, "401": {"$ref": "#/components/responses/Problem"}, "402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}}}}, "/v1/signatures/{signatureId}/cancel": {"post": {"tags": ["Signatures"], "operationId": "cancelSignature", "summary": "Cancel a pending signature", "description": "A confirmation already on the phone cannot be withdrawn there; if\nthe signer still confirms it, the result is discarded and not\nbilled.\n", "parameters": [{"$ref": "#/components/parameters/SignatureId"}], "responses": {"200": {"description": "The signature is cancelled.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Signature"}}}}, "401": {"$ref": "#/components/responses/Problem"}, "402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "409": {"description": "The signature had already ended.", "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}}}}, "429": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}}}}, "/v1/signatures/{signatureId}/document": {"get": {"tags": ["Signatures"], "operationId": "downloadSignature", "summary": "Download the signed artifact", "description": "`.p7s` (`application/pkcs7-signature`) for `cades-detached`, the\nsigned XML for XAdES profiles. Kept for 30 days or until\n`/v1/erasure`; afterwards `410`. A sandbox simulation streams an\nartifact with no signature, marked by `X-Tvarka-Sandbox`.\n", "parameters": [{"$ref": "#/components/parameters/SignatureId"}], "responses": {"200": {"description": "The signed artifact.", "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`)."}}, "content": {"application/pkcs7-signature": {"schema": {"type": "string", "format": "binary"}}, "application/pdf": {"schema": {"type": "string", "format": "binary"}}}}, "401": {"$ref": "#/components/responses/Problem"}, "402": {"$ref": "#/components/responses/Problem"}, "403": {"$ref": "#/components/responses/Problem"}, "404": {"$ref": "#/components/responses/Problem"}, "409": {"description": "The signature is not signed.", "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}}}}, "410": {"description": "The artifact was erased; the signature record remains.", "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}}}}, "429": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}}}}, "/v1/signatures/{signatureId}/simulate": {"post": {"tags": ["Signatures"], "operationId": "simulateSignature", "summary": "Drive a sandbox signature to an outcome", "description": "Sandbox keys only. `sign` returns your XML unchanged, or an empty\n`.p7s` placeholder for a hash, flagged `sandbox: true` and\n`completionMode: sandbox_simulation`: no signature is added.\n`fail` takes an optional `reason` from `SignatureFailureReason`.\n", "parameters": [{"$ref": "#/components/parameters/SignatureId"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"action": {"type": "string", "enum": ["sign", "decline", "expire", "fail"]}, "reason": {"$ref": "#/components/schemas/SignatureFailureReason"}}, "required": ["action"], "additionalProperties": false}}}}, "responses": {"200": {"description": "The signature after the simulated outcome.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Signature"}}}}, "400": {"$ref": "#/components/responses/Problem"}, "401": {"$ref": "#/components/responses/Problem"}, "402": {"$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 signature had already ended.", "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}}}}, "429": {"$ref": "#/components/responses/Problem"}, "503": {"$ref": "#/components/responses/Problem"}}}}}, "webhooks": {"signingEvent": {"post": {"summary": "Signing event", "description": "Sent to `webhookUrl` as a signing progresses. Deliveries carry\n`X-Tvarka-Signature: sha256=<HMAC-SHA256 of the raw body>` keyed with\nyour webhook secret - verify it before acting - plus\n`X-Tvarka-Idempotency-Key` and `X-Tvarka-Event`.\n\nPrefer `X-Tvarka-Signature-V2: t=<unix seconds>,v1=<hex>`, where\n`v1` is the HMAC-SHA256 of `t + \".\" + raw body` under the same secret\nand `t` equals `X-Tvarka-Timestamp`. Reject deliveries whose `t` is\nmore than 5 minutes from your clock: a replayed delivery fails.\n\nRetried with backoff on 5xx and transport failures. Delivery is a\nconvenience, never the only way to learn an outcome: polling always\nworks, and a failed delivery never changes a signing's state.\n\nThe payload never carries a signer's identity data - no personal\ncode, no certificate subject, no phone number. It echoes the email\nyou supplied and nothing more.\n", "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"event": {"type": "string", "enum": ["signing.signer_signed", "signing.signer_declined", "signing.signer_failed", "signing.completed", "signing.declined", "signing.cancelled", "signing.expired", "signing.failed"]}, "signingId": {"type": "string", "format": "uuid"}, "status": {"$ref": "#/components/schemas/SigningStatus"}, "externalId": {"type": "string"}, "occurredAt": {"type": "string", "format": "date-time"}, "completionMode": {"type": "string", "enum": ["qes_ceremony", "sandbox_simulation"]}, "signer": {"type": "object", "properties": {"signerId": {"type": "string", "format": "uuid"}, "email": {"type": "string", "format": "email"}, "status": {"$ref": "#/components/schemas/SignerStatus"}, "declineReason": {"type": "string"}}}, "document": {"$ref": "#/components/schemas/SignedDocument"}}}}}}, "responses": {"401": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/Problem"}, "200": {"description": "Acknowledged. Any 2xx stops redelivery."}}}}, "signatureEvent": {"post": {"summary": "Headless signature event", "description": "Same delivery, headers, HMAC signatures and 72-hour retry as\n`signingEvent`. `signature.confirmation_required` carries the new\n`verificationCode` when the second confirmation starts. Never\npersonal data: no personal code, no phone, no certificate subject.\n", "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"event": {"type": "string", "enum": ["signature.confirmation_required", "signature.signed", "signature.declined", "signature.expired", "signature.failed", "signature.cancelled"]}, "signatureId": {"type": "string", "format": "uuid"}, "status": {"$ref": "#/components/schemas/SignatureStatus"}, "occurredAt": {"type": "string", "format": "date-time"}, "externalId": {"type": "string"}, "step": {"type": "string", "enum": ["authentication", "signing"]}, "verificationCode": {"type": "string"}, "failureReason": {"$ref": "#/components/schemas/SignatureFailureReason"}, "completionMode": {"type": "string", "enum": ["signed", "sandbox_simulation"]}, "sandbox": {"type": "boolean"}, "document": {"type": "object", "properties": {"sha256": {"type": "string"}}}}, "required": ["event", "signatureId", "status", "occurredAt"]}}}}, "responses": {"401": {"$ref": "#/components/responses/Problem"}, "429": {"$ref": "#/components/responses/Problem"}, "200": {"description": "Acknowledged. Any 2xx stops redelivery."}}}}}, "components": {"securitySchemes": {"agentOAuth": {"type": "oauth2", "description": "Agent connections for Claude, ChatGPT, Gemini, Copilot and any MCP\nclient. OAuth 2.1 authorization code with PKCE S256, dynamic client\nregistration (RFC 7591) and RFC 9207 `iss` in every authorization\nresponse. Discovery: `/.well-known/oauth-protected-resource/mcp`\n(RFC 9728) and `/.well-known/oauth-authorization-server` (RFC 8414).\nThe person signs in with their eID, picks the company and allows the\nconnection; the token then acts exactly as that person's own API key\nwould, with the same role re-check on every request. Registration\naccepts the callback URLs of supported agent platforms and loopback\naddresses. Access tokens live one hour; refresh tokens rotate and a\nreused refresh token revokes the connection. The person sees and\nrevokes connections under Settings, API settings, Connected apps.\nHeadless signatures (`/v1/signatures`) are not available to agent\nconnections.\n", "flows": {"authorizationCode": {"authorizationUrl": "https://sign-api.tvarka.pro/oauth/authorize", "tokenUrl": "https://sign-api.tvarka.pro/oauth/token", "refreshUrl": "https://sign-api.tvarka.pro/oauth/token", "scopes": {"sign.read": "Read signings and download signed documents.", "sign.signings": "Send documents for signature, add and remind signers, cancel signings."}}}}, "bearerAuth": {"type": "http", "scheme": "bearer", "description": "`Authorization: Bearer <key>` for REST and MCP. Every active, identified\ncompany member with Sign access has production API access automatically.\nSelect the company, open https://sign.tvarka.pro/sign/app/settings/#api-keys\nand click Show or Copy. The secret is prepared on first use and reused.\nA `tsk_live_` key acts on production; existing `tsk_test_` sandbox keys\nremain supported. Sandbox is retired from ordinary onboarding.\n\nCompany administrators invite users by email and select Employee or\nRepresentative (all rights). The optional email checkbox only requests\nan AES-256 ZIP after identity confirmation; it does not grant API access.\nThe ZIP includes the API key and webhook secret. Its password is the\nlast four personal-code digits, including leading zeroes. Email failure\ndoes not roll back joining the company. Only the owner can reveal keys.\nHistorical hash-only keys remain valid; first Show/Copy prepares a\nrecoverable companion on the same integration. Deliberate revocations\nare never silently undone. Existing integration IDs and history remain.\n\nUsers and access has visible Change rights, Send API key and API settings\nactions without a More menu. A full-rights representative can administer\nbilling and delegate to other representatives. Pending and failed email\ndelivery appear inline; transport acceptance is not proof of inbox delivery.\n\nManager Replace key gives older keys at most 24 hours of overlap;\nthe successor appears in employee Settings. Manage access offers\nindividual-key revocation, whole-integration revocation, immediate\nreplacement, planned rotation and webhook-secret changes. Advanced\nreplacement, planned rotation and webhook-secret changes require\nrecent eID confirmation. Rotation/revocation leaves the webhook secret\nunchanged unless it is explicitly changed separately.\n\nThe key acts as a named workspace user. Removing that person's role\npermanently revokes its API access; restoring the role does not revive\nkeys. Suspended access blocks API use. Revocation stops the affected\nkeys immediately but does not cancel already-created signing orders.\nATK and Due Diligence credentials are separate. Full guidance:\nhttps://sign-api.tvarka.pro/docs#keys.\n"}}, "parameters": {"IdempotencyKey": {"name": "Idempotency-Key", "in": "header", "required": false, "description": "Optional tenant-and-operation-scoped create key (1-128 visible ASCII characters). Identical input replays the original response; changed input or an unresolved execution returns 409. After 90 days the response expires but the key remains reserved. Do not change keys to retry a request with an uncertain outcome.", "schema": {"type": "string", "minLength": 1, "maxLength": 128}}, "BatchId": {"name": "batchId", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}}, "FileToken": {"name": "fileToken", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}}, "SigningId": {"name": "signingId", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}}, "SignerId": {"name": "signerId", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}}, "SignatureId": {"name": "signatureId", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}}}, "responses": {"Problem": {"description": "An RFC 9457 problem document.", "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}}}}}, "schemas": {"CreateSignatureRequest": {"type": "object", "additionalProperties": false, "required": ["method", "signer", "document", "renderHash"], "properties": {"method": {"type": "string", "enum": ["smart_id", "mobile_id"]}, "signer": {"type": "object", "additionalProperties": false, "required": ["personalCode"], "properties": {"personalCode": {"type": "string", "pattern": "^[0-9]{11}$", "description": "Identity lock: only this person's signature is accepted. Never\nreturned and never sent in webhooks.\n"}, "phoneNumber": {"type": "string", "pattern": "^\\+3706[0-9]{7}$", "description": "Required for `mobile_id`, refused otherwise."}}}, "document": {"type": "object", "additionalProperties": false, "required": ["name", "profile"], "description": "Exactly one of `hash` or `pdf`.", "properties": {"name": {"type": "string", "minLength": 1, "maxLength": 120}, "profile": {"type": "string", "enum": ["cades-detached", "pades"]}, "hash": {"type": "object", "additionalProperties": false, "required": ["algorithm", "value"], "description": "The digest to sign, for `cades-detached`.", "properties": {"algorithm": {"type": "string", "enum": ["sha256"]}, "value": {"type": "string", "format": "byte", "description": "Base64, exactly 32 bytes."}}}, "pdf": {"type": "object", "additionalProperties": false, "required": ["content"], "description": "The PDF to sign, for `pades`. Base64, at most 15 MiB decoded;\nmust be an unencrypted PDF.\n", "properties": {"content": {"type": "string", "format": "byte"}}}}}, "display": {"type": "object", "additionalProperties": false, "properties": {"text": {"type": "string", "description": "Shown in the signer's app. Smart-ID shows up to 60 characters;\nMobile-ID up to 40, or 20 when the text has a Lithuanian\nletter with a diacritic. Printable characters only. Default:\n`<company name>: pasirašymas`.\n"}}}, "renderHash": {"type": "object", "additionalProperties": false, "required": ["algorithm", "value"], "description": "Digest of what you showed the signer. Kept as evidence.", "properties": {"algorithm": {"type": "string", "enum": ["sha256", "sha384", "sha512"]}, "value": {"type": "string", "format": "byte"}}}, "externalId": {"type": "string", "maxLength": 120}, "webhookUrl": {"type": "string", "format": "uri", "description": "Public HTTPS URL."}}}, "Signature": {"type": "object", "required": ["id", "status", "method", "step", "createdAt"], "properties": {"id": {"type": "string", "format": "uuid"}, "status": {"$ref": "#/components/schemas/SignatureStatus"}, "method": {"type": "string", "enum": ["smart_id", "mobile_id"]}, "step": {"type": "string", "enum": ["authentication", "signing"], "description": "The confirmation in flight. Tvarka decides which run."}, "verificationCode": {"type": ["string", "null"], "description": "Show this to the signer while `pending`."}, "expiresAt": {"type": "string", "format": "date-time"}, "externalId": {"type": ["string", "null"]}, "document": {"type": "object", "properties": {"name": {"type": "string"}, "profile": {"type": "string"}, "inputSha256": {"type": ["string", "null"], "description": "Hex digest the signature covers."}}}, "failureReason": {"$ref": "#/components/schemas/SignatureFailureReason"}, "sandbox": {"type": "boolean"}, "completionMode": {"type": "string", "enum": ["signed", "sandbox_simulation"]}, "createdAt": {"type": "string", "format": "date-time"}, "completedAt": {"type": ["string", "null"], "format": "date-time"}, "result": {"type": "object", "description": "Present once `signed`.", "properties": {"profile": {"type": "string"}, "level": {"type": ["string", "null"], "description": "For example CAdES-BASELINE-LT."}, "sha256": {"type": "string"}, "bytes": {"type": "integer"}, "signingTime": {"type": ["string", "null"], "format": "date-time"}, "timestampTime": {"type": ["string", "null"], "format": "date-time"}, "certificate": {"type": ["object", "null"], "properties": {"subjectCommonName": {"type": "string"}, "issuer": {"type": "string"}, "serialNumber": {"type": "string", "description": "Certificate serial, hex."}, "notAfter": {"type": "string", "format": "date-time"}, "qualified": {"type": "boolean", "description": "The certificate states it is qualified and on a qualified\nsignature creation device. A statement in the certificate,\nnot a trusted-list verdict.\n"}}}, "validation": {"type": ["object", "null"], "description": "Advisory. `notApplicable` for `cades-detached`: a detached\nsignature over a hash is validated beside the document, which\nTvarka never receives.\n"}, "available": {"type": "boolean", "description": "False once the artifact was erased."}}}}}, "SignatureStatus": {"type": "string", "enum": ["pending", "signed", "declined", "expired", "failed", "cancelled"]}, "SignatureFailureReason": {"type": "string", "enum": ["identity_mismatch", "certificate_not_qualified", "certificate_rotated", "provider_error", "timestamp_unavailable"], "description": "`identity_mismatch` - the certificate belongs to another person.\n`certificate_not_qualified` - the certificate cannot sign documents.\n`certificate_rotated` - the signer's certificate changed since Tvarka\nlast saw it; the new one is kept, so a retry needs one confirmation.\n`provider_error` - the signing service failed.\n`timestamp_unavailable` - no qualified timestamp could be obtained.\nNone of these is billed.\n"}, "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:\n`.pdf`; `.docx`, `.doc`, `.odt`, `.rtf`, `.xlsx`, `.xls`, `.ods`, `.pptx`, `.odp`, `.txt`, `.jpg`, `.jpeg` and `.png`, converted to PDF/A-2b and signed as a\nPDF (v1.9.0); `.asice`/`.sce`/`.bdoc` to countersign an existing\nappendable ASiC-E; `.adoc`/`.bedoc`/`.cedoc`/`.gedoc`/\n`.ggedoc` to countersign a signed ADOC-family container.\nFor containers the extension only decides that we look\ninside - the container's own mimetype member decides what\nit is. `.mdoc` is refused: an MDOC takes a first signature\nonly.\n"}, "content": {"type": "string", "format": "byte", "description": "Base64-encoded bytes, up to 15 MiB decoded. Provide exactly\none of `content`, `fileToken`, `markdown` or `url`.\n"}, "fileToken": {"type": "string", "format": "uuid", "description": "A reusable upload from `POST /v1/files`, referenced instead\nof carrying the bytes again. The bytes are copied into the\nsigning at creation, so purging the file later never\nreaches into a signing that already used it. Provide\nexactly one of `content`, `fileToken`, `markdown` or `url`.\n"}, "markdown": {"type": "string", "minLength": 10, "maxLength": 200000, "description": "The document as Markdown (CommonMark with tables and\nstrikethrough). Tvarka renders it to an A4 PDF/A-2b and signs\nthat PDF. Raw HTML is shown as text and nothing remote is\nloaded, so the PDF is exactly the text. The first `# `\nheading becomes the title when `title` is omitted, and\n`name` defaults to that title.\n"}, "url": {"type": "string", "format": "uri", "description": "A public HTTPS link Tvarka downloads the document from:\na PDF, a convertible Office file or image (converted to\nPDF/A-2b, v1.9.0), an appendable ASiC-E or a signed\nADOC-family container. Private and loopback addresses are refused, each\nredirect (up to three) is re-checked, and the download is\nbounded by the document size limit and 20 seconds. `name`\ndefaults to the last path segment.\n"}, "flatten": {"type": "boolean", "default": false, "description": "PDF only. Draw every form field's appearance into page\ncontent and drop the AcroForm, so what a reader sees cannot\ndepend on a viewer's form handling. Fails closed: if any\nwidget has no appearance stream Tvarka can draw faithfully,\nnothing is flattened and the response names those fields.\nSigning a live AcroForm without this is perfectly valid -\nthe signature still detects any later edit.\n"}}, "additionalProperties": false}, "container": {"type": "object", "description": "Compose a NEW container from several files and sign that, instead\nof posting one finished document. Mutually exclusive with\n`document`; exactly one of the two is required.\n", "properties": {"format": {"type": "string", "enum": ["asice", "adoc"], "description": "`asice` takes PDFs only. `adoc` is the Lithuanian ADOC-V1.0\npackage and takes `.pdf` and `.xlsx`. The first file is the\nmain document; the rest are appendices.\n"}, "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": "Participants in input order, which is the release order for a\nsequential signing. Each email may appear once.\n", "items": {"$ref": "#/components/schemas/SignerRequest"}}, "delivery": {"type": "string", "enum": ["link", "email"], "default": "link", "description": "`link` returns a `ceremonyUrl` per signer for you to deliver.\n`email` has Tvarka email each signer in your entity's name.\n"}, "returnUrl": {"type": "string", "format": "uri", "maxLength": 500, "description": "Exact HTTPS URL registered for this API client. Requires returnState. After signing, decline or expiry the same browser tab returns here. Browser results are navigation hints, never proof of signing; confirm through GET signing or a verified webhook."}, "returnState": {"type": "string", "minLength": 16, "maxLength": 128, "pattern": "^[A-Za-z0-9_-]+$", "description": "Random per-browser-session correlation value; required with returnUrl. Compare it on return. Never put personal data or secrets here."}, "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": {"type": "string", "enum": ["parallel", "sequential"], "default": "parallel"}, "softDeadlineInDays": {"type": "integer", "minimum": 1, "description": "Sets the soft deadline. On the day it passes, every signer who can\nstill sign is reminded once. Independent of `expiresInDays`, which\nis the hard deadline.\n"}, "expiresInDays": {"type": "integer", "minimum": 1, "maximum": 30, "default": 14}}, "required": ["signers"], "oneOf": [{"required": ["document"]}, {"required": ["container"]}], "description": "Exactly one of `document` (a finished file) or `container` (several\nfiles composed into a new one) is required. `title` is required\nunless `document.markdown` starts with a `# ` heading.\n", "additionalProperties": false}, "SignerRequest": {"type": "object", "properties": {"role": {"type": "string", "enum": ["signer", "viewer"], "default": "signer", "description": "A `viewer` is recorded but never invited, never gets a ceremony\nlink, and never holds up completion. Tvarka sends them nothing.\nAt least one signer is required.\n"}, "email": {"type": "string", "format": "email"}, "name": {"type": "string", "maxLength": 500}, "language": {"type": "string", "enum": ["lt", "en", "et", "lv", "pl", "uk", "ru", "de"], "default": "lt", "description": "Per signer, for their email and ceremony page. The list grew on 2026-09-14 and is additive: every value accepted before is still accepted, and the API answers an unknown one exactly as before."}, "personalCode": {"type": "string", "pattern": "^[0-9]{11}$", "description": "Optional Lithuanian identity lock. Tvarka stores a keyed hash\nand encrypted value, never returns the code, and refuses a\ndifferent authenticated signer.\n"}, "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;\nSmart-ID and Mobile-ID use the hosted page or mobile app.\n"}, "visibleSignature": {"$ref": "#/components/schemas/VisibleSignature"}}, "required": ["email"], "additionalProperties": false}, "VisibleSignature": {"description": "Entitled workspaces may place this signer's visible PDF stamp. Use\neither page coordinates in PDF points from the lower-left MediaBox\ncorner, or one existing unsigned signature field. The accepted value\nis immutable and returned on the signer. PDF only; field binding cannot\nbe combined with `document.flatten: true`.\n`page` is 1-based. Coordinates are finite PDF points (1/72 inch) in\nunrotated page space; x/y are offsets even when the MediaBox origin is\nnonzero. The whole box must fit MediaBox and CropBox. Minimum size is\n148 x 52 pt; placement requires /UserUnit: 1. A field must have\none usable visible widget, be unsigned and unlocked, and meet the\nsame geometry rules. Earlier field locks, ambiguous names and fields\nalready reserved for another signer are rejected. Explicit positions\ndo not become cascade anchors. Do not send this property for viewers.\nStamp entitlement is required, including when adding a signer. It does\nnot grant VIP whitelabel access. Omit the property to use normal corner\nplacement; configured workspace branding still applies.\n", "examples": [{"page": 1, "x": 36, "y": 36, "width": 148, "height": 52}, {"field": "Sig_Vadovas"}], "oneOf": [{"type": "object", "properties": {"page": {"type": "integer", "minimum": 1}, "x": {"type": "number", "minimum": 0}, "y": {"type": "number", "minimum": 0}, "width": {"type": "number", "minimum": 148}, "height": {"type": "number", "minimum": 52}}, "required": ["page", "x", "y", "width", "height"], "additionalProperties": false}, {"type": "object", "properties": {"field": {"type": "string", "minLength": 1, "maxLength": 200}}, "required": ["field"], "additionalProperties": false}]}, "SigningList": {"type": "object", "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/Signing"}}, "hasMore": {"type": "boolean"}, "nextCursor": {"type": ["string", "null"], "format": "uuid"}}, "required": ["data", "hasMore", "nextCursor"]}, "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\nposted. The signers who are waiting hold a ceremony link that\nanswers 404 until their turn, so the ordering is enforced rather\nthan merely unsent. A decline stops the workflow at the\ndecliner's turn: the signing settles `declined` and the signers\nstill waiting are cancelled.\n"}, "softDeadlineAt": {"type": ["string", "null"], "format": "date-time", "description": "The date the signing is wanted by, as opposed to `expiresAt`,\nwhich is the date it dies on. Nothing expires here; it is when the\none automatic reminder goes out.\n"}, "externalId": {"type": ["string", "null"]}, "createdAt": {"type": "string", "format": "date-time"}, "expiresAt": {"type": ["string", "null"], "format": "date-time"}, "completedAt": {"type": ["string", "null"], "format": "date-time"}, "completionMode": {"type": ["string", "null"], "enum": ["qes_ceremony", "sandbox_simulation", null], "description": "`qes_ceremony` means human eID signing. `sandbox_simulation`\nexercises lifecycle/webhook integration and creates no\nsignature; its artifact is flagged `sandbox: true`.\n"}, "erasedAt": {"type": ["string", "null"], "format": "date-time"}, "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"}}}, "Signer": {"type": "object", "properties": {"signerId": {"type": "string", "format": "uuid"}, "role": {"type": "string", "enum": ["signer", "viewer"], "description": "A `viewer` is a recorded non-signing party: no invitation is\nraised, no ceremony link exists, and they never hold up\ncompletion. Tvarka sends them nothing - you hold the webhook and\nthe download, and distribute. At least one signer is required.\n"}, "email": {"type": ["string", "null"], "format": "email"}, "name": {"type": ["string", "null"]}, "language": {"type": "string", "enum": ["lt", "en", "et", "lv", "pl", "uk", "ru", "de"]}, "status": {"$ref": "#/components/schemas/SignerStatus"}, "ceremonyUrl": {"type": "string", "format": "uri", "description": "Present only while this signer can still sign. A finished or\nretracted invitation has no live URL.\n"}, "signedAt": {"type": ["string", "null"], "format": "date-time"}, "declinedAt": {"type": ["string", "null"], "format": "date-time"}, "declineReason": {"type": "string"}, "methodsAllowed": {"type": "array", "items": {"$ref": "#/components/schemas/SigningMethod"}}, "actualMethod": {"oneOf": [{"$ref": "#/components/schemas/SigningMethod"}, {"type": "null"}], "description": "Present after a real signature; contains no identity data."}, "identityLocked": {"type": "boolean"}, "visibleSignature": {"oneOf": [{"$ref": "#/components/schemas/VisibleSignature"}, {"type": "null"}]}, "identityMatched": {"type": ["boolean", "null"], "description": "True only after a locked signer completes the real ceremony."}, "simulatedAt": {"type": ["string", "null"], "format": "date-time"}, "emailStatus": {"type": "string", "enum": ["not_requested", "pending", "accepted", "failed", "cancelled", "unknown"], "description": "Initial invitation email state. Accepted means the email backend\naccepted the message, not inbox delivery. Unknown denotes legacy\nbest-effort delivery whose outcome was not recorded. Sequential\nsigners stay not_requested until their turn is released.\n"}, "emailAcceptedAt": {"type": ["string", "null"], "format": "date-time"}}}, "Batch": {"type": "object", "properties": {"batchId": {"type": "string", "format": "uuid"}, "title": {"type": "string"}, "externalId": {"type": ["string", "null"]}, "createdAt": {"type": "string", "format": "date-time"}, "signings": {"type": "array", "items": {"$ref": "#/components/schemas/Signing"}}}}, "StoredFile": {"type": "object", "properties": {"fileToken": {"type": "string", "format": "uuid"}, "name": {"type": "string"}, "mediaType": {"type": "string"}, "sha256": {"type": "string"}, "bytes": {"type": "integer"}, "createdAt": {"type": "string", "format": "date-time"}, "expiresAt": {"type": "string", "format": "date-time", "description": "An unreferenced upload is not kept forever just because nobody\ndeleted it. Re-upload after this, or post the bytes inline.\n"}, "usedCount": {"type": "integer", "description": "How many signings have copied these bytes."}}}, "Comment": {"type": "object", "properties": {"commentId": {"type": "string", "format": "uuid"}, "author": {"type": "string", "enum": ["client", "tvarka"]}, "authorLabel": {"type": ["string", "null"]}, "body": {"type": "string"}, "createdAt": {"type": "string", "format": "date-time"}}}, "SignedDocument": {"type": "object", "description": "The newest signed output. Absent until somebody signs, except on a\nsandbox simulation, where it describes the unsigned artifact.\n", "properties": {"sha256": {"type": "string"}, "bytes": {"type": "integer"}, "mimeType": {"type": "string"}, "sandbox": {"type": "boolean", "description": "Present and true only on a sandbox simulation. The bytes are a\nreal file to which no signature was added.\n"}, "signed": {"type": "boolean", "description": "Present and false only on a sandbox simulation. Absent on a real\nsigning, where the output is signed by definition.\n"}}}, "SigningStatus": {"type": "string", "enum": ["pending", "completed", "declined", "cancelled", "expired", "failed"], "description": "`pending` - at least one signer can still sign.\n`completed` - every signer signed.\n`declined` - somebody declined and nobody else can still sign.\n`expired` - the window closed first.\n`cancelled` - you retracted it.\n`failed` - the signing could not be prepared.\n"}, "SignerStatus": {"type": "string", "enum": ["pending", "signed", "declined", "cancelled", "expired", "failed", "simulated", "waiting", "recorded"], "description": "`waiting` - a sequential signer whose turn has not come; their\nceremony link answers 404 until it does.\n`failed` - the signer's requested PDF stamp could not be prepared.\n`recorded` - a viewer. Never invited, never blocks completion.\n"}, "SigningMethod": {"type": "string", "enum": ["nfc", "smart_id", "mobile_id"], "description": "The orchestration methods. For direct LT ATK reader, USB, raw CAdES\nand lower-level NFC integration, use the ATK API.\n"}, "ProblemFieldError": {"type": "object", "description": "One rejected field.", "properties": {"pointer": {"type": "string", "description": "RFC 6901 JSON Pointer into the request body, for example\n`/signers/0/email` or `/container/files/2/content`.\n"}, "detail": {"type": "string"}}, "required": ["pointer", "detail"]}, "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\nthat name a field. Request-body validation collects rather than\nfailing fast, so a caller who got three fields wrong learns all\nthree from one response.\n", "items": {"$ref": "#/components/schemas/ProblemFieldError"}}, "recoveryUrl": {"type": "string", "format": "uri-reference", "description": "Present on the payment-required refusal when a web page exists\nthat fixes it: the tenant's payment setup, reached with an\nordinary browser login. Show it to your operator; it is not an\nAPI endpoint.\n"}, "billingContext": {"type": "object", "description": "Present on order-time payment-required refusals. Identifies the\ncompany fixed to the API key, regardless of browser workspace.\nPayment arrangements require an active company administrator (manager or full-rights representative).\n", "required": ["workspaceId", "companyCode", "companyName", "canManageBilling"], "properties": {"workspaceId": {"type": "string", "format": "uuid"}, "companyCode": {"type": "string"}, "companyName": {"type": "string"}, "canManageBilling": {"type": "boolean"}}}}}}}}