Blog/Agentic Payments: A Developer Guide
Developer Guide

Agentic Payments: A Developer Guide (2026)

Autonomous AI agents are buying things. Here's the full stack: isolated cards per agent, spend controls that actually hold, and MCP-native payment tools.

limit.md·June 2026·8 min read

Autonomous AI agents are buying things. Not theoretically — right now, agents built on various AI models are booking flights, purchasing API credits, renewing SaaS subscriptions, and paying contractors. The infrastructure layer powering this is called agentic payments, and most developers building agents are either cobbling it together themselves or punting on it entirely.

This guide explains the full stack: what agentic payments actually are, the two dominant rails (card and stablecoin), how to give each agent its own isolated payment method, how to set spend caps that don't require your pager going off at 3 AM, and how to wire it all into an MCP server so your agent can pay without leaving the tool-call loop.


What “agentic payments” actually means

A payment is agentic when an AI system autonomously authorizes, initiates, or completes a financial transaction — without a human in the loop for each individual purchase.

This is distinct from:

  • A human using an app powered by AI (still human-initiated)
  • An AI that drafts a purchase order for human approval (still human-gated)
  • A traditional cron job that debits a fixed amount on schedule (no inference involved)

Agentic payments are live inference + live money. The agent reads context, decides to spend, and the spend happens.

                     ┌────────────────────────────────────┐
                     │           AI Agent                 │
                     │  "I need to buy a Pro API plan"    │
                     └───────────────┬────────────────────┘
                                     │ tool call
                                     ▼
                     ┌────────────────────────────────────┐
                     │      MCP / Payment Tool            │
                     │  purchase({ item: "api-pro",       │
                     │             amount: 49_00 })       │
                     └───────────────┬────────────────────┘
                                     │
                ┌────────────────────┼─────────────────────┐
                ▼                    ▼                      ▼
         Virtual card          Stablecoin            ACH/bank rail
         (Visa/MC rail)        (USDC/PYUSD)          (slow, human)

The two rails

Rail 1: Virtual cards (Visa/Mastercard)

This is the high-coverage, high-compatibility rail. Every merchant that accepts Visa accepts a virtual card. The tradeoff is that it's fiat, USD-denominated, and requires a card-issuing partner.

Why this rail wins for most agents:

  • Works at 99% of online merchants without integration work
  • Familiar fraud/chargeback layer
  • Easy spend controls (per-card limits, MCC restrictions, expiry)
  • Instant issuance via API — you can issue a card in one API call, use it once, then expire it

The limit.md approach: limit.md sits on top of a card-issuing API. When you create a virtual card via limit.md, you get a card scoped to a single agent identity — with spend caps, allowed merchant categories, and expiry baked in.

Rail 2: Stablecoins (USDC / PYUSD)

Stablecoin rails are gaining traction for agent-to-agent payments and for markets where traditional card rails don't reach (cross-border B2B, crypto-native services).

Why this rail matters:

  • Programmable at the protocol layer — you can embed payment conditions in the transaction itself
  • No chargeback risk for merchants
  • Native to agentic architectures that already handle crypto wallets
  • Lower fees for large-volume agent transactions

The tradeoff: Stablecoin payment acceptance is still a small fraction of the merchant universe. For general-purpose web agents, card rail is the right default. For specialist agents operating in crypto-native markets, stablecoin rails are increasingly a first-class option.


Provision an isolated card per agent

The most important design principle for agentic payments: one card per agent identity, not one card per application.

Here's why: if three agents share a card and one of them goes rogue — starts buying things it shouldn't, hits a compromised endpoint that tricks it into a spend — you cannot revoke that agent's access without killing the other two. Worse, you can't audit spend per agent. You get a $3,000 AWS bill with no idea which agent caused it.

The right pattern:

import { LimitClient } from "@limit-md/sdk";

const limit = new LimitClient({ apiKey: process.env.LIMIT_API_KEY });

// When you instantiate a new agent, provision its card
async function createAgentWithPaymentMethod(agentName: string, task: string) {
  const card = await limit.cards.create({
    label: `${agentName}:${task}`,          // traceable label
    spendLimit: { amount: 5000, currency: "USD", period: "monthly" },
    allowedMerchantCategories: ["5045", "7372", "5734"], // software, SaaS, electronics
    expiry: { afterDays: 30 },               // auto-expire if unused
    singleUse: false,                        // multi-use within limits
  });

  return {
    agent: agentName,
    cardId: card.id,
    // Pass only the token to the agent — never the raw PAN
    paymentToken: card.paymentToken,
  };
}

