{
  "question": "How do I stop several AI agents from overspending a shared team budget when they pay at the same time?",
  "slug": "how-to-reserve-against-a-shared-team-spend-pool-so-concurrent-ai-agents-cannot-overspend-it",
  "url": "https://www.rightshold.com/answers/how-to-reserve-against-a-shared-team-spend-pool-so-concurrent-ai-agents-cannot-overspend-it",
  "short_answer": "Make every agent reserve before it pays, in one atomic step at the place that holds the pool. A remaining-balance number in the prompt, or a \"check the balance, then pay\" pair of calls, is not a lock: two agents can both read 400.00 left, both pay 300.00, and the team ends 200.00 over. The fix is a hold: one operation that checks limit minus settled minus held, and adds the hold only if the amount still fits, so the second agent's request fails instead of reading a stale number. Release the hold when the payment settles (turn it into spend at the settled amount) or when it's cancelled or expires. Card issuers work the same way: Stripe Issuing holds an authorization's amount until capture, and a cardholder's spending limits apply across all of their cards, but Stripe says spend aggregation is best effort with up to 30 seconds of delay, so a burst can slip past a card limit. Keep your own reservation in front of it. On Rightshold, authorize_spend checks and records each allow against a credential's daily cap in one server-side step, so two allows on the same credential are decided one after the other. It caps each agent; it isn't a shared pool with holds and releases.",
  "checklist": [
    "Put the team pool in one place that every agent's payment path goes through: a row in your database, a ledger service or the card issuer's cardholder limit. Not a number in each agent's context.",
    "Track three numbers for the pool: limit, settled and held. Available is limit minus settled minus held.",
    "Reserve before paying with a single conditional write, for example UPDATE pool SET held = held + :amount WHERE id = :pool AND limit - settled - held >= :amount, and treat zero rows updated as a deny. Don't read the balance in one call and write the hold in another.",
    "Give each hold an id, the agent, the amount, the merchant and an expiry. Pass the hold id as the payment's idempotency key or reference so a retry reuses the hold instead of taking a second one.",
    "On settlement, move the hold to settled at the settled amount (it may be lower, or higher after tips and fees). On cancel, decline or expiry, release it. Run a sweeper so holds from crashed agents expire instead of locking the pool forever.",
    "Give each agent its own ceiling too: issue_agent_credential plus set_spend_policy with per_tx_max_minor and daily_max_minor, so one runaway agent can't drain the whole pool before the others get a turn.",
    "Call authorize_spend for the agent's credential after the pool hold succeeds, with reference set to the hold id, so the per-agent decision and the pool hold point at each other in the record.",
    "Keep the card limit as the outer backstop (a Stripe Issuing cardholder limit covers all of that cardholder's cards), and reconcile holds against card authorizations and captures daily."
  ],
  "sections": [
    {
      "heading": "Why a remaining balance in the prompt isn't a lock",
      "points": [
        "Each agent sees the balance as it was when it last read it. When two agents read, decide and pay in parallel, both reads can be correct and the total still overspends. Databases call this check-then-act race write skew: PostgreSQL's isolation docs show two transactions that each read a sum and then write a row, and both commit under Repeatable Read; only Serializable rolls one back.",
        "A conditional UPDATE avoids that without special isolation. Under PostgreSQL's default Read Committed level, a second UPDATE on the same row waits for the first to commit, then re-checks its WHERE clause against the updated row. If the hold no longer fits, it updates nothing, and that is your deny.",
        "Telling agents to check with each other, or giving them a shared notes file, has the same flaw: there's still a gap between reading and paying."
      ]
    },
    {
      "heading": "Hold at authorize time, release on settle or expiry",
      "points": [
        "A hold is what card networks already do. Stripe's docs describe placing a hold that reserves funds and capturing later, with online card authorizations usually valid for 7 days; if not captured in time the funds are released. On Stripe Issuing, an approved authorization holds the amount and capture releases the hold and posts the transaction.",
        "Settled can differ from held. Stripe Issuing allows partial capture (less than authorized) and, in some merchant categories such as restaurants and ground transportation, over capture, and its spending controls note that tips and fees can be posted later and push spend past a limit. Settle the pool at the captured amount and keep headroom for categories that over-capture.",
        "Holds need an owner and an expiry. If an agent crashes between reserving and paying, its hold should lapse on a timer (a few minutes for API calls, longer for orders that capture days later) rather than wait for someone to notice."
      ]
    },
    {
      "heading": "What Rightshold does and doesn't do for a shared pool",
      "points": [
        "set_spend_policy puts a per-transaction and daily cap on one agent credential, in one currency. authorize_spend reads that credential's total for the UTC day and records the allow or deny in one synchronous step on the server, so allows on the same credential can't race each other, and the signed receipt shows the total before the decision.",
        "An allow counts toward the daily cap as soon as it's recorded and isn't released if the purchase never happens: it stays counted until the day resets at midnight UTC. Merchants should accept the receipt only before its valid_until, 15 minutes after it was issued.",
        "There's no team pool with holds and releases across several credentials in authorize_spend. Build the pool reservation in your payment service or at the card issuer, use Rightshold for each agent's ceiling and the signed decision record, and put the hold id in reference so an auditor can match them."
      ]
    }
  ],
  "checks": [],
  "ask_rightshold": {
    "start": "https://www.rightshold.com/start?ref=seo",
    "mcp_endpoint": "https://www.rightshold.com/mcp",
    "ask": "Three of my agents share a 500.00 USD team budget. Before this agent pays, check its own spending policy and record the decision, and tie it to the hold I took on the team pool.",
    "mcp_call": {
      "tool": "authorize_spend",
      "arguments": {
        "credential_id": "crd_…",
        "amount_minor": 30000,
        "currency": "USD",
        "merchant_domain": "api.vendor.example",
        "category": "saas",
        "reference": "pool-hold_…"
      }
    },
    "also": [
      "Needs your API key. It returns a signed allow or deny with reasons (over_per_transaction_cap, over_daily_cap, merchant_not_allowed and others) and the credential's spend for the day before the decision. It records the decision only and never moves money.",
      "Set each agent's ceiling first with set_spend_policy (owner account key): currency, per_tx_max_minor, daily_max_minor and allowed_domains.",
      "verify_spend_authorization is free, so the payment step or the merchant can confirm the receipt is genuine and an allow before it pays.",
      "The team pool hold itself lives in your database or at the card issuer; Rightshold doesn't hold or release pool balances across credentials."
    ],
    "scope": "Rightshold records spending policies and signs allow/deny decisions per agent credential on a hash-chained ledger. It doesn't hold money, keep a shared pool balance, place or release holds, or see card authorizations and captures. The atomic reservation, its release and reconciliation are steps you run in your payment path. Not financial advice."
  },
  "sources": [
    {
      "title": "PostgreSQL docs: Transaction Isolation (Read Committed re-checks WHERE on concurrent updates; write skew under Repeatable Read)",
      "url": "https://www.postgresql.org/docs/current/transaction-iso.html"
    },
    {
      "title": "Stripe Docs: Place a hold on a payment method (authorization validity windows)",
      "url": "https://docs.stripe.com/payments/place-a-hold-on-a-payment-method"
    },
    {
      "title": "Stripe Issuing: spending controls (cardholder limits across cards; aggregation delay up to 30 seconds)",
      "url": "https://docs.stripe.com/issuing/controls/spending-controls"
    },
    {
      "title": "Stripe Issuing: transactions (holds, partial capture, over capture)",
      "url": "https://docs.stripe.com/issuing/purchases/transactions"
    },
    {
      "title": "Agent spending policies on Rightshold",
      "url": "https://www.rightshold.com/agent-spending-policies"
    }
  ],
  "related": [
    {
      "title": "How to set a separate budget for each AI agent calling paid APIs",
      "url": "https://www.rightshold.com/answers/how-to-set-a-separate-budget-for-each-ai-agent-calling-paid-apis"
    },
    {
      "title": "How to cap an AI agent's spend per vendor per day across multiple tasks",
      "url": "https://www.rightshold.com/answers/how-to-cap-an-ai-agents-spend-per-vendor-per-day-across-multiple-tasks"
    },
    {
      "title": "How to stop an AI agent from paying twice when it retries",
      "url": "https://www.rightshold.com/answers/how-to-stop-an-ai-agent-from-paying-twice-when-it-retries"
    },
    {
      "title": "How to enforce AI agent payment caps at the execution boundary, not in the prompt",
      "url": "https://www.rightshold.com/answers/how-to-enforce-ai-agent-payment-caps-at-the-execution-boundary-not-in-the-prompt"
    }
  ],
  "computed_from": "Curated guidance; license verdicts computed from Rightshold's license profiles at render time. Not legal advice.",
  "updated": "2026-11-08"
}