The perito package
What a notification's evidence package (.zip) contains and how a perito verifies it without access to Notificado.
Updated:
The evidence package is a .zip that a perito (expert witness), a judge or the other party verifies with standard tools and no access to Notificado. Download it from the notification, under Notifications, together with the constancia PDF. Every download is recorded.
From the API or an assistant
An assistant connected over MCP does not receive the package's bytes: a .zip of tens of megabytes does not fit in a tool answer. The evidenceDownloadInfo read (scope evidence:read, also at GET /v1/evidence-download-info) takes orgId, notificationId, kind (zip when omitted, or constancia) and, for an earlier version, version. It answers the file name (filename), its type (contentType), its size in bytes (bytes), its SHA-256 (sha256), the package format version (zipVersion), the constancia it belongs to (constanciaId) and, in fetch, the HTTP call that downloads it: method, path and the exact body. That read records nothing. The download is that call, POST /v1/download-evidence with the same token, and it is recorded like any other download.
With no constancia yet, both answer 404 X_CONSTANCIA_NOT_FOUND; while the package is still being built, 409 X_EVIDENCE_ZIP_NOT_READY (retry after 30 seconds).
curl -X POST 'https://www.notificado.co/v1/download-evidence' \
-H "authorization: Bearer $NOTIFICADO_TOKEN" \
-H 'content-type: application/json' \
-d '{"orgId":"<orgId>","notificationId":"<notificationId>","kind":"zip","version":1}' \
| jq -r .contentBase64 | base64 -d > evidencia.zip
sha256sum evidencia.zipThe example asks for version 1; send the body of fetch exactly as it came, which names the version described. The hash sha256sum prints must equal the sha256 that evidenceDownloadInfo and the download answered. If it differs, the file was damaged on the way: download it again before handing it over.
What it contains
| File | What it is |
|---|---|
manifest.json |
Every file with its SHA-256 and size; the timestamps and what each covers. |
SHA256SUMS |
The same hashes, for sha256sum -c. |
mensaje/*.eml |
Each recipient's frozen message, as handed to the mail provider. |
adjuntos/ |
The documents, byte for byte. |
enlazados/ |
Not in the package: the folder where you put a copy of the linked files. |
eventos/eventos.json |
Chained events (previous hash + hash), daily anchors and Merkle proofs. |
eventos/raw/ |
What the providers answered, as received. |
sellos/*.tsr |
The RFC 3161 timestamp tokens. |
sellos/tsa-ca/*.pem |
Each timestamp authority's root of trust. |
constancia.pdf |
The signed constancia. |
firma/cert-chain.pem |
The chain of trust of the constancia's signature. |
verificar.js |
Automatic verifier. |
LEEME.txt / README.txt |
Instructions in Spanish and in English. |
Automatic verification
With Bun, openssl, sha256sum and pdfsig (poppler-utils) installed, inside the unzipped folder:
bun verificar.jsExit 0 = verified; 1 = something does not verify (it is named); 2 = unreadable package; 3 = openssl missing; 4 = verified, except a linked file that was not supplied and was not checked.
The report has four parts, always in the same order:
- Checked: each area (files, event chain, the files the events name, daily anchor, timestamps) marked
OK,WARNorFAIL; the SHA-256 ofmanifest.json, which identifies the whole package; and the stretch of the chain it covers (first and lastseq). - Timestamps: for each token, the authority, the file it covers and the time the authority itself asserts, read from the token.
- Findings: every failure, warning or note, naming the file or event behind it.
- Not checked by this program: the PDF's PAdES signature (
pdfsig), trust in each timestamp authority, and Amazon SNS's signature on the raw responses, which needs the network. Those are checked with the public tools named there.
The last line is always the result. bun verificar.js --json gives the same as JSON.
Manual verification
sha256sum -c SHA256SUMS
openssl ts -reply -in sellos/<stamp>.tsr -text
openssl ts -verify -data mensaje/<recipient>.eml -in sellos/<stamp>.tsr -CAfile sellos/tsa-ca/<authority>.pem
pdfsig constancia.pdfEach timestamp must answer Verification: OK. In the chain, every event satisfies hash = sha256(previous_hash followed by the event in RFC 8785 canonical JSON): altering, removing or reordering one event breaks every event after it.
Linked files
A file too large to travel attached to the email is made available to the recipient by download link. That file is not inside the zip: the package names it by its SHA-256 fingerprint and the constancia lists it as "puesto a disposición por enlace de descarga". A package with linked files says "version": 2 in manifest.json and carries the linkedFiles list (fingerprint, size, name and the date until which Notificado keeps the file). A package without them is still version 1.
How to obtain the file. Ask the firm that sent the notification, which keeps its original, or the recipient, for a copy. Any copy will do: what identifies the file is its SHA-256 fingerprint, not where it comes from.
How to check it. Copy the file into an enlazados/ folder inside the unzipped package, or name the folder that holds it:
mkdir enlazados && cp /path/to/the/file enlazados/
bun verificar.js
bun verificar.js --linked /path/to/the/folder
sha256sum enlazados/*The verifier computes the fingerprint of each copy and compares it with the one the message.frozen event froze. It also checks that the name manifest.json gives the file is the one that event froze. There are three outcomes:
- The copy is the file. The report marks it as verified and the exit code is
0. - Nobody supplied a copy. The rest of the package verifies, the file is listed as
NOT CHECKEDand the exit code is4. It is not a failure and it is not a pass either: the program did not see that file. - The copy has the file's name and another fingerprint. It is a failure (
linked-file-hash) and the exit code is1: it is not the notified file.
The timestamp of a linked file covers its fingerprint and is checked with openssl ts -verify -digest <fingerprint>; README.txt prints the exact line. On the constancia, each recipient lists what is recorded about each linked file: Descarga iniciada (the link.download event) and Transmisión del servidor (the link.download.transmitted event, with the bytes transmitted and the fingerprint of what was transmitted), or "sin descarga registrada".
link.download.transmitted says what Notificado's server did: it wrote the whole file to the connection from which the link was used. It does not prove that the recipient received or kept it: a connection that closes without reading the last bytes cannot be told from one that read them.
Matching a delivered copy
If the recipient or the other party produces the email that reached their mailbox, do not compare it by the .eml's SHA-256: the file under mensaje/ is what Notificado handed to the mail provider, and the provider re-encodes the message and sets its own headers in transit (the date, the MIME boundaries, the header order, the DKIM signature). The hash of the whole file will differ even though the content is the same. Match it like this:
- The Message-ID. The delivered copy's
Message-IDheader is the same as the.eml's and the one themessage.frozenevent names. - Each attachment. Extract the attachments from the delivered copy and compute their SHA-256: it must be the one under
adjuntos/, inmanifest.jsonand printed on the constancia. - The text. The text and the HTML are the same, trailing whitespace aside. The sender, the recipient, the reply address and the subject read the same.
grep -i '^Message-ID:' delivered-copy.eml mensaje/<recipient>.eml
sha256sum adjuntos/*Which method version applies
README.txt states the evidence-method version the constancia was issued under (for example 2026-09-v3). That version is recorded in the chained constancia.issued event (methodVersion). In packages issued before it was recorded, the version appears on the constancia, in its "Versión del software" (software version) field.
What it does not prove
That a person read the message: openings are indicios. Nor who clicked a download link: only when, from which IP and with which browser. The judge decides whether the notification is valid.