Developers

Add user-controlled authority to your agent app.

Create an application and begin integrating immediately. Use Test mode while building, then request Live access for your production deployment.

The application keeps execution. The user keeps authority.

Hosted mandates and approvals
Signed permit and denial decisions
One-time execution admissions
Independent pause and revocation
No real money in test mode
Your provider credentials stay with you
Signed decisions verify locally
Receipt never holds or moves funds

Install the SDKs

Add the server-side Partner SDK and the Gate SDK to the infrastructure that owns your provider credentials.

npm install @receiptprotocol/partner-sdk
npm install @receiptprotocol/gate-sdk

How it works

One authority boundary between user intent and provider entry.

  1. 01

    Register your application

    Create a Test application and issue server-side credentials.

  2. 02

    Send the user to Receipt

    Let the user review and authorize a scoped, revocable mandate.

  3. 03

    Submit an exact action

    Bind the amount, counterparty, provider, and material action fields.

  4. 04

    Enforce the one-time admission

    Claim the signed admission before your infrastructure calls the provider.

  5. 05

    Report and reconcile the result

    Report success, failure, or uncertainty and reconcile the original action once.

Build your first governed action

Use the Partner SDK to submit the action and the Gate SDK to enforce the one-time admission before provider entry.

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

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

// Submit one exact action against the active mandate.
const proposal = await receipt.createProposal({
  partner_reference: process.env.RECEIPT_PARTNER_REFERENCE!,
  principal_reference: principalReference,
  agent_connection_reference: agentConnectionReference,
  mandate_reference: mandateReference,
  expected_mandate_version: mandateVersion,
  action_profile_reference: "reference_economic_action.v1",
  action_profile_version: 1,
  proposed_action: action,
  occurrence_id: action.occurrence_id,
  idempotency_key: `proposal:${action.occurrence_id}`,
  proposal_expires_at: action.action_expires_at,
});

if (proposal.policy_decision !== "permitted" || !proposal.execution_admission) {
  throw new Error(`not_permitted:${proposal.policy_decision}`);
}

// Enforce the one-time admission before any provider call.
const signingKeys = await receipt.getSigningKeys();
const gate = new ReceiptAuthorityGate({
  authorityBaseUrl: process.env.RECEIPT_PARTNER_BASE_URL!,
  partnerKey: process.env.RECEIPT_PARTNER_KEY!,
  partnerReference: process.env.RECEIPT_PARTNER_REFERENCE!,
  environment: "sandbox",
  agentConnectionReference,
  trustedAdmissionKeys: Object.fromEntries(
    signingKeys.keys
      .filter((key) => key.environment === "sandbox" && key.purpose === "execution_admission")
      .map((key) => [key.key_id, key.public_jwk]),
  ),
});

// Gate claims the admission once, invokes your callback once,
// and reports the result. Your provider credentials never leave here.
const executed = await gate.executeOnce({
  admission: proposal.execution_admission,
  action,
  providerReference: "provider:synthetic",
  invoke: () => callYourProvider(action),
  classifyResult: (report) => ({ event_type: "complete", report }),
});
View the complete quickstart