Verification
Tento obsah zatiaľ nie je dostupný vo vašom jazyku.
Anyone can check the signatures of a signed PDF: a TrustInk document, or any other. Adobe Reader shows TrustInk’s signatures as “validity unknown” until the TrustInk root is trusted; the verify routes validate them, and every other PDF signature, with the EU’s DSS library. The page trustink.io/verify uses the same routes.
Two public routes, without authorization:
| Route | What it does |
|---|---|
POST /verify/upload |
answers a presigned PUT for the PDF to check |
POST /verify |
validates the PDF uploaded under key and answers the report |
The PDF goes straight to storage, so documents up to 25 MB fit.
-
Ask for an upload URL. The request takes no body:
Terminal window API=https://api.trustink.ioUPLOAD=$(curl -s -X POST $API/verify/upload){"key": "verify/7c1f4d7e-9b1a-4c55-8f0e-2d6a1b3c4e5f.pdf","url": "https://…","method": "PUT","headers": {"Content-Type": "application/pdf"},"expiresAt": "2026-09-26T12:15:00.000Z","maxSize": 26214400}The URL works for 15 minutes.
-
PUT the PDF:
Terminal window curl -s -X PUT -H 'Content-Type: application/pdf' --upload-file signed.pdf "$(echo "$UPLOAD" | jq -r .url)" -
Validate it:
Terminal window curl -s -X POST -H 'Content-Type: application/json' \-d "{\"key\":\"$(echo "$UPLOAD" | jq -r .key)\"}" $API/verifyThe upload is deleted once it is validated.
The report
Section titled “The report”{ "documentSha256": "c54348f52e84885bc0666df13168cf14fe842d178fc649891cb9d63ae6054eb2", "issuedByTrustInk": true, "validationTime": "2026-09-26T15:23:48.077Z", "trustedListsIssuedAt": "2026-09-24T12:04:06Z", "signatures": [ { "id": "S-3F5C04CAC7B2CABDFB700EB35661335D5FD34908CBC900B89FC06B84AD15BB52", "signedBy": "Jana Nováková", "signingTime": "2026-09-26T11:48:19Z", "bestSignatureTime": "2026-09-26T15:23:48.077Z", "indication": "TOTAL_PASSED", "level": "LTA", "format": "PAdES-BASELINE-LTA", "qualification": "N/A", "issuedByTrustInk": true, "timestamps": [ {"type": "SIGNATURE_TIMESTAMP", "producedBy": "www.freetsa.org", "productionTime": "2026-09-26T11:48:21Z", "indication": "INDETERMINATE", "subIndication": "NO_CERTIFICATE_CHAIN_FOUND", "qualification": "N/A"}, {"type": "DOCUMENT_TIMESTAMP", "producedBy": "www.freetsa.org", "productionTime": "2026-09-26T11:48:35Z", "indication": "INDETERMINATE", "subIndication": "NO_CERTIFICATE_CHAIN_FOUND", "qualification": "N/A"} ] } ]}This is a test document. Its timestamps come from a timestamp authority that no trusted list names, which
is why they are INDETERMINATE and bestSignatureTime is the validation time. TrustInk now asks a
qualified timestamp authority first, whose timestamps count as QTSA.
| Field | Meaning |
|---|---|
documentSha256 |
hex SHA-256 of the uploaded file |
issuedByTrustInk |
a signature’s certificate chains to the TrustInk root; also per signature |
trustedListsIssuedAt |
when the EU list of trusted lists the validation used was issued |
signatures[].indication |
ETSI EN 319 102-1: TOTAL_PASSED, INDETERMINATE or TOTAL_FAILED; subIndication says why, e.g. HASH_FAILURE for a document changed after signing, NO_CERTIFICATE_CHAIN_FOUND for a signer nobody trusts, OUT_OF_BOUNDS_NO_POE for an expired certificate without a trusted timestamp |
signatures[].level |
B-B, B-T, LT or LTA; format has DSS’s full name (PAdES-BASELINE-LTA, PKCS7-B, PDF-NOT-ETSI) |
signatures[].qualification |
the eIDAS qualification as DSS names it: QESig, QESeal, AdESig-QC, AdESig, N/A, or Indeterminate QESig and the like when the signature could not be proven |
signatures[].bestSignatureTime |
the earliest time the signature is proven to exist: its first trusted timestamp, else the validation time |
signatures[].timestamps |
the signature’s own timestamp and the document timestamps that cover it, each with its indication and QTSA, TSA or N/A |
signatures[].errors |
DSS’s reasons for an indication other than TOTAL_PASSED |
An unsigned PDF is not an error: it answers signatures: [].
What is trusted
Section titled “What is trusted”Validation uses DSS with its default validation policy and trusts two sources:
- The TrustInk root, so TrustInk’s documents pass although no trusted list names the TrustInk PKI.
- The EU trusted lists: the EU list of trusted lists and the national lists it points to, refreshed daily, for everyone else’s signatures and for the timestamp authorities. They decide the qualification.
Revocation data a document does not carry is fetched online. TrustInk’s documents carry theirs: they are extended to PAdES B-LTA, so they stay verifiable after the signers’ one-shot certificates, valid 24 hours, have expired.
Errors
Section titled “Errors”| Status | code |
When |
|---|---|---|
| 400 | (validationErrors) |
no key, other fields, or a key that is not verify/<uuid>.pdf |
| 404 | DOCUMENT_NOT_FOUND |
nothing was uploaded under the key, the upload expired, or it was verified already |
| 413 | DOCUMENT_TOO_LARGE |
the upload is larger than 25 MB; it is deleted |
| 422 | NOT_A_PDF |
the upload does not start with %PDF- in its first kilobyte; it is deleted |
| 422 | UNREADABLE_DOCUMENT |
the PDF or its signatures cannot be read; it is deleted |
| 429 | too many requests ({message} only) |
|
| 503 | TIMEOUT |
the validation did not finish in time; the upload stays for a retry |
Both routes are rate limited for all callers together: POST /verify/upload at 2 requests a second (burst 10),
POST /verify at 1 a second (burst 5). Retry a 429 after a short pause.
The audit trail
Section titled “The audit trail”Every case keeps an audit trail: who did what, when, from where, and on which document. It is the evidence
behind the signature. Read it with GET /cases/{caseId}/events, newest first:
{ "events": [ {"eventId": "…", "type": "participant.signed", "occurredAt": "2026-09-24T08:00:01.512Z", "actor": "participant", "participantId": "…", "ip": "203.0.113.7", "userAgent": "Mozilla/5.0 …", "documentSha256": "9f2c…", "data": {"signatureCount": 1}} ], "nextCursor": "eyJwayI6…"}?limit= takes 1 to 100 events per page (100 by default); anything else answers 400 INVALID_LIMIT. While
older events remain, nextCursor is set: pass it as ?cursor= for the next page. A cursor continues only
the listing it came from; any other answers 400 INVALID_CURSOR.
The trail records, among others, case.created, document.uploaded (with the document’s SHA-256),
participant.invited, participant.viewed, participant.otp_verified, participant.consented,
participant.signed, participant.certificate_issued, participant.signature_applied, case.finalised and
case.completed. Events caused by a request carry the IP address and user agent. participant.viewed,
participant.consented and participant.signed carry the SHA-256 of the original, so the trail shows which
bytes the participant saw and agreed to. The trail never holds tokens or the participants’ personal data.
Consent
Section titled “Consent”POST /participant/{participantId}/signature requires the consent the signing page showed:
consent: {textVersion, acceptedAt}, with acceptedAt an ISO timestamp with an offset. Optional client
hints (time zone, language, screen size, whether every page was viewed) are recorded with it.
The evidence summary
Section titled “The evidence summary”Next to the signed PDF, every completed case has a sealed evidence summary PDF (evidenceDocument),
extended to PAdES B-LTA like the document. It names the case, the original’s SHA-256 and the signed
document’s name, SHA-256 and level; for each participant the masked contact, when they were invited, opened
the document, verified the one-time code, consented, signed and had the signature applied, the IP address and
browser, the consent text version and their certificate; and the seal and timestamp authorities. It is a
separate PDF on purpose: a page added to the signed document would break its signatures.