The card paymentToken is what you give the agent to use in tool calls. It proxies to the real card number without exposing it — critical when your agent is running tool calls that might log their inputs.


Spend controls that actually work

Spend controls for agentic payments need to be more granular than “monthly limit.” A single runaway agent can hit a $50,000 monthly cap in minutes. Think in layers:

Layer 1: Per-transaction limits

const card = await limit.cards.create({
  spendLimit: {
    amount: 5000,      // $50.00 per transaction
    currency: "USD",
    period: "transaction",
  },
});

This prevents a single malformed tool call from buying a $500 GPU instance.

Layer 2: Velocity controls

const card = await limit.cards.create({
  velocityControls: {
    maxTransactionsPerHour: 3,     // no more than 3 purchases/hour
    maxTransactionsPerDay: 10,     // daily transaction count cap
    cooldownMinutes: 15,           // required pause between transactions
  },
});

Velocity controls catch runaway loops. If your agent hits a bug that causes it to retry a purchase, the velocity control kicks in before it drains your account.

Layer 3: Merchant category restrictions

// Allowlist approach — safest for narrow-purpose agents
const researchCard = await limit.cards.create({
  merchantFilter: {
    mode: "allowlist",
    mccs: [
      "7372", // Prepackaged Software
      "5045", // Computers and Peripherals
      "7374", // Computer Processing and Data Preparation
    ],
  },
});

// Blocklist approach — for general-purpose agents
const generalCard = await limit.cards.create({
  merchantFilter: {
    mode: "blocklist",
    mccs: [
      "4511", // Airlines
      "7011", // Hotels
      "5912", // Drug Stores
    ],
  },
});

Layer 4: Webhook alerts

// Set a webhook on card creation
const card = await limit.cards.create({
  label: "research-agent-001",
  spendLimit: { amount: 20000, currency: "USD", period: "monthly" },
  webhooks: {
    onTransaction: "https://your-app.com/webhooks/limit",
    onLimitApproach: "https://your-app.com/webhooks/limit", // at 80%
    onDecline: "https://your-app.com/webhooks/limit",
  },
});

// In your webhook handler
app.post("/webhooks/limit", async (req, res) => {
  const event = req.body;
  if (event.type === "transaction.declined") {
    // Alert operator, pause agent, log for audit
    await pauseAgent(event.card.label);
    await alertOncall(event);
  }
  res.sendStatus(200);
});

MCP integration: payments as a tool call

The cleanest pattern for agentic payments is exposing the payment capability as an MCP tool. The agent calls purchase or check_balance the same way it calls any other tool — no special payment logic in the agent itself.

Define the MCP server

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { LimitClient } from "@limit-md/sdk";

const limit = new LimitClient({ apiKey: process.env.LIMIT_API_KEY });

const server = new McpServer({
  name: "agent-payments",
  version: "1.0.0",
});

// Tool: purchase
server.tool(
  "purchase",
  "Make a purchase on behalf of the agent. Returns transaction ID and confirmation.",
  {
    amount_cents: z.number().int().positive().describe("Amount in cents (e.g. 4900 for $49.00)"),
    merchant_name: z.string().describe("Merchant name for confirmation"),
    description: z.string().describe("What this purchase is for (logged for audit)"),
    card_token: z.string().describe("The payment token for this agent"),
  },
  async ({ amount_cents, merchant_name, description, card_token }) => {
    try {
      const transaction = await limit.transactions.create({
        cardToken: card_token,
        amount: amount_cents,
        merchant: merchant_name,
        metadata: { agentDescription: description },
      });

      return {
        content: [{
          type: "text",
          text: JSON.stringify({
            success: true,
            transactionId: transaction.id,
            amount: `$${(amount_cents / 100).toFixed(2)}`,
            merchant: merchant_name,
            status: transaction.status,
          }),
        }],
      };
    } catch (error) {
      return {
        content: [{
          type: "text",
          text: JSON.stringify({
            success: false,
            error: error.message,
            code: error.code,
          }),
        }],
        isError: true,
      };
    }
  }
);

