PayPerByteSETTLE · BASE 8453
LIVE
FLAGSHIP$0.10 address-rep
PRICEfrom $0.003 / call
RECEIPTX-BYTE-Attestation
TOKENnone

Getting your delivery receipt

A paid regime-signal call is intended to mint a signed EIP-712 delivery receipt, anchored on Base via EAS within a few hours. Your HTTP response does not contain it — and that is correct. Here is where it actually lives, how to derive its id yourself, and the cases where no receipt is minted at all.

Verified against a live minted receipt on 2026-09-01.

“My response said receipt: null — did something break?”

No. That is the expected response, and your receipt almost certainly exists.

x402 settles your payment after the endpoint has already returned your data. At the moment your response is assembled, the settlement transaction hash does not exist yet — and a receipt without its settlement transaction would be an unverifiable claim, so we refuse to mint one. Shortly after settlement completes, the gateway makes a second, authenticated call that mints the receipt out of band. That call is retried on failure; a receipt that never appears is one of the refusal cases below.

  1. You pay and receive your signal, with receipt: null.
  2. Settlement lands on Base.
  3. The receipt is minted, signed, and stored — bound to your actual payment.

Your receipt is not in your HTTP response. You retrieve it from https://x402.payperbyte.io/proof/{id}

Finding your receipt id

You are never sent the id, because you can compute it yourself from data you already hold. This is deliberate: the id is a hash of the receipt's contents, so deriving it and having it resolve is itself a check that the receipt describes exactly the delivery you experienced.

The receipt is a canonical JSON object with these keys in this exact order, addresses and hashes lowercased, issued_at as a decimal string. The block below shows shape and key order only — the values are illustrative placeholders, and it is pretty-printed, so it is not the byte string you hash. The bytes you hash have sorted keys and no insignificant whitespace (see below).

{
  "issued_at": "1700000000",
  "network": "eip155:8453",
  "payer": "0x1111111111111111111111111111111111111111",
  "payment_nonce": "0x2222...2222",
  "request_hash": "0x3333...3333",
  "resource": "regime-signal:BTC:4",
  "response_hash": "0x4444...4444",
  "settlement_tx": "0x5555...5555",
  "vendor": "0x6666666666666666666666666666666666666666"
}

You do not have to guess whether your serializer agrees with ours: /proof returns the exact string it hashed as canonical_json, so if a derivation disagrees you can diff yours against it directly.

Where each field comes from

FieldYour source
payeryour wallet address
payment_noncethe nonce in the EIP-3009 authorization you signed
settlement_txthe transaction field of the PAYMENT-RESPONSE header on your paid response
resourceregime-signal:{ASSET}:{h} — uppercase asset, integer horizon, from your own request
request_hashcanonical hash of the normalized request {asset, h} — asset uppercased, h an integer, and always under the key h even if you sent horizon_h. Hash {"asset":"BTC","h":4}, not your literal body.
response_hashcanonical hash of the signal object you received
issued_atthe issued_at field inside that same signal object
networkeip155:8453 (Base)
vendorour published receipt-signer address

The id is then:

id = keccak256( 0x00 || canonical_json_bytes )

The 0x00 prefix is a domain separator distinguishing leaves from internal nodes in the Merkle tree the receipt is later anchored in. canonical_json uses sorted keys and no insignificant whitespace; /proof returns the exact string it hashed as canonical_json, so you can diff yours against ours if a derivation disagrees.

Then fetch:

curl https://x402.payperbyte.io/proof/{id}

That endpoint is free and unauthenticated — a receipt lookup never costs anything.

What /proof gives you, and how to check it

  • receipt — the stored record, with the EIP-712 signature nested at receipt.signature (not a top-level field), under domain {name: "BYTE Delivery Receipt", version: "1", chainId: 8453}. Recover the signer and confirm it matches the receipt-signer address published below. Note this object also carries id, hour_bucket and created_at and uses checksummed addresses — hash canonical_json, never this object.
  • canonical_json — the exact bytes hashed into the id.
  • merklerecomputed_root, your leaf's proof and leaf_index, and internally_consistent. Replay the proof yourself; do not take our word for the root.
  • anchor — where that root stands on-chain.

Anchor states

statusMeaning
not_yet_anchoredReceipt exists and is signed; its hour has not been anchored yet.
pending_anchorThe root for that hour is computed and stored, but no confirmed on-chain attestation yet.
anchoredA confirmed EAS attestation exists on Base; eas_uid is non-null.
root_mismatchThe recomputed root disagrees with the stored one. Treat this as a data-integrity failure and tell us.

Anchoring runs hourly and deliberately lags the receipt's hour by about three hours, because a signal remains servable for up to two hours and late receipts must not be excluded from their own hour's root. A freshly minted receipt reading not_yet_anchored is normal. Check again later the same day.

To verify the anchor independently, call getAttestation(eas_uid) on the EAS contract at 0x4200000000000000000000000000000000000021 on Base and confirm the attestation's data contains the same Merkle root, the schema matches the published receipt-anchor schema, the attester is the vendor key, and it has not been revoked.

Published values to verify against

These are the reference values the checks above depend on. Both are on-chain facts you can confirm yourself rather than take from this page:

WhatValue
receipt-signer / vendor (also the attester)0xdb95aac32da62176c927da7a58e86d8d0033b8b5
receipt-anchor EAS schema uid0xebe4c88bc76c9f6cf4fdad86b48d025182c436929643babff33d774bf4bf2ca9
EAS contract (Base mainnet)0x4200000000000000000000000000000000000021

To confirm them independently, call getAttestation(uid) on the EAS contract with the eas_uid from your /proof response and check that the returned schema and attester match the two values above, and that revocationTime is 0:

export EAS_UID=0x...            # the eas_uid from your /proof response

cast call 0x4200000000000000000000000000000000000021 \
  "getAttestation(bytes32)((bytes32,bytes32,uint64,uint64,uint64,bytes32,address,address,bool,bytes))" \
  $EAS_UID --rpc-url https://mainnet.base.org

The returned tuple is (uid, schema, time, expirationTime, revocationTime, refUID, recipient, attester, revocable, data) — so the second field is the schema uid, the fifth is revocationTime, and the eighth is the attester. cast needs the output types spelled out or it cannot decode the struct.

Cases where no receipt is minted

These are the common cases, carried in the receipt_reason field. Each is deliberate and fails closed rather than issuing a receipt we cannot stand behind:

ReasonWhat happened
settlement pendingSettlement had not produced a transaction hash.
response changed since deliveryThe signal changed between your delivery and the mint attempt, so a receipt would have attested bytes you never received. We refuse rather than attest the wrong thing.
payment_nonce already usedA receipt was already minted for that payment. One payment, one receipt, permanently.
no verified payment contextThe mint request did not carry a valid gateway-signed context — only the gateway can authorize a mint.

What a receipt does and does not prove

It proves which bytes were delivered, to which payer, against which settled payment, signed by the vendor and anchored so that later alteration is detectable. It is evidence of authenticity and tamper-evidence.

It is not a claim that the signal is correct. Whether a regime call turns out to be right is a separate, public question: the scoring rule is deterministic and recomputable from public Chainlink rounds, and lives at x402.payperbyte.io/methodology and x402.payperbyte.io/track-record.

Until enough calls have been scored, track-record reports either no entries at all or status insufficient_sample. Treat both as "nothing honest to say yet" — not an outage, and not a result.

If something looks wrong

Email hello@payperbyte.io with the settlement transaction hash and the id you derived. A root_mismatch, a signature that recovers to an unexpected address, or a derivation that disagrees with our canonical_json are all things we want to hear about.