Skip to main content

Spending Intents

What You’ll LearnThis page covers:
  • Why intents matter for AI agent accountability
  • Intent lifecycle (pending → matched/mismatched/expired)
  • Human-in-the-loop approval for high-value or sensitive purchases
  • How to create and manage intents
  • Automatic transaction matching
  • Best practices for intent tolerance and TTL
Time to read: 8 minutes
Intents are pre-transaction declarations that create an audit trail and enable automatic transaction matching. They answer the question: “What did the agent claim it was going to do?”
Terminology note: In Proxy, “Intent” refers to a spending intent — a declaration of what your agent plans to purchase. This is different from “intent” in LLM/NLU contexts (user intent classification) or Stripe’s PaymentIntent (a payment lifecycle object).Think of it as: “Before spending, declare your intent.”
Intents work with all card types. When a transaction arrives, it’s automatically matched against pending intents for that card.

Why Intents?

Without IntentsWith Intents
Transaction happens, no contextAgent declares intent before spending
Hard to audit what agent intendedClear audit trail of claimed purpose
Can’t detect unexpected purchasesTransactions matched against expectations

Intent Lifecycle

pending_approval → pending → matched    (approved, then transaction arrived within tolerance)
pending_approval → pending → mismatched (approved, then transaction outside tolerance)
pending_approval → pending → expired    (approved, but TTL reached)
pending_approval → rejected             (human rejected the intent)
pending          → matched              (no approval needed, transaction within tolerance)
pending          → mismatched           (no approval needed, transaction outside tolerance)
pending          → expired              (TTL reached, no transaction)
pending          → canceled             (manually canceled)
StatusDescription
pending_approvalAwaiting human approval before agent can proceed
pendingApproved and awaiting transaction
rejectedHuman rejected the intent
matchedTransaction arrived within tolerance
mismatchedTransaction arrived outside tolerance
expiredTTL reached without transaction
canceledManually canceled

Create an Intent

Before making a purchase, declare what you’re about to do:
curl -X POST https://api.useproxy.ai/v1/cards/$CARD_ID/intents \
  -H "Api-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "intentId": "order-123",
    "summary": "Order lunch on DoorDash",
    "expectedAmount": 2500,
    "expectedMerchant": "DoorDash",
    "tolerance": 500,
    "ttlMinutes": 30
  }'
{
  "id": "int_abc123",
  "cardId": "card_xyz789",
  "intentId": "order-123",
  "summary": "Order lunch on DoorDash",
  "expectedAmount": 2500,
  "expectedMerchant": "DoorDash",
  "tolerance": 500,
  "status": "pending",
  "expiresAt": 1703521800000,
  "createdAt": 1703520000000
}

Intent Fields

FieldRequiredDescription
intentIdYesYour unique ID (for idempotency)
summaryYesHuman-readable description
expectedAmountNoExpected amount in cents
expectedMerchantNoExpected merchant name
toleranceNoAllowed variance in cents (e.g., 500 = ±$5)
ttlMinutesNoTime to live (default 30, max 1440)
metadataNoAdditional context (taskId, orderId, etc.)

Human-in-the-Loop Approval

When to use HITL ApprovalEnable approval when you want human oversight before an AI agent can access card credentials. Common use cases:
  • High-value purchases above a threshold
  • Sensitive categories requiring human judgment
  • Compliance requirements for transaction authorization
When requireApproval is enabled on a card, agent, or policy, intents start in pending_approval status instead of pending. The agent cannot access card credentials until a human approves the intent.

Flow

Agent declares intent → status: pending_approval → webhook sent

Developer notified (your system) → calls /approve or /reject

If approved → status: pending → agent can get card details
If rejected → status: rejected → agent cannot proceed

Enable Approval

Set requireApproval at the card level:
curl -X POST https://api.useproxy.ai/v1/agents/$AGENT_ID/cards \
  -H "Api-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "purpose": "Shopping assistant purchases",
    "type": "multi",
    "requireApproval": true,
    "autoApproveBelow": 2500
  }'
FieldDescription
requireApprovalWhen true, intents require human approval
autoApproveBelowAuto-approve intents below this amount (cents)

Inheritance Chain

Approval settings follow this priority: Card > Agent > Policy
Policy sets requireApproval: true
  ↓ (agent inherits)
Agent can override with defaultRequireApproval: false
  ↓ (card inherits from agent)
Card can override with requireApproval: true

Auto-Approval Threshold

