DDS anatomy and receipts
What a Due Diligence Statement actually contains, how eudr.dev produces it, and how an auditor reads the signed receipt.
Annex II structure (post-2025/2650)
Reg. (EU) 2025/2650 deleted Annex II point 4. The current Annex II has these numbered points:
| Point | Content | Article |
|---|---|---|
| 1 | Operator information — name, address, EORI. | Annex II point 1 |
| 2 | HS code(s) + product description + quantity. | 9(1)(a)+(b) |
| 3 | Country of production + geolocation of all plots + production time-range. | 9(1)(c)+(d) |
| ~~4~~ | ~~Upstream-DDS reference number.~~ | DELETED by Reg. 2025/2650. Downstream operators and traders still collect it (Art. 5(3)(a)) and keep it five years (Art. 5(4)); it is no longer a numbered Annex II field. |
| 5 | Declaration that the goods comply with Art. 3. | Annex II text. |
| 6 | Operator signature block. | Annex II text. |
The engine's JSON wire format mirrors this with keys 1_operator, 2_product, 3_geolocation, 5_declaration, 6_signature.
Engine extensions (not part of Annex II)
The engine adds three top-level fields to the receipt that are not part of the regulator-required Annex II content but are essential for offline verification:
source_receipts. The chain of content-addressed evidence (emem fact CIDs per cell, connector provenance, methodology pointers).conclusion. The engine's risk verdict + score + country tier.signature(top-level). The ed25519 signature over the canonical JSON of the receipt.
The signed-receipt envelope
{
"payload": { /* canonical Annex II JSON with engine extensions */ },
"cid": "bafkreih..", // CIDv1 of payload (BLAKE3 + base32-lower)
"signature_b64": "..", // ed25519 signature over cid bytes
"pubkey_b32": "ed25519:abc..", // signer's public key
"did": "did:web:eudr.dev",
"issued_at": "2026-05-18T12:14:22Z"
}
The CID is a deterministic BLAKE3 hash of the canonical JSON of payload. Any byte change in payload produces a different CID; the signature over the CID then fails verification.
How to read a receipt
Open /verify in a browser and paste the receipt JSON, or use the CLI:
eudr verify path/to/receipt.json
Both paths run the four offline checks described in verify-offline:
- Re-canonicalise the payload exactly as the engine did.
- Re-hash with BLAKE3 → re-encode as CIDv1; compare to the claimed
cid. - Verify the ed25519 signature over the CID bytes against the public key in the agent card at
https://eudr.dev/.well-known/agent-card.json. - Spot-check the emem fact CIDs in
source_receipts.emem.per_cell[].fact_cids[]againsthttps://emem.dev/v1/facts/<cid>.
Engine reference vs official reference
The dds_reference field in the payload is the engine's internal identifier (a UUID). TRACES NT assigns the official reference number + verification code on submission. The engine's reference is stable for client-side joining; the TRACES reference is what the regulator uses.
Once the operator has submitted via TRACES, they can update the receipt by storing the TRACES reference + code alongside.
Where the engine's keypair lives
The agent card at /.well-known/agent-card.json publishes the ed25519 public key in two forms (publicKeyMultibase and pubkey_b32) plus the DID did:web:eudr.dev.
The private key is held in AWS SSM Parameter Store under /eudr/prod/agent-privkey-b64.
See also
This page last changed on . It describes the Regulation as it stood then; where a date or a code matters to a decision, check it against the EUR-Lex text it cites.