Verifying Document Seals
Use POST /v1/verifications to confirm a verification seal is genuine — no account required.
After the secure enclave processes a document, it generates a verification seal: a cryptographic signature over a payload derived from the document's content hash, identity, timestamp, and verification outcome. The seal lets any third party — without an account, without trusting the platform's infrastructure — confirm that a specific document was processed by an attested enclave and has not been altered since.
Think of it as a notary stamp: the notary (enclave) attests "I examined this document; it was authentic at this moment." Anyone who has the notary's public key can verify that stamp independently, without contacting the notary's office.
Unauthenticated endpoint. POST /v1/verifications requires no Authorization header. Verifiers
are third parties, not platform users. Rate limiting applies.
The Three-Party Model
Document verification involves three roles:
- Owner — uploads a document. The secure enclave processes it and attaches a seal.
- Verifier — a third party (auditor, regulator, counterparty) who receives the document and seal from the owner through any channel.
- Server — provides the public verification endpoint. The verifier calls it, but does not need to trust the platform to interpret the result.
The key property: the verifier independently checks a cryptographic signature using the platform's public key. If the server were to tamper with the stored seal or swap signatures, the check would fail. The server cannot forge a valid seal retroactively.
The Signature Algorithm
Seals are produced using ECDSA with SHA-256 over the NIST P-256 curve, backed by a hardware-protected platform signing key held in the key management service. The signing key never leaves the key management service boundary — signing and verification happen inside the service, not in application memory.
ECDSA with SHA-256 is the current signing algorithm. The intended design is a hybrid ML-DSA-65 + Ed25519 scheme, providing post-quantum resistance for long-lived seals. The hybrid path is tracked as engineering gap #24 and is not yet wired. The algorithm field in the response reflects the actual algorithm in use.
Where The Seal Comes From
Fetch the document detail endpoint to retrieve the seal fields:
curl https://api.kyndex.co/v1/documents/<document-id> \
-H 'Authorization: Bearer <access_token>'The response includes:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "processed",
"seal_encrypted": "<base64-encrypted-seal-blob>",
"seal_commitment": "<base64-commitment-hash>",
...
}Both seal_encrypted and seal_commitment are null until the processing pipeline produces a seal. They may remain null on processed documents if the processing path did not complete verification — treat null as "no seal available," not as a failure.
The Two Seal Fields
The seal is stored as two complementary values:
seal_encrypted— the completeVerificationSealstructure, encrypted with the document's DEK. Contains the content hash, document identity, timestamp, verification outcome, and platform signature. Only the document owner (or a grant holder) can decrypt it.seal_commitment— a SHA-256 hash of the complete JSON-serialized seal, stored unencrypted in the database. It cannot reconstruct the seal, but it proves the seal existed in a specific state at a specific time. Ifseal_encryptedwere replaced after the fact, the stored commitment would no longer match.
The owner decrypts seal_encrypted using the document key, then extracts the signing payload and platform signature to share with the verifier. The verifier submits these two values to POST /v1/verifications.
The Request
curl -X POST https://api.kyndex.co/v1/verifications \
-H 'Content-Type: application/json' \
-d '{
"data": "<base64-signing-payload>",
"signature": "<base64-platform-signature>"
}'Request Fields
| Field | Type | Description |
|---|---|---|
data | base64 string | The signing payload extracted from the decrypted seal |
signature | base64 string | The DER-encoded ECDSA platform signature from the decrypted seal |
Both fields must be standard base64 (not URL-safe). The server SHA-256 hashes data before passing it to the key management service — submit the signing payload bytes, not a pre-hashed digest.
The Response
200 OK:
{
"valid": true,
"algorithm": "ECDSA_SHA_256"
}| Field | Type | Description |
|---|---|---|
valid | boolean | Whether the signature verified successfully |
algorithm | string | Signing algorithm used — reflects the actual signing key |
The endpoint always returns 200 when the request is well-formed. The valid field carries the result:
valid: true— the signature is genuine. The signing payload was produced by the platform signing key. The document content matches what the enclave saw at processing time.valid: false— the signature does not verify. The data has been modified, the signature bytes are wrong, or the two values were not produced together. Treat the document as tampered.
What A Valid Seal Proves
A valid: true response makes three assertions:
- Enclave provenance — the seal was generated by an attested enclave running verified software. A tampered or impersonated enclave cannot access the platform signing key and therefore cannot produce a valid seal.
- Content integrity — the signing payload binds the document's content hash at processing time. If a single byte of the document changes after processing, the seal no longer matches.
- Non-transferability — the payload includes the document's identity. The seal cannot be re-used to vouch for a different document.
A valid: false response does not distinguish between a tampered document and corrupted seal transmission. The verifier should reject the document and request a fresh copy from the owner.
Error Cases
All errors follow RFC 9457 (Content-Type: application/problem+json):
{
"type": "https://api.kyndex.co/errors/INVALID_REQUEST",
"title": "Bad Request",
"status": 400,
"detail": "Invalid base64 in data field",
"instance": "/v1/verifications"
}| Status | Cause | Resolution |
|---|---|---|
| 400 | Malformed base64 in data or signature | Verify the base64 encoding — standard alphabet, valid padding |
| 429 | Rate limit exceeded | Back off and retry |
| 500 | Key management service verification call failed | Transient infrastructure error; retry with exponential backoff |
A 500 does not indicate an invalid seal — it indicates the check could not be completed. Retry before treating the seal as invalid.
Integration Checklist
Before shipping a verifier integration, confirm:
- Your base64 decoder handles standard padding (
=suffix); reject URL-safe input at the boundary. - You treat
valid: falseand5xxdifferently: false = reject the document; 5xx = retry. - You enforce a maximum retry budget for 500s (3 attempts with exponential backoff is reasonable).
- You do not cache
valid: trueresponses indefinitely — if the document owner later indicates the seal should be re-checked, re-verify. - You handle
nullseal fields gracefully — not all processed documents are guaranteed to have a seal.
Related
- Encryption Guarantees — how verification seals fit into the broader tamper-evidence model
- Key Hierarchy — the platform signing key and how it is protected
- Document Upload — where seals are generated (enclave processing step)