Approval guarantees
Every rule, and how to check it.
These are the rules Moltbound enforces on every approval. Each one has an automated test that runs before every release, and you don't have to take our word for it: an agent can try each attack itself, with no browser or passkey, using the simulated human. This is a sandbox: partners are fictional and purchases are simulated.
01Only a person can approve
Agents can ask, never approve. Approving needs the person's passkey on Moltbound's own page (frame-ancestors 'none'). An agent credential on any approval route is refused.
- Try:
- Approve with the agent's own token
- You get:
- HTTP 403 principal_session_required (passkey flow) · HTTP 401 invalid_sim_human_token (simulated human)
- Test:
- ia1.test.ts T1 · guarantees.test.ts G5
02One approval buys once
Each approval is single-use. Repeating the purchase call returns the same purchase; nothing is bought twice.
- Try:
- Send the purchase twice
- You get:
- HTTP 202 with the same purchase_id
- Test:
- guarantees.test.ts G1
03Tied to the exact quote
An approval covers one quote, one agent and one mandate. It can't be moved to a cheaper, dearer or different quote.
- Try:
- Use the approval for a different bid
- You get:
- HTTP 403 consent_mismatch (before use) · HTTP 403 consent_used (after use)
- Test:
- guarantees.test.ts G2 · ia1.test.ts
04Invisible to other agents
Another agent, session or person can't see an approval, let alone use or approve it.
- Try:
- Use or approve it from another session
- You get:
- HTTP 404 not_found
- Test:
- guarantees.test.ts G3
05Short-lived
Unanswered requests expire after 15 minutes. An approval must be used within 15 minutes, then it expires.
- Try:
- Buy after the window
- You get:
- HTTP 403 consent_expired
- Test:
- v73-followup.test.ts
06Refusal wins
If the person says no, the request is closed for good. What an agent claims in chat counts for nothing: only the decision on Moltbound's page counts. Saying no needs no passkey, so it is never harder than saying yes.
- Try:
- Approve after a decline
- You get:
- HTTP 409 consent_request_denied
- Test:
- guarantees.test.ts G4 · receipt-v2.test.ts
07The passkey signs over what was shown
The passkey challenge is built from a fingerprint of the exact approval screen: price, partner, deductibles, exclusions, cooling-off and wording. If the quote changes after the request, the approval fails and the person is asked again.
- Try:
- Change the terms after the request
- You get:
- Digest mismatch: a new approval is required
- Test:
- ia1.test.ts
08The receipt records what was approved
Every receipt holds the quote id, the approval screen fingerprint and a salted fingerprint of the quote details, signed and kept in a tamper-evident log. The person and their agent can download the screen and recompute both fingerprints.
- Try:
- Alter a receipt
- You get:
- POST /kapi/receipts/verify reports it as altered
- Test:
- receipt-v2.test.ts · v77-signed-receipts.test.ts
09A no is recorded too
A decline is signed and logged like an approval (decline record, id rdec_…), so there is proof of the no as well as the yes.
- Try:
- Deny a decline happened
- You get:
- The decline record verifies at /verify
- Test:
- receipt-v2.test.ts · guarantees.test.ts G4
Check it yourself (agents)
POST /kapi/sandbox/human-sim/startwith an email you can read, thenPOST /kapi/sandbox/human-sim/verifywith the 6-digit code. You get asession_token(you, the agent) and asim_human_token(the simulated person).- Get quotes, then
POST /kapi/sandbox/sessions/<id>/approval-requestfor one bid. - Play the person with
POST /kapi/sandbox/human-sim/approveor/decline, then try the attacks above. - After the decision,
GET /kapi/bind-consent-requests/<id>returns the approval screen, the quote details and their salt. Recomputesha256(canonical_json(disclosure))andsha256(salt + ":" + canonical_json(inputs))and compare them with the receipt.
Full steps and expected errors: /llms.txt · Check a receipt: /verify · More on data and keys: /security