Task mandates
A task mandate is permission for one task instead of one payment. The agent asks once, for example "buy 3 licences of the search API, at most 300 USD, within 2 hours". An owner or admin approves it once. While it is active, the payments of that task that would only wait for the approval threshold or an approval rule pass without asking again. It never lifts a cap, a deny, a security hold or the decision webhook.
Which problem does it solve?
- Cards cannot wait. Stripe gives Burnbound 2 seconds to answer a card authorization. A card payment that needs an approval is declined with
approval_required, because nobody can approve in that window. A mandate approved beforehand lets that payment through. - Approval fatigue. A task with ten payments over the threshold means ten approvals of the same thing. With a mandate, a person decides once, on the whole task: what it is for, how much, on which methods and hosts, for how long.
- Payments the user did not ask for. A mandate is narrow: an agent, a list of methods, optional hosts, merchants and payees, a budget and an end. That limits what a payment the agent is pushed into can take: one outside the scope, or one that hits a security hold a mandate never lifts, waits for a person as before. One inside the scope is covered, so keep the scope as narrow as the task.
How does the agent ask for one?
With the MCP tool request_mandate. Only the agent's own key (bb_agent_…) can ask, and only for itself.
| Field | Required | What it is |
|---|---|---|
purpose |
yes | What the task is, in the user's words, 1 to 200 characters. Shown to the approver. |
methods |
yes | The payment methods it covers, 1 to 20. See Which methods can it cover?. |
hosts |
no | Seller hosts it covers, in allowHosts syntax: an exact host (a port is allowed) or *.example.com. Omitted: any allowed host. |
merchantCategories |
no | Cards: Stripe merchant categories, such as computer_software_stores. |
merchantCountries |
no | Cards: merchant countries, ISO 3166-1 alpha-2, such as US. |
payees |
no | Who gets paid: addresses or ids. Omitted: any payee. |
maxUsd |
yes | The most the task may spend in total, in USD, such as "300". |
maxPayments |
no | The most payments it covers, 1 to 1000. Omitted: no count. |
durationMinutes |
yes | How long the task lasts once approved, 1 minute to 7 days (10080). |
context |
no | Facts for the approver, such as a ticket or an order. Shown labelled as declared by the agent, never verified. |
Each optional list holds 1 to 20 values. A filter you give must hold: a payment that does not have the fact is not covered. An x402 payment has no merchant category, so a mandate with merchantCategories never covers one.
{
"purpose": "Buy 3 licences of the search API for the Q4 report",
"methods": ["x402.exact"],
"hosts": ["api.example.com"],
"maxUsd": "300",
"maxPayments": 3,
"durationMinutes": 120,
"context": { "ticket": "OPS-1423" }
}
The answer carries the same fields as get_mandate_status (mandateId, status, usually pending, remainingUsd, expiresAt…), plus next (what the agent should do now) and, when the MCP server knows the dashboard's address, mandateUrl: the mandate's page in the dashboard. Clients that support URL-mode elicitation offer the user that page from the chat, as with a held payment, and approvalPrompt says what happened (opened, declined, cancelled, failed or unsupported). The text of that prompt is fixed: the amount and the duration, never the agent's purpose.
The request is refused, and nothing is asked, when:
| Error | Why |
|---|---|
mandate_exceeds_caps |
maxUsd is over the agent's daily cap times the days the task lasts, rounded up. The error carries maxUsd, the most it can ask for. |
mandate_queue_full |
The agent already has 5 mandates waiting for a decision. |
mandate_rejected_request |
The API refused the request for another reason, given in reason. |
How does it know where it stands?
get_mandate_status with the mandateId returns status, maxUsd, spentUsd, remainingUsd, maxPayments, usedPayments, durationMinutes, decidedAt and expiresAt. The agent only sees its own mandates: another agent's mandate reads as mandate_not_found. It never sees who decided or their note.
| Status | Meaning |
|---|---|
pending |
Waiting for a person. It expires with the organization's approval timeout (15 minutes by default). |
active |
Approved. It covers payments until expiresAt, the approval time plus durationMinutes. |
rejected |
A person rejected it. The task's payments wait for an approval each, as before. |
expired |
Nobody decided in time, or the task's time ran out. |
exhausted |
maxUsd or maxPayments is used up. It goes back to active if a covered payment is given back. |
revoked |
A person stopped it. The next payment of the task waits for an approval again. |
The time counts from the approval, not from the request: a 2-hour mandate approved at 10:00 ends at 12:00. If the agent waited on the chat prompt and the mandate was rejected or expired, request_mandate fails with mandate_rejected or mandate_expired. The agent should not ask again for the same task unless the user says so.
Who approves, rejects or revokes it?
Only an owner or an admin of the organization, signed in to the dashboard. The agent's key cannot, and nothing the MCP client answers in the chat decides anything. Members and viewers can see mandates, with the buttons disabled and the reason.
- Where: the approvals view has a task mandates tab next to held payments, and each agent's page has a task mandates section. The mandate's page shows the purpose as the agent wrote it, the scope, the budget, the duration and the declared context.
- Approve or reject a
pendingmandate, with an optional note of up to 500 characters. Approving needs a confirmation. With 20 mandates already active in the organization, approving fails withtoo_many_active_mandates. - Revoke an
activeorexhaustedmandate. It stops at once; what was already paid stays paid. - Slack and email: a new mandate is announced like a held payment, through the same channels and with the same hourly limits: email on every plan, Slack on Team and up. The message shows our amount and duration in the headline, then the agent's purpose and scope as plain text after fixed labels, and the declared context as unverified. Its Approve and Reject buttons only open the mandate's page in the dashboard, where you confirm signed in.
Which payments does it cover?
A mandate only turns into an allow a payment the policy would hold for one of two reasons:
threshold: its amount is at or above the agent's approval threshold;rule: an enforced custom rule asks for approval, the agent's, its subteam's or the organization's.
And only when all of this holds:
- the payment is the mandate's agent's;
- its method is in
methods; - it meets every filter given (hosts, merchant categories, merchant countries, payees);
- its amount in USD fits in
maxUsd − spentUsd; - a payment is left, if there is
maxPayments; - the mandate is
activeand beforeexpiresAt.
If several mandates cover a payment, the one that ends first is used. A payment that does not fit waits for an approval as before: the mandate is not split or stretched.
What does it never cover?
A mandate never widens the policy. These apply exactly as without it:
- Denies: the maximum per payment, the daily cap, a person's sub-cap, subteam and organization budgets,
limitanddenyrules. The daily cap still applies to each day of the task. - Security holds: a 402 that reads like instructions (
instruction_like_description), the first payment to a new receiving address (new_pay_to), the first payment to a catalog host (catalog_new_host). - The decision webhook: a payment your decision webhook holds keeps waiting, even if a rule also holds it.
The ceiling on maxUsd follows from this. A mandate cannot hold more than the agent may spend in the days it lasts: its daily cap times the days, rounded up, so 2 hours counts as 1 day. With a daily cap of 100 USD, a 2-hour mandate asks for at most 100 USD and a 3-day one for at most 300 USD. Above that, mandate_exceeds_caps says the most.
How is it used up?
- Each covered payment adds its amount in USD to
spentUsdand 1 tousedPayments, in the same step that reserves the payment, so two payments at once cannot both take the last of it. - When
spentUsdreachesmaxUsd, orusedPaymentsreachesmaxPayments, the mandate becomesexhausted. - A covered payment that fails or expires gives its amount and its count back. An
exhaustedmandate becomesactiveagain. - If another payment took what was left between the decision and the reservation, or a person revoked it,
authorizeanswersagent_busyand the retry decides again with the mandate as it is now. A card in that case is declined. fetch_paidandstripe_refundreturn themandateIdthat covered the payment.
Which methods can it cover?
Any payment method of the agent, through the same policy:
| Method | Kind |
|---|---|
x402.exact |
x402 API payment |
mpp.tempo.charge |
MPP payment on Tempo |
card.stripe_issuing |
Stripe Issuing card |
action.stripe.refund |
Stripe refund, via the gateway |
action.stripe.credit |
Stripe customer credit |
action.shopify.refund |
Shopify refund, via the gateway |
A mandate does not turn a method on. The method must also be allowed for the agent in its Payment methods (see Which ways can an agent spend?). Mandates are on every plan.
What goes into the audit?
Every change of a mandate is an audit event with the agent, mandateId, status, maxUsd, spentUsd, methods and expiresAt:
| Event | When |
|---|---|
mandate.requested |
The agent asked for it. |
mandate.approved |
A person approved it. With who and their note. |
mandate.rejected |
A person rejected it. With who and their note. |
mandate.revoked |
A person revoked it. With who and their note. |
mandate.expired |
It ended on its own: reason is ttl (nobody decided in time) or duration (the task ended). |
mandate.exhausted |
A payment used up its budget or its payments. |
For each covered payment, payment.authorized carries the mandateId, and the payment records which mandate covered it. The policy's own decision is kept as it was, the hold, so the audit shows both what the policy said and which mandate let the payment through. If a Slack message or an email about a mandate fails, approval.notification_failed names the mandateId and the channel.