How to give an AI agent a payment card

Your agent needs to pay for API calls. You want it to pay on its own, only the merchants you allow, only the amounts you allow, with a record of what it meant to pay for.

In sandbox today

Cards run with test money, and you try payments in the Try a test payment panel on the card's dashboard page.

How it fits together

Your agent
   ├─ signs a payment intent with its Ed25519 key
   │     → POST /api/agents/{id}/intents   (checked and logged)
   └─ pays with its card
         → each payment is checked against the card's limits and merchants

The two are separate today: the intent log records what the agent meant to pay for, and the card's rules decide each payment. A card payment doesn't check for a matching intent yet.

Step 1: Get an API key

Sign up at sorenpay.com/signup → Business. Then create a key in Developers → API keys with write:agents, read:agents, write:cards and read:cards.

Step 2: Register the agent

Generate the key pair yourself, so the private key never leaves your environment, and send only the public key:

register-agent.shbash
openssl genpkey -algorithm Ed25519 -out agent.key
openssl pkey -in agent.key -pubout -out agent.pub

curl -X POST https://sorenpay.com/api/agents \
-H "Authorization: Bearer $SOREN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d "$(jq -n --arg pem "$(cat agent.pub)" \
      '{agentName: "research-assistant", protocols: ["TAP"], publicKeyPem: $pem}')"

Step 3: Give it a card with limits

mint-card.tstypescript
const res = await fetch("https://sorenpay.com/api/cards", {
method: "POST",
headers: {
  Authorization: `Bearer ${process.env.SOREN_API_KEY}`,
  "Content-Type": "application/json",
  "Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
  holderKind: "agent",
  holderLabel: "research-assistant",
  monthlyLimitUsdCents: 100_00,        // $100 a month
  perTxnLimitUsdCents: 10_00,          // $10 a payment
  allowedMccs: ["5734", "7372"],       // software, computer services
  allowedMerchants: ["openai", "anthropic"],
}),
});
const { card } = await res.json();
console.log("card ending", card.last4);

Step 4: Log a signed intent before paying

The intent is canonical JSON (keys sorted, no whitespace) signed with the agent's Ed25519 key. The protocols page has the format. A valid intent answers:

{ "accepted": true, "log_id": "…", "intent_id": "…", "next": { "message": "…" } }

A bad or expired signature answers 401 signature_invalid (with a reason), and a reused intent_id answers 422 intent_replay. Invalid submissions are logged too.

Step 5: Review the log

GET /api/agents/{id}/intents returns the agent's intents, including rejected ones. The dashboard shows them on the agent's page.

What this gives you today

  • One card per agent, each with its own merchants and limits, checked on every payment.
  • A signed record of what each agent meant to pay for, with replays refused.

Not built yet: card payments that require a matching intent, and webhooks for intents or card payments.