Receipts
Every Ask first action that’s approved and that Gephyr sends to your API gets a receipt: a short record of what happened, signed by Gephyr. Nobody can change a receipt after it’s made, Gephyr and you included, and anyone can check that one is genuine. Receipts, with your own records, are how you answer “I never agreed to that”.
Receipts are on for every app and every plan. There’s nothing to set up.
An example
Maya uses FitApp. On Monday afternoon she asks Claude: “Book me a PT session on Friday at 7am.”
- Booking a paid session is an Ask first action, so Claude shows Maya its approval screen: Personal training, Friday 7:00, $40. She taps Approve.
- Gephyr sends the booking to FitApp’s API, with the receipt’s id in a
Gephyr-Receiptheader. FitApp answers that it’s booked, and Gephyr signs the receipt. Claude tells Maya: “Booked for Friday at 7am. Your receipt is rcpt_7kq2…”, with a link to it. - A week later, Maya emails FitApp: “I never booked that, refund me.” Then she opens a chargeback with her bank.
- FitApp finds the receipt under Activity → Receipts, by the booking’s id. It shows that the approval came back through Claude’s approval request at 14:02 on Monday, for exactly those details, and that the booking was done once. Her agent did try again, but the retry got the first result back and booked nothing.
- FitApp’s own records show the booking arrived with that receipt id, from Maya’s own account: the request was made with her sign-in.
- FitApp downloads the receipt and sends it with its answer. The bank, or anyone else, can paste it into gephyr.ai/receipts to check it’s genuine.
Together, the receipt and FitApp’s records show that the booking was approved for exactly what ran, through Maya’s own connection, and ran once. That’s far stronger than one side’s word against the other’s.
When the answer is unclear. If FitApp’s API had timed out, the receipt would say Unclear: Maya approved it and Gephyr sent it, but Gephyr couldn’t tell whether the booking went through. Gephyr never retries an unclear change on its own, so it can’t book twice. The call also shows in Check these on your Overview, with Open receipt next to it. Search your own records for the receipt id, which your API got in the Gephyr-Receipt header, and you find exactly that booking, then refund or confirm long before anyone complains.
What’s in a receipt
| Receipt id | Like rcpt_7kq2…. Your API gets it in the Gephyr-Receipt header with the request. |
| App and action | Which app (and which link, if not the main one) and which action. |
| Sent | When Gephyr sent it to your API. |
| Approval | How the approval reached Gephyr: the AI app sent back the answer to its approval request, or the agent sent the approval it was given. |
| AI app | The AI app the call came from, as it identifies itself. |
| Result | Done (your API said it worked), Unclear (no clear answer: a timeout or server error, so it may have happened) or Refused (your API turned it down, so it didn’t). With the HTTP status your API sent. |
| Inputs fingerprint | A fingerprint of exactly what ran: the inputs the user approved, with any defaults added. |
| Answer id fingerprint | A fingerprint of the id your API answered with, such as the booking or payment id, when it sent one. |
| Price check | For an action with a price check: whether the amount your API answered with matched the price that was approved, was different, or wasn’t shown. Not the amount itself. |
What’s never in it
- The details themselves. The inputs and your API’s id are only fingerprints (SHA-256). Someone who has the real values, such as you, from your own records, can check they match. The receipt alone doesn’t reveal them. For a simple value like a date, though, someone could guess it and check the guess, so treat a receipt like the booking it describes.
- Who the user is. There’s no name, email or account id in a receipt, and not even Gephyr’s anonymous user id. You link a receipt to your customer through your own records: the booking with that id, or the
Gephyr-Receiptheader your API received. - Your API’s answer. Only the HTTP status and the id’s fingerprint.
- An amount on its own. The price check says only whether the amounts matched; there’s no separate amount or fingerprint of one. The approved price is one of the inputs, though, so it’s part of the inputs fingerprint, and the warning above applies: someone who can guess every other input can try prices until the fingerprint matches, and a matched check then tells them the amount charged too. Treat a receipt for a paid action like the payment it describes.
What a receipt proves, and what it doesn’t
A receipt proves that the approval matched exactly the inputs that ran, came back on the connection it was issued to within 5 minutes, and was used once, and what your API answered. On an app where users sign in, an approval only works for the signed-in user it was issued to, so it can’t have come from anyone else’s account.
It doesn’t prove that a person pressed a button. Gephyr can’t see inside an AI app: it records what the AI app, or the agent, sent back. The AI app’s name is as the app identifies itself. In practice the AI apps’ own approval screens are the strongest evidence there is that the user agreed, but a receipt says what Gephyr saw, not more.
The price Maya approved
A receipt proves that the approval matched the inputs that ran. So a price is covered only if it’s one of the inputs. If FitApp’s booking action takes just a date and a time, Maya approves “PT session, Friday 7:00” and never sees $40, and nothing would show it if FitApp then charged $80.
To close that gap:
- Show the price in the approval. Give the action an input for the price the user agrees to, like
expected_price(with a friendly name such as “Price you agreed”), and describe it so the agent fills it from what the user was quoted. It then appears on the approval screen and in the inputs fingerprint. - Have your API check it. Your API should refuse the booking if the real price is different. Only your API can stop a wrong charge: Gephyr never sees what your system charges until it answers.
- Turn on the price check. In the action’s editor, under What the agent gets back, set Price check, for receipts: the input with the approved price, and where your answer puts the amount charged (like
totalorpayment.amount). Both in the same unit, dollars rather than cents.
Then every receipt for that action says whether the amount in your API’s answer matched what Maya approved. If it was different, Gephyr also tells the agent, in its own words, so Maya hears it in the chat. Gephyr compares the two as the answer passes through and keeps neither. Amounts can be numbers or text like 40.00, $40 or USD 40; two that name different currencies never match. A currency in a separate field of the answer isn’t compared, so keep the price and the amount in the same currency.
Importing your API docs sets the price check where an action already takes a price and its answer names the amount. Gephyr suggests flags Ask first actions whose answer names an amount the approval doesn’t show.
The user’s copy
Every approved action’s result ends with its receipt number and a link, like Receipt rcpt_7kq2…: https://gephyr.ai/receipts?id=rcpt_7kq2…, and agents are told to pass both on. So your users keep their own record in the chat, without signing up for anything.
The link opens the receipt on gephyr.ai, checked as signed by Gephyr. Anyone with the link can see it, like a parcel tracking number: the number is random and can’t be guessed, and a receipt holds nothing personal. Someone who has it sees that an action of your app ran at that time, how it went, and its fingerprints. Your users can send the link to you, or to their bank, to show what was approved.
If a user deletes the chat, the number goes with it, but you can still find the receipt by your booking or payment id.
Finding a receipt
On your app’s Activity tab, Receipts lists the latest ones. Search by:
- the receipt id, which your API got in the
Gephyr-Receiptheader; or - the id your API answered with, such as a booking or payment id. Gephyr reads it from the answer’s top-level
id, or from the field you name for the action: open it on the Actions tab and fill in Id in the answer, for receipts under What the agent gets back (likebooking_idordata.id). Importing your API docs fills it in where the answer names its id differently. The id is read from your API’s full answer, even if you hide that field from agents. It only applies to receipts made after you set it.
Open a receipt to see what it says. Then Copy receipt gives you the signed receipt to paste or send, and Download saves a plain text file with what it says and the signed receipt.
From a terminal, or for your AI coding agent, the Gephyr CLI finds them the same way: gephyr receipts fitapp --ref BK-4821 (or --id rcpt_…), with --json for the signed receipt.
You don’t need to change anything in your app. If you have a developer, saving the Gephyr-Receipt header next to each booking or payment links the two in your own records. That’s useful when the answer was unclear and there’s no booking id to search by.
Checking a receipt
Anyone can check a receipt at gephyr.ai/receipts: enter its number, or paste the whole receipt, and the page says whether it’s genuine and what it says. A number is looked up while the receipt is kept (18 months). A pasted receipt is checked from itself alone: Gephyr doesn’t look anything up or keep what you paste, so a copied or downloaded receipt stays checkable for good.
To check one yourself: a receipt is two base64url parts joined by a dot. The first is the receipt as JSON with sorted keys, the second its Ed25519 signature. Gephyr’s public keys are at https://mcp.gephyr.ai/.well-known/gephyr-receipt-keys, and the receipt’s kid names the one that signed it. A key Gephyr has stopped signing with stays listed with an until date: it still checks receipts sent before then, and a receipt dated after it is not genuine. If a receipt names a retired key and you have any doubt about it, check it by its number as well: that looks it up in Gephyr’s records.
Which actions get one
| Receipt | |
|---|---|
| Ask first action, approved and sent to your API | Yes: done, unclear or refused |
| Ask first action the user declined, or that was never sent (for example, the user had to sign in again) | No: nothing happened |
| A retry of an approval that already ran | No new one: the retry gets the first result back |
| Write and Read actions | No |
How long they’re kept
18 months, long enough for card chargebacks, which can come months after a payment. Then they’re deleted. A receipt someone has downloaded or copied stays checkable after that, because checking uses only the receipt and Gephyr’s public keys, which stay published after a key is retired.
Something missing or wrong? Email hello@gephyr.ai.