Guides / Your first seal
Your first seal
From an empty Platform to a record anyone on earth can verify. The model in one sentence: you hash the document in your own systems, Rox seals the digest into an anchor on RoxChain, and the document itself never leaves you. A seal request is validated and either rejected immediately or accepted with 202; every accepted record then reaches exactly one terminal outcome, anchored with proof or failed.
1 · Set up in your Platform
Everything credential-shaped is created inside your Platform by your team; there is no public signup for keys. You need three things before the first API call:
- A vault: the isolation boundary your records live in.
- A record type (for example policy): it fixes the metadata schema and the publicly visible verify fields.
- A vault API key with the seal scope, and a request-signing key: generate the keypair below and register the PUBLIC half under a key id (kid) of your choice. Additional keys can later be registered over the API, signed by an existing key.
2 · Hash locally, then seal
Compute the SHA-256 of the exact bytes you want to be able to prove later, and send the digest to POST /v1/seal. The request carries your API key plus a detached JWS signed with your request-signing key, so possession of the API key alone can never anchor in your name. Send an Idempotency-Key so a network retry can never create a duplicate record. The signing rules, exactly:
- Every mutation must carry the JWS; an unsigned mutation is refused with RR-1101.
- Reads are authorized by the API key alone, the JWS is optional there. But a presented signature is never ignored: a signed read is validated exactly like a mutation, so a retired or unknown kid gets RR-1101 on any endpoint it is presented to. A passing signed read is therefore proof that the kid is still registered and active, and a signed read is the simplest way to verify a key rotation or retirement took effect.
- Keys are vault-scoped, on both sides: an API key belongs to one vault, and the kid you sign with must be registered on that same vault. One kid cannot serve two vaults; give each vault its own request-signing key.
3 · Store what the 202 gives you
The acceptance body carries three values with different jobs:
- record_id: your API handle for the record.
- reference (rr_ref_...): public, safe to print on the document and in QR codes.
- salt_hex: returned once, never again. Store it alongside your document; byte-exact verification needs it, and it is what keeps enumerable documents unguessable from the public record. It is never public and never on-chain.
4 · Wait for the terminal outcome
The 202 means validated and accepted, not anchored. Exactly one terminal outcome follows asynchronously: anchored (with anchor coordinates and proof) or failed. Consume it push-style via the record.anchored / record.failed webhooks (see the webhooks guide), or poll the record. Build on the outcome, not on any expectation of when it lands; the acceptance carries no timing fields, and anchoring scheduling is internal to Rox.
5 · Verify like a stranger would
Verification is public by design: no key, no login, and the response never contains the salt or the digest. A reference lookup proves the record exists and is anchored; the digest + salt check proves the exact bytes you hold are the sealed bytes. People with a verify link or stamped QR use registry3.pebe.io; machines call the verify API directly.
Next: subscribe to webhooks so outcomes push to you, put the proof on the document itself with stamping, or commit per-field hashes with selective disclosure so you can later prove single fields without revealing the document. The SDKs wrap every call on this page.