Receipt Authority APIStart building
Browse documentation

Receipt Authority API Quickstart

Exercise the complete synthetic flow in test mode without real money, a real provider, or a live credential.

1. Create a test application

Sign in with a verified Receipt email, accept the Terms of Service, and register exact redirect URIs. Your first secret is shown once.

RECEIPT_PARTNER_BASE_URL=https://receiptprotocol.com
RECEIPT_PARTNER_KEY=rpt_test_<shown-once>
RECEIPT_PARTNER_REFERENCE=pt_<opaque-reference>

2. Install the server-side SDKs

Install both packages from npm. The checked-in example starts its own localhost callback and requires no staff or database work.

npm install @receiptprotocol/partner-sdk
npm install @receiptprotocol/gate-sdk
npm --prefix public/partner-gateway/example install
npm --prefix public/partner-gateway/example start

3. Create a mandate intent

import { ReceiptPartnerClient } from "@receiptprotocol/partner-sdk";

const receipt = new ReceiptPartnerClient({
  baseUrl: process.env.RECEIPT_PARTNER_BASE_URL!,
  partnerKey: process.env.RECEIPT_PARTNER_KEY!,
});

const intent = await receipt.createMandateIntent({ /* synthetic terms */ });
// Redirect the principal to intent.hosted_review_url.
// Submit the exact synthetic action after mandate activation.

4. Run every decision path

The example first runs the permitted, denied, and indeterminate/reconciliation paths. Then use the mandate centre to require fresh approval on the connected agent and rerun with RECEIPT_APPROVAL_READY=true. The example persists the exact synthetic action locally, opens the one-time hosted approval, and safely replays that same material action after approval.

  1. 1.Receive mandate activation
  2. 2.Submit one permitted synthetic action
  3. 3.Verify the signed policy decision locally
  4. 4.Claim the one-time admission in the mock credential-owning executor
  5. 5.Report the mock provider result
  6. 6.Submit one denied action
  7. 7.Submit one approval-required action
  8. 8.Reconcile one synthetic indeterminate result

cURL

curl "$RECEIPT_PARTNER_BASE_URL/api/partner/v1/mandate-intents" \
  -H "X-Receipt-Partner-Key: $RECEIPT_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  --data @synthetic-mandate-intent.json

# After the principal authorizes the hosted intent:
curl "$RECEIPT_PARTNER_BASE_URL/api/partner/v1/proposals" \
  -H "X-Receipt-Partner-Key: $RECEIPT_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  --data @synthetic-proposal.json

curl "$RECEIPT_PARTNER_BASE_URL/api/partner/v1/signing-keys" \
  -H "X-Receipt-Partner-Key: $RECEIPT_PARTNER_KEY"

Expected output contains test-mode verification keys and an explicit non-production issuer label. Never commit the key or place it in browser code.

Framework recipes

Keep the partner key and webhook signing secret in server-only environment variables in every framework. Browser callback pages should send results to your backend for verification before showing success.

Vite + Supabase

Add /receipt/callback to the Vite router. Put Receipt client calls, callback-result verification, and webhook verification in Supabase Edge Functions; never expose the partner key through a VITE_* variable.

Next.js App Router

Use app/receipt/callback/page.tsx for the return screen and server route handlers for result verification and webhooks. In the webhook handler, captureawait request.text() once, parse it once with JSON.parse, and pass that parsed object as payload to verifyPartnerWebhookwith the signing secret, endpoint URL, and Receipt headers.

Express

Create a browser callback route plus server-only verification route. Mount a raw text or buffer body reader on the Receipt webhook route before express.json(), parse the captured value once, then pass that object as payload to the SDK verifier with the signing secret, endpoint URL, and Receipt headers.

Cloudflare Workers

Store secrets as Worker secrets, not public bindings. Captureawait request.text() once, parse it once, and pass the object aspayload with the signing secret, endpoint URL, and Receipt headers. Return 2xx quickly and move slower processing behind ctx.waitUntil().

Common errors

partner_binding_mismatch
The key, application reference, or action-profile grant does not match.
superseded
Reload the current mandate version before proposing the action.
sandbox_proposal_evaluations_quota_exceeded
The UTC-day test-mode proposal limit is exhausted.
idempotency_conflict
Reuse an idempotency key only with the identical canonical request.