Gatefee receipts — format v1
A receipt is a signed, independently verifiable record of what was licensed, from whom, under which terms, and when. It is issued in the same database transaction as the retrieval it describes: a document is served with a receipt, or it is not served.
You can verify a receipt with nothing but this page, a standard JWS library, and our public keys. You do not need to call us, and you do not need to trust us. This page describes a data format. It is not legal advice.
What a receipt is
A compact JWS (RFC 7515), signed with Ed25519 (alg: EdDSA, RFC 8037). Three base64url parts joined by dots:
header.payload.signature.
Header
{ "alg": "EdDSA", "kid": "<key id>", "typ": "gatefee-receipt+jwt" }
kid is the RFC 7638 JWK thumbprint (SHA-256, base64url) of the signing key, so you can check that a key
in our key set really is the key it claims to be.
Payload. The payload bytes are the RFC 8785 (JSON Canonicalization Scheme) form of the claims — keys sorted, no whitespace — so the signed bytes have exactly one reading.
| Claim | Meaning |
|---|---|
iss |
The issuer: an https origin. |
jti |
The receipt id (UUID). |
iat |
When the retrieval was licensed, in whole seconds (Unix time). Rights were evaluated at this same instant. |
sub |
The licensee: the buyer organisation's id. |
gp.v |
Claim-set version. This page describes 1. |
gp.publisher_org_id |
The licensor: the publisher organisation's id. |
gp.document_id, gp.version_id, gp.version_no |
The document, and the exact immutable version that was served. |
gp.content_hash |
sha256: + the SHA-256 of the body that was served. Hash the body_md you received; it must match. |
gp.canonical_url |
The publisher's URL for the document. |
gp.purpose |
The licensed purpose: rag, display or summarize. |
gp.terms_hash |
SHA-256 of the public terms document (below). |
gp.terms_url |
Where that document can be fetched — publicly, without an account. |
gp.list_price_micro, gp.currency |
The publisher's list price for this purpose, in millionths of a US dollar. |
gp.request_id |
The X-Request-Id of the API call, to join the receipt to your own logs. |
gp.attribution_required |
Whether the publisher requires attribution. |
gp.max_retention_days |
How long the content may be retained; null means no limit is stated. |
What a receipt deliberately does not contain: what the buyer actually paid, how the retrieval was accounted for (part of a plan, metered overage, or free tier), their plan, spend cap or any negotiated rate; any person's name, email or IP address; any API key. A receipt is meant to be shown to customers, auditors and regulators, and it discloses nothing about the buyer's commercial terms: two buyers on different plans retrieving the same document under the same grant hold receipts that differ only in their identifiers and time.
A later version of Gatefee may add claims. It will never change the meaning of a claim listed here
without changing gp.v. Verifiers should ignore claims they do not recognise.
The terms behind terms_hash
GET <terms_url> returns a small JSON document — the publisher's grant (purpose, list price, attribution,
retention, geography, scope, effective date) and the version of the public buyer terms that applied. It
contains nothing specific to any buyer. The response body is exactly the bytes that were hashed:
sha256(response body) == gp.terms_hash
How to verify
- Fetch our keys:
GET <iss>/.well-known/jwks.json. Keys are never removed — a retired key stays published for at least seven years — so an old receipt stays verifiable. - Find the key whose
kidequals the receipt header'skid. - Verify the JWS signature with that key (
EdDSA). Accept no other algorithm, whatever the header says. - Check the header
typisgatefee-receipt+jwtandissis the issuer you expect. - Optionally: hash the body you were served and compare with
gp.content_hash; fetchgp.terms_urland compare its SHA-256 withgp.terms_hash.
A receipt is proof of what was licensed at iat. If a publisher later revokes a version, past receipts stay
valid; future retrievals stop.
Known-answer test vector
Use this to test a verifier — yours or ours. The key below is a published test key (RFC 8032 §7.1, TEST 1). It is not a Gatefee signing key, it appears in no Gatefee key set, and no real receipt is ever signed with it. Ed25519 signatures are deterministic, so signing the input below with this key must reproduce the signature exactly; we reproduced it byte for byte with OpenSSL 3.
Test private key (32-byte seed, hex): 9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60
Test public key set:
{
"keys": [
{
"kty": "OKP",
"crv": "Ed25519",
"x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
"kid": "kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k",
"alg": "EdDSA",
"use": "sig"
}
]
}
Claims:
{
"gp": {
"attribution_required": true,
"canonical_url": "https://news.example/local/council-vote",
"content_hash": "sha256:b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
"currency": "USD",
"document_id": "00000000-0000-7000-8000-0000000000d0",
"list_price_micro": 8000,
"max_retention_days": 30,
"publisher_org_id": "00000000-0000-7000-8000-0000000000b0",
"purpose": "rag",
"request_id": "01932f4e-7b3a-7c1d-9e2f-000000000002",
"terms_hash": "7a3ed7e6ee4a6ec4b06cc650b206eb8fa4386ca13daf40d9c51777b7386545f9",
"terms_url": "https://dev.gatefee.invalid/v1/terms/7a3ed7e6ee4a6ec4b06cc650b206eb8fa4386ca13daf40d9c51777b7386545f9",
"v": 1,
"version_id": "00000000-0000-7000-8000-0000000000d1",
"version_no": 3
},
"iat": 1789819200,
"iss": "https://dev.gatefee.invalid",
"jti": "01932f4e-7b3a-7c1d-9e2f-000000000001",
"sub": "00000000-0000-7000-8000-0000000000e0"
}
Payload — the RFC 8785 bytes of those claims (one line, UTF-8):
{"gp":{"attribution_required":true,"canonical_url":"https://news.example/local/council-vote","content_hash":"sha256:b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9","currency":"USD","document_id":"00000000-0000-7000-8000-0000000000d0","list_price_micro":8000,"max_retention_days":30,"publisher_org_id":"00000000-0000-7000-8000-0000000000b0","purpose":"rag","request_id":"01932f4e-7b3a-7c1d-9e2f-000000000002","terms_hash":"7a3ed7e6ee4a6ec4b06cc650b206eb8fa4386ca13daf40d9c51777b7386545f9","terms_url":"https://dev.gatefee.invalid/v1/terms/7a3ed7e6ee4a6ec4b06cc650b206eb8fa4386ca13daf40d9c51777b7386545f9","v":1,"version_id":"00000000-0000-7000-8000-0000000000d1","version_no":3},"iat":1789819200,"iss":"https://dev.gatefee.invalid","jti":"01932f4e-7b3a-7c1d-9e2f-000000000001","sub":"00000000-0000-7000-8000-0000000000e0"}
Signing input — base64url(header) + "." + base64url(payload) — and the receipt:
eyJhbGciOiJFZERTQSIsImtpZCI6ImtQcktfcW14VldhWVZBOXd3QkY2SXVvM3ZWeno3VHhIQ1R3WEJ5Z3JTNGsiLCJ0eXAiOiJnYXRlZmVlLXJlY2VpcHQrand0In0.eyJncCI6eyJhdHRyaWJ1dGlvbl9yZXF1aXJlZCI6dHJ1ZSwiY2Fub25pY2FsX3VybCI6Imh0dHBzOi8vbmV3cy5leGFtcGxlL2xvY2FsL2NvdW5jaWwtdm90ZSIsImNvbnRlbnRfaGFzaCI6InNoYTI1NjpiOTRkMjdiOTkzNGQzZTA4YTUyZTUyZDdkYTdkYWJmYWM0ODRlZmUzN2E1MzgwZWU5MDg4ZjdhY2UyZWZjZGU5IiwiY3VycmVuY3kiOiJVU0QiLCJkb2N1bWVudF9pZCI6IjAwMDAwMDAwLTAwMDAtNzAwMC04MDAwLTAwMDAwMDAwMDBkMCIsImxpc3RfcHJpY2VfbWljcm8iOjgwMDAsIm1heF9yZXRlbnRpb25fZGF5cyI6MzAsInB1Ymxpc2hlcl9vcmdfaWQiOiIwMDAwMDAwMC0wMDAwLTcwMDAtODAwMC0wMDAwMDAwMDAwYjAiLCJwdXJwb3NlIjoicmFnIiwicmVxdWVzdF9pZCI6IjAxOTMyZjRlLTdiM2EtN2MxZC05ZTJmLTAwMDAwMDAwMDAwMiIsInRlcm1zX2hhc2giOiI3YTNlZDdlNmVlNGE2ZWM0YjA2Y2M2NTBiMjA2ZWI4ZmE0Mzg2Y2ExM2RhZjQwZDljNTE3NzdiNzM4NjU0NWY5IiwidGVybXNfdXJsIjoiaHR0cHM6Ly9kZXYuZ2F0ZWZlZS5pbnZhbGlkL3YxL3Rlcm1zLzdhM2VkN2U2ZWU0YTZlYzRiMDZjYzY1MGIyMDZlYjhmYTQzODZjYTEzZGFmNDBkOWM1MTc3N2I3Mzg2NTQ1ZjkiLCJ2IjoxLCJ2ZXJzaW9uX2lkIjoiMDAwMDAwMDAtMDAwMC03MDAwLTgwMDAtMDAwMDAwMDAwMGQxIiwidmVyc2lvbl9ubyI6M30sImlhdCI6MTc4OTgxOTIwMCwiaXNzIjoiaHR0cHM6Ly9kZXYuZ2F0ZWZlZS5pbnZhbGlkIiwianRpIjoiMDE5MzJmNGUtN2IzYS03YzFkLTllMmYtMDAwMDAwMDAwMDAxIiwic3ViIjoiMDAwMDAwMDAtMDAwMC03MDAwLTgwMDAtMDAwMDAwMDAwMGUwIn0.6o8T6MPDaD1dwd-ZdSRkLp9bvfoKLm4AFw88at9lNwEGzmOo6AstYCef9ZgFT2egFlaKhaG5WuPVRBNTIeCeBA
The receipt is the signing input, a dot, and the base64url of the 64-byte Ed25519 signature. A correct verifier accepts it against the test key set above, and rejects it if any character of the payload changes.