What this is about
A final document is kept at CRMS. Your application can read it again at any time, list the versions of a chain, and have CRMS check whether chain and bytes are still correct. For this there is a small group of endpoints under /crms/documents.
The endpoints
| Call | Role (default) | Answer |
|---|---|---|
GET /crms/documents?template=…&scope=…&reference=… | crms-document-reader | all revisions of the chain, oldest first |
GET /crms/documents/{revision} | crms-document-reader | the bytes, the description in headers |
POST /crms/documents/{revision}/verify | crms-document-reader | { "ok": …, "problem": … } |
POST /crms/documents/{revision}/legal-hold | crms-document-custodian | 204, see Retention |
POST /crms/documents/{revision}/legal-hold/release | crms-document-custodian | 204 |
POST /crms/documents/{revision}/erase | crms-document-eraser | 204 |
{revision}is a UUID in canonical form. Any other spelling is a violation on the fieldrevision.- You set the roles with
CODAMAI_CRMS_REST_DOCUMENT_ROLES_READ,_HOLDand_ERASE, each as a comma-separated list. One role from the list is enough, and an empty list admits nobody. - The role is checked before CRMS looks for the document. If it is missing: 403
document-access-denied. - You only see documents of your own tenant. A revision of another tenant does not exist for you: 404
document-not-found.
If CRMS runs as a separate service, crms-client offers the same calls through the same interface and forwards the user’s token. See The CRMS client.
Reading a document
GET /crms/documents/7c1e…200 OK
Content-Type: application/pdf
Content-Disposition: inline; filename="audit-report.pdf"
X-Content-Type-Options: nosniff
Crms-Document-Revision: 7c1e…
Crms-Document-Number: 2
Crms-Document-Format: PDF
Crms-Document-Stored-At: 2026-10-17T09:12:44.123456Z
Crms-Document-Retain-Until: 2036-10-17T09:12:44.123456Z
Crms-Document-Legal-Hold: false
Crms-Document-Content-Hash: sha-256:3b1d…
Crms-Template-Key: audit-report
Crms-Template-Scope: TENANT
Crms-Template-Variant: de
Crms-Template-Revision: a2f0…
Crms-Template-Revision-Hash: sha-256:9f86…
(the bytes)When reading, CRMS decrypts the bytes and compares them with the revision’s content hash. If they do not match, there is no document but 500 document-integrity-failed. An HTML document also gets Content-Security-Policy: sandbox, so that a script in it does not run in the name of your application.
| Case | Answer |
|---|---|
| the revision does not exist (for this tenant) | 404 document-not-found |
| the revision is erased | 410 document-erased |
| the bytes do not match the hash | 500 document-integrity-failed |
| the object is missing in the storage | 500 document-object-missing |
Logging reads
With CODAMAI_CRMS_AUDIT_READS=true, CRMS writes every read of a final document as document-read with the reader into the journal. The default is false. A check through verify does not count as a read.
Verifying a document
-
CRMSChainDoes every entry of the whole document chain give its stored hash?↳ no
ok: false,document-chain-broken -
CRMSErased?Does the revision still have bytes?↳ no
ok: true– a tombstone is intact if the chain is -
CRMSDecryptCan the bytes be decrypted with their key?↳ no
ok: false,document-decryption-failed -
CRMSContentDo the bytes match the content hash?↳ no
ok: false,document-integrity-failed ok: true
POST /crms/documents/7c1e…/verify{ "ok": false, "problem": "document-chain-broken" }If everything is fine, problem is missing: { "ok": true }. A finding is a normal answer with 200, not an error. The keys are listed in Errors.
If the object is missing in the storage, the check reports document-object-missing.
Verifying templates
The chain of a template variant can be verified as well: POST /crms/variants/{variant}/verify. The answer is { "intact": true } or { "intact": false, "brokenAt": 4 } with the first position that does not match. More in Life cycle of a template.
What the chain does not prove
| Question | Does the evidence show it? |
|---|---|
| Are the bytes unchanged since they were stored? | yes, through the content hash and the chain |
| Which approved template revision was it rendered with? | yes, through the template’s revision hash |
| Which data was it rendered with? | yes, whoever holds the data compares it with the payload hash |
| Who stored, held or erased it, and when? | yes, through user and time in the chain and the journal |
| Was the history recomputed consistently afterwards? | yes, the anchor reveals it |
| Was the data correct? | no, that is the responsibility of the application that sent it |