Use autoApproveBelow to skip human review for low-value intents:
{
  "requireApproval": true,
  "autoApproveBelow": 5000
}
With this config:
  • Intent for $30 → auto-approved (status: pending)
  • Intent for $75 → requires approval (status: pending_approval)

Approve an Intent

When you receive an intent.pending_approval webhook, review and approve:
curl -X POST https://api.useproxy.ai/v1/intents/$INTENT_ID/approve \
  -H "Api-Key: $API_KEY"
{
  "id": "int_abc123",
  "status": "pending",
  "approvedAt": 1703520600000,
  "approvedBy": "lk_live_abc..."
}

Reject an Intent

To reject with an optional reason:
curl -X POST https://api.useproxy.ai/v1/intents/$INTENT_ID/reject \
  -H "Api-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason": "Amount exceeds budget"}'
{
  "id": "int_abc123",
  "status": "rejected",
  "rejectedAt": 1703520600000,
  "rejectedBy": "lk_live_abc...",
  "rejectionReason": "Amount exceeds budget"
}

Intent Response with Approval Status

When an intent requires approval, the response includes the pending_approval status:
{
  "id": "int_abc123",
  "cardId": "card_xyz789",
  "intentId": "order-123",
  "summary": "Order lunch on DoorDash",
  "expectedAmount": 7500,
  "status": "pending_approval",
  "approvalRequired": true,
  "expiresAt": 1703521800000,
  "createdAt": 1703520000000
}

Agent Cannot Access Credentials Until Approved

If an agent tries to get card details before approval:
curl -X POST https://api.useproxy.ai/v1/cards/$CARD_ID/details \
  -H "Authorization: Bearer $AGENT_TOKEN"
{
  "error": {
    "code": "intent_pending_approval",
    "message": "Intent is awaiting approval. Wait for developer to approve before accessing credentials.",
    "param": "int_abc123"
  }
}

Automatic Matching

When a transaction webhook arrives from the payment network, the system automatically:
  1. Finds any pending intent for the card
  2. Compares the transaction amount against expectedAmount (within tolerance)
  3. Compares the merchant name against expectedMerchant (case-insensitive partial match)
  4. Updates the intent status to matched or mismatched
  5. Emits a webhook event (intent.matched or intent.mismatched)

Transaction Matching

When a transaction arrives, it’s matched against pending intents: Matched - Transaction is within tolerance:
{
  "status": "matched",
  "transactionId": "txn_def456",
  "matchResult": {
    "amountMatch": true,
    "merchantMatch": true,
    "actualAmount": 2450,
    "actualMerchant": "DOORDASH"
  }
}
Mismatched - Transaction outside tolerance:
{
  "status": "mismatched",
  "transactionId": "txn_def456",
  "matchResult": {
    "amountMatch": false,
    "merchantMatch": true,
    "actualAmount": 5000,
    "actualMerchant": "DOORDASH"
  }
}

List Intents

curl https://api.useproxy.ai/v1/cards/$CARD_ID/intents \
  -H "Api-Key: $API_KEY"
{
  "cardId": "card_xyz789",
  "intents": [
    {
      "id": "int_abc123",
      "intentId": "order-123",
      "summary": "Order lunch on DoorDash",
      "status": "matched",
      "createdAt": 1703520000000,
      "matchedAt": 1703520600000
    },
    {
      "id": "int_def456",
      "intentId": "order-124",
      "summary": "Order dinner on DoorDash",
      "status": "pending",
      "expiresAt": 1703525400000,
      "createdAt": 1703523600000
    }
  ]
}

Best Practices

The intent creates an audit trail. Even if you don’t use matching, it documents what the agent claimed.
Tips, taxes, and fees can vary. Set tolerance to account for this:
{
  "expectedAmount": 2500,
  "tolerance": 500
}
Add context that helps with debugging:
{
  "metadata": {
    "taskId": "task_123",
    "orderId": "order_456",
    "cartItems": ["burger", "fries"]
  }
}
Default is 30 minutes. Adjust based on your use case:
  • Quick purchases: 10-15 minutes
  • Complex checkouts: 30-60 minutes
  • Async workflows: up to 1440 (24 hours)

Intent vs Credential Access

Both create audit trails, but serve different purposes:
IntentCredential Access
Declared before any actionRequired to get PAN/CVV
Optional (but recommended)Required for card use
Matches against transactionsCorrelates with transactions
Has tolerance for varianceNo tolerance, just audit
For maximum accountability, use both:
  1. Create intent (declare what you’re about to do)
  2. Request credentials (get PAN/CVV with attestation)
  3. Make purchase
  4. Transaction matched to intent