# The Outcome Receipt, v0.1 (draft, 17 Oct 2026) An open format for one fact AI can't fake: **a real person caused this outcome, through this post, and it stuck.** Anyone may issue, read or verify Outcome Receipts. Avouch is the first issuer; the format belongs to no one. > **A note on names (17 Oct 2026).** Avouch was called Sharecut while this format was drafted. Receipts Avouch signs still carry `"provider": "sharecut"`, the `scr_` token prefix and `sc-` key ids, and will keep them: the provider field is inside the signed text, so changing it would make every receipt already handed out read differently. Treat `sharecut` as Avouch's provider id. Avouch publishes its keys at `/.well-known/avouch-receipts.json`, and the same keys stay at `/.well-known/sharecut-receipts.json`. Why it exists: - Agents, platforms and businesses can now check that a *payment* happened (Stripe), that an *agent* is legitimate (card-network agent programs), and where a piece of *content* came from (C2PA). - None of those join **a checked human, a specific post, and an outcome that held up after the refund window.** This does. ## 1. The receipt A receipt is a JSON object, the **body**, plus an Ed25519 signature over its canonical form. | Field | Type | Meaning | |---|---|---| | `provider` | string | Who issued it, e.g. `"sharecut"` (Avouch's provider id, kept from its earlier name) | | `token` | string | The receipt's id. Opaque, unguessable, stable. Avouch's live tokens look like `scr__`; its sandbox tokens look like `scr_test___` and never resolve against the live ledger. | | `status` | string | `held` · `cleared` · `refunded` · `unconfirmed` · `refused` · `no seller` · `returning customer` | | `product` | string | The issuer's id for the product | | `merchant` | string | The merchant's domain | | `merchant_charge` | string \| null | **The merchant's own charge id** (e.g. a Stripe payment intent, a Shopify order). This lets the merchant check the receipt in its own records without trusting the issuer. | | `publisher_id` | string | The person's public id at the issuer | | `creative_id` | string \| null | The specific post that carried the click, if any | | `on_camera_checked` | boolean | A person checked that the post shows the publisher's own face. Never set by face matching. | | `confirmed_by_business` | boolean | The merchant signed or confirmed the sale | | `new_customer` | boolean \| null | The merchant said this was a new customer (`null` = not stated) | | `amount_cents`, `currency` | int, string | The sale, in the smallest unit | | `sold_at` | date | The day of the outcome | | `checked_at` | timestamp | When this copy of the receipt was produced | | `livemode` | boolean | `true` for a real outcome. `false` marks a **sandbox** receipt: made by the issuer's test mode or live demo, with example sellers and a simulated buyer. It proves the issuer's code and key work; it never proves a sale. Like Stripe's `livemode`, it is inside the signed text, so it can't be flipped. | **Statuses move one way:** `held` (inside the refund window) → `cleared` (it stuck) or `refunded` (it didn't). `cleared` is final, except for a chargeback for fraud. **A receipt never contains:** the customer's identity, email or any personal data, ID images, money owed to anyone, or the issuer's fee. ## 2. Signing - `signed` = the body serialized as JSON with **sorted keys and no spaces** (`separators=(",", ":")`), UTF-8. - `signature` = Ed25519 over the bytes of `signed`, base64url without padding. - `kid` = the key id: `"sc-"` + the first 12 hex characters of SHA-256 of the raw public key. - The issuer publishes its keys at `/.well-known/-receipts.json` (Avouch: both `avouch-receipts.json` and `sharecut-receipts.json`): ```json {"provider": "sharecut", "keys": [{"kid": "sc-…", "alg": "Ed25519", "public_key": ""}, …]} ``` `keys` lists the current key first, then every older key the issuer has signed with (section 5, rule 7). ## 3. Verifying (anyone, offline) 1. **Pin the issuer's key.** Fetch the issuer's keys (`/.well-known/…`) from the issuer you trust, by its own address (Avouch: `https://avouch.co`), or use a copy of them you saved from there. **Never take the keys from the host that served the receipt**: any host can publish its own key beside a receipt it made up, and that pair checks out. Fetch the receipt (`GET /v1/receipts/`) from the issuer too; a receipt from anywhere else is checked only against the issuer's keys. 2. Find the key whose `kid` matches. Verify `signature` over the UTF-8 bytes of `signed`. 3. Read the facts **only from `signed`**, never from unsigned fields next to it. The signed `token` must be the token you asked about: a genuine receipt for another sale is not a receipt for this one. 4. If you are the merchant: find `merchant_charge` in your own Stripe or Shopify. 5. Read `livemode`. Only `true` is a real outcome. A receipt with `livemode: false` is a sandbox example, and one with no `livemode` (issued before 17 Oct 2026) should be fetched again: the issuer signs a fresh copy on every fetch. If any step fails, the receipt proves nothing. `verify_receipt.py` in this folder (also at `/docs/verify_receipt.py`) does steps 1–3 and 5 with no Avouch code. It takes the keys from `https://avouch.co` unless you name another issuer with `--issuer `, and refuses (NOT VERIFIED) a receipt host that is not that issuer. `--file receipt.json --keys keys.json` checks offline against keys you saved from the issuer, and `--require-live` makes a sandbox receipt exit non-zero. The Verify button on an Avouch receipt page checks the receipt with the keys of the address the page was opened on. That is the issuer only when the address bar shows the issuer's own address; anywhere else, use `verify_receipt.py`. **Try it:** `POST /v1/sandbox/run` on any Avouch host runs the whole flow (listing, check, matching, a click, a signed sale) on the real code in a sandbox and returns a real, signed receipt with `"livemode": false`. Example: ```json {"provider":"sharecut","token":"scr_test_r5f3323df9105_1_15bc…","status":"held","livemode":false,"product":"portrait-pro", "merchant":"portrait-pro.example","merchant_charge":"pi_sandbox_8212…","publisher_id":"e561aa","confirmed_by_business":true, "new_customer":true,"amount_cents":10000,"currency":"usd","sold_at":"2026-10-17","checked_at":"2026-10-17T04:23:53Z", …} ``` ## 4. Where it plugs in | System | Mapping | |---|---| | **ACP** (OpenAI and Stripe's agent checkout) `affiliate_attribution` | `provider` = `"sharecut"`, `token` = the receipt token, `publisher_id`, `creative_id`. The merchant verifies the token with the provider, which is exactly section 3. | | **UCP** (Google's) `attribution` map | `com.sharecut.attribution` = `{token, publisher_id, creative_id}` | | **AdCP** (agent advertising) `revenue_share` / `cpa` | `event_source_id: "sharecut_receipts"`. Held receipts are provisional reports, refunds are `adjusted`, and cleared receipts are final (`is_final: true`, `finalized_at`). This settles the refund window that AdCP leaves to "commercial terms". | | **The merchant's own payments** | `merchant_charge` is the merchant's Stripe payment intent or Shopify order id | ## 5. Rules an issuer must keep (to call its receipts Outcome Receipts) 1. **A real, adult person behind every `publisher_id`**, one person to one account. 2. **No first-person claims of use** in content the issuer writes for a person, unless the person says they use the product. Their word is enough. 3. **Self-purchase, own-device clicks and duplicate orders never earn.** 4. **Pay for new customers only, unless the merchant chooses otherwise.** `new_customer` says which. 5. **Hold until the merchant's refund window closes** before `cleared`. 6. **Never sell rank.** A neutral issuer can't be paid to place one seller above another. 7. **Publish every key you have signed with.** A new key gets a new `kid`. Avouch records each public key the first time it signs a receipt with it (or publishes it), and its `/.well-known/` key file lists every recorded key, the current key first and the others after it, the most recently added first. It never removes a key, so a receipt signed before a rotation still verifies. Verifiers pick the key whose `kid` matches the receipt's, never simply the first. ## 6. Versioning This is v0.1, a draft. Fields may be added; existing fields keep their meaning. Comments and other issuers are welcome.