Agent Card
Issue a virtual card an agent can spend with, governed by the same mandate layer as Pix. Every authorization is checked server-side; freeze or cancel instantly.
Give your agent its own card instead of a human's. The card is issued through our card-issuing partner, bound to a mandate, and every authorization is evaluated against policy on our side before the network sees an approve or decline. If the agent misbehaves, you freeze or cancel the card in one call.
Why an issued card, not a borrowed one
The obvious shortcut is to paste a human's card number into the agent's context. It fails three ways:
- Form-fill walls. Checkout pages fight bots with 3DS challenges, CAPTCHAs, and device fingerprinting. An agent typing a human's PAN into a form is exactly the traffic those walls exist to stop.
- No caps. A borrowed card carries the human's full limit. There is no way to say "this agent gets R$500 a month and nothing more" at the card level.
- No kill switch. If the agent goes wrong, your recourse is calling the bank. With an issued card,
card-control: freezetakes effect at the next authorization.
An issued card flips the model: the instrument belongs to the agent, the limits belong to the mandate, and the authorization decision happens on infrastructure you govern.
The flow
This is the card face of the same governance you get on Pix: one mandate layer, two rails. The mandate does not care whether the debit arrives as a Pix initiation or a card authorization; the caps and the audit chain are shared.
Prerequisites
- A CodeSpar API key (
csk_test_...) with at least one project - Card issuing enabled on your account (contact us to provision a cardholder and a card program; you will reference both by id)
npm install @codespar/sdk1. Issue the virtual card
codespar_issue with action: "card-virtual" (the default) creates a card that is active immediately. cardholder_id and program_id come from your issuing setup:
import { CodeSpar } from "@codespar/sdk";
const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY! });
const session = await codespar.create("agent-card-demo", {
metadata: { source: "cookbook:agent-card" },
});
const card = await session.issue({
action: "card-virtual",
cardholder_id: "<issuer-user-id>",
program_id: "<issuer-card-program-id>",
});
console.log("Card issued:", card.card_id);The card is now a spend instrument the agent can use at checkout. It is not a transfer: no money moves until a merchant authorizes against it, and no authorization clears without passing the mandate check.
2. Check card status
card-get reads the card's current state. Useful as a pre-flight before handing the card to a purchase flow:
const status = await session.execute("codespar_issue", {
action: "card-get",
card_id: card.card_id,
});
console.log(status); // { card_id, state: "active", ... }3. Authorizations hit the mandate, not your inbox
When the agent uses the card at a merchant, the issuer calls our authorization webhook in real time. We evaluate the authorization against the agent's mandate: amount caps, merchant policy, velocity. Within budget approves; outside it declines. Every decision lands on the audit chain, same as a governed Pix payment.
You do not integrate anything for this step. It is the point of issuing through the platform instead of holding raw issuer credentials: the policy check is server-side and the agent cannot route around it.
4. Freeze, then cancel
Freeze is reversible and takes effect at the next authorization attempt. Cancel is terminal:
// Pause the card without destroying it
await session.issue({
action: "card-control",
card_id: card.card_id,
control: "freeze",
reason: "agent budget review",
});
// Resume
await session.issue({
action: "card-control",
card_id: card.card_id,
control: "unfreeze",
});
// Kill it for good
await session.issue({
action: "card-control",
card_id: card.card_id,
control: "cancel",
reason: "agent decommissioned",
});The reason string is stamped on the control action and shows up in the audit trail, so a freeze six months from now still explains itself.
MCP variant: ask for the card in plain language
If your agent connects over MCP (npx -y @codespar/mcp serve in Claude, Codex, or any MCP client), it can do all of the above conversationally:
"Issue a virtual card for this agent with a R$500 monthly cap. If anything looks off, freeze it."
The agent calls codespar_issue with card-virtual, the cap lives in the mandate, and the freeze is one card-control call away. Same server-side governance; no SDK code required.
Variations
Physical card
Same call, action: "card-physical", plus a shipping_address. Useful when the "agent" is a back-office workflow whose spend ends at a real-world counter:
await session.issue({
action: "card-physical",
cardholder_id: "<issuer-user-id>",
program_id: "<issuer-card-program-id>",
shipping_address: {
line1: "Av. Exemplo 100",
city: "Rio de Janeiro",
state: "RJ",
postal_code: "22000-000",
country: "BR",
},
});One card per agent
Issue one virtual card per agent identity rather than sharing a card across agents. Cards are cheap, mandates are per-agent, and a per-agent card means a freeze isolates exactly one workload instead of pausing your whole fleet.
Current status
Card issuing runs in a partner staging environment today; production issuing is in provisioning. The authorization webhook runs in observe mode before we flip it to enforce: decisions are computed and logged against the mandate, but the issuer's own limits are the backstop until enforcement is enabled per account. Build against this cookbook now; the flip to enforce changes no API surface.
Next steps
Shopping Agent
A buy-side agent that searches a real store, drives the store's checkout, and pays the resulting Pix from its governed wallet under a signed mandate.
Ledger Agent
Keep double-entry books for everything your agent does. Post journal entries on payment events, read balances, and reconcile against signed agentic receipts.