// Tool: check_spend
server.tool(
  "check_spend",
  "Check remaining spend limit on this agent's card.",
  { card_token: z.string() },
  async ({ card_token }) => {
    const balance = await limit.cards.getBalance(card_token);
    return {
      content: [{
        type: "text",
        text: JSON.stringify({
          spent: balance.spent,
          remaining: balance.remaining,
          limit: balance.limit,
          period: balance.period,
        }),
      }],
    };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);

Wire it to your agent

Example implementation:

import Anthropic from "@anthropic-ai/sdk";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

const anthropic = new Anthropic();

// Connect the MCP payment server
const transport = new StdioClientTransport({
  command: "node",
  args: ["agent-payments-mcp.js"],
  env: { LIMIT_API_KEY: process.env.LIMIT_API_KEY },
});

const mcpClient = new Client({ name: "agent", version: "1.0.0" });
await mcpClient.connect(transport);

// List tools from MCP server
const { tools } = await mcpClient.listTools();

// Now run the agent with payment tools available
const response = await anthropic.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 4096,
  tools: tools.map(tool => ({
    name: tool.name,
    description: tool.description,
    input_schema: tool.inputSchema,
  })),
  messages: [{
    role: "user",
    content: "Research the top 3 AI API providers and subscribe to the best one for our use case.",
  }],
});

// Handle tool calls in the agent loop
while (response.stop_reason === "tool_use") {
  const toolUse = response.content.find(b => b.type === "tool_use");
  const result = await mcpClient.callTool({
    name: toolUse.name,
    arguments: toolUse.input,
  });
  // ... continue the loop
}

The agent sees purchase and check_spendas ordinary tools. No payment code lives in the agent prompt or system instructions. The card token scoped to this agent's identity is injected via environment at server start — the model never sees the raw PAN.


Audit and observability

For production agents, you need a complete spend trail before anything fails:

// Every transaction comes through your webhook
// Structured log entry for audit
function logTransaction(event: LimitWebhookEvent) {
  logger.info({
    event: "agent_transaction",
    agentId: event.card.label,          // "research-agent-001"
    transactionId: event.transaction.id,
    amount: event.transaction.amount,
    merchant: event.transaction.merchant,
    approved: event.transaction.approved,
    timestamp: event.transaction.createdAt,
    cardId: event.card.id,
  });
}

Key audit dimensions:

  • Per-agent spend over time (catch slow leaks)
  • Merchant diversity (a research agent buying from 40 merchants in one hour is suspicious)
  • Decline rate (high decline rate = agent is attempting transactions outside its scope)
  • Transaction velocity (spikes indicate runaway loops)

Anti-patterns to avoid

  • Sharing a card across agents. Audit trail collapses, blast radius is unbounded, you can't revoke individual agents.
  • Passing raw card numbers to the agent context. If the model is logging its tool inputs (many do by default), your PAN ends up in log storage. Use payment tokens.
  • Monthly limits only. A $10,000 monthly cap is meaningless against a runaway agent that burns $500/minute. Layer per-transaction and velocity controls.
  • No webhook. Without real-time transaction events, you find out about anomalies in the next billing cycle. Set up webhooks on card creation.
  • Expiry-less cards. For single-task agents, set expiry: { afterDays: N }. A card that expires automatically can't be abused after the task is done.

Getting started with limit.md

limit.md is the developer-first platform for agentic payments. Built on enterprise card-issuing infrastructure, it gives you:

  • Virtual card API — issue isolated cards per agent in a single API call
  • Granular spend controls — per-transaction limits, velocity controls, MCC filters
  • MCP-native SDK — a payment MCP server that drops into any AI agent in under 30 minutes
  • Real-time webhooks — transaction events, limit alerts, decline notifications
  • Audit dashboard — per-agent spend timeline, merchant breakdown, anomaly detection
npm install @limit-md/sdk
import { LimitClient } from "@limit-md/sdk";
const limit = new LimitClient({ apiKey: process.env.LIMIT_API_KEY });

// Provision a card for your agent in one call
const card = await limit.cards.create({
  label: "my-first-agent",
  spendLimit: { amount: 10000, currency: "USD", period: "monthly" },
});

console.log(card.paymentToken); // Hand this to your agent

Join the waitlist at limit.md — or explore the API docs to see the full card issuance and spend control reference.


limit.md is built for developers shipping AI agents to production. If you're building agents that need to pay for things, this is the infrastructure layer.