> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kordio.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Give an agent a budget for one run

> Open a budget, spend against it, and stop a loop at the envelope.

A per-transaction cap stops one big mistake. It does nothing about the same reasonable-looking
\$340 charge happening nineteen times, which is the failure agents actually have. A budget is the
control that answers that one, and it is the first thing to reach for on any run.

## Before you start

<Snippet file="env-agents.mdx" />

The amounts here assume the agent's starter policy is disabled, as the
[quickstart](/agents/quickstart) does in step 2. Leave it on and it holds anything over \$100 for
a person before your own rules get a say.

## 1. Open one for the run

An agent opens its own budget with its own key. Open it where the run starts, not where the
process starts:

```bash theme={"dark"}
BUDGET=$(curl -s -X POST https://api.kordio.io/control/v1/agent/budgets \
  -H "Authorization: Bearer $KORDIO_AGENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"budget_cents": 50000, "currency": "USD"}')

export KORDIO_BUDGET_ID=$(echo "$BUDGET" | jq -r '.data.id')
```

```json 201 Created theme={"dark"}
{
  "data": {
    "id": "3f1c8a92-5d41-4c8e-9f2b-7a6e1d0c4b83",
    "agent_id": "7d2a6c15-3f89-4b02-9e64-8a15d7c0b3f2",
    "parent_budget_id": null,
    "budget_cents": 50000,
    "spent_cents": 0,
    "remaining_cents": 50000,
    "currency": "USD",
    "status": "active",
    "created_at": "2026-08-22T09:11:52Z"
  }
}
```

How large a budget an agent may open is capped by the agent's `max_budget_cents`, which is set
from the console and cannot be raised with an agent key. Ask for more and the call is refused
before anything exists:

```json 422 Unprocessable Entity theme={"dark"}
{
  "error": { "message": "This agent may not open a budget above $1000.00", "code": "budget_ceiling" },
  "ceiling_cents": 100000,
  "requested_cents": 250000
}
```

## 2. Spend against it

Every authorization names the budget, and an allowed one reserves from it immediately:

```bash theme={"dark"}
ACTION=$(curl -s -X POST https://api.kordio.io/control/v1/agent/actions \
  -H "Authorization: Bearer $KORDIO_AGENT_KEY" \
  -H "Idempotency-Key: order-4471-attempt-1" \
  -H "Content-Type: application/json" \
  -d "{
    \"budget_id\": \"$KORDIO_BUDGET_ID\",
    \"action_type\": \"payment.create\",
    \"resource\": \"acme-supplies.example\",
    \"cost_cents\": 12000
  }")

export KORDIO_ACTION_ID=$(echo "$ACTION" | jq -r '.data.id')
```

```json 201 Created theme={"dark"}
{
  "data": {
    "id": "9b2ef0c1-6a3d-4e75-8c19-2f4b7d5a0e63",
    "budget_id": "3f1c8a92-5d41-4c8e-9f2b-7a6e1d0c4b83",
    "cost_cents": 12000,
    "state": "pending"
  },
  "decision": {
    "outcome": "allowed",
    "rule": null,
    "detail": {},
    "headroom": { "session_remaining_cents": 50000 }
  },
  "cosignature": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImtleV8xIn0..."
}
```

The reservation is held until you report what happened. `complete` keeps it, `fail` gives it
back:

```bash theme={"dark"}
curl -s -X POST https://api.kordio.io/control/v1/agent/actions/$KORDIO_ACTION_ID/fail \
  -H "Authorization: Bearer $KORDIO_AGENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason": "vendor returned 502"}'
```

```json 200 OK theme={"dark"}
{
  "data": {
    "id": "9b2ef0c1-6a3d-4e75-8c19-2f4b7d5a0e63",
    "cost_cents": 12000,
    "state": "failed",
    "failure_reason": "vendor returned 502"
  }
}
```

<Warning>
  Nothing sweeps a reservation that was never reported. An agent that dies between the `201` and
  the `complete` leaves its budget held for good, so open a budget per run and let it end with
  the run rather than reusing one across runs.
</Warning>

## 3. Watch the loop hit the wall

A budget refuses on the total, not on the individual amount, and it refuses before any rule you
wrote gets a say. The nineteenth reasonable charge is the one that stops:

```bash theme={"dark"}
curl -s -X POST https://api.kordio.io/control/v1/agent/actions \
  -H "Authorization: Bearer $KORDIO_AGENT_KEY" \
  -H "Idempotency-Key: order-4489-attempt-1" \
  -H "Content-Type: application/json" \
  -d "{
    \"budget_id\": \"$KORDIO_BUDGET_ID\",
    \"action_type\": \"payment.create\",
    \"resource\": \"acme-supplies.example\",
    \"cost_cents\": 12000
  }"
```

```json 403 Forbidden theme={"dark"}
{
  "data": {
    "id": "2c7f4b81-0d63-4a19-8e05-1b9c6f3a7d24",
    "cost_cents": 12000,
    "state": "denied"
  },
  "decision": {
    "outcome": "denied",
    "rule": "session_budget",
    "detail": { "remaining_cents": 2000, "requested_cents": 12000 },
    "headroom": { "session_remaining_cents": 2000 }
  }
}
```

`session_budget` is one of three intrinsic rules that run ahead of every policy and cannot be
switched off. Give `headroom` to your agent: one that knows it has \$20 left picks a cheaper
option, and one that only knows it was refused retries into the same wall.

Subscribe to `budget.low` to hear about it early. It fires when a budget drops to 10% or less of
its size, after an executed payment.

## 4. Split it between sub-agents

A budget can open a child budget, and the child reserves its whole size from the parent when it
opens. Children therefore cannot sum past what the parent has left:

```bash theme={"dark"}
curl -s -X POST https://api.kordio.io/control/v1/agent/budgets \
  -H "Authorization: Bearer $KORDIO_AGENT_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"budget_cents\": 20000, \"parent_budget_id\": \"$KORDIO_BUDGET_ID\"}"
```

```json 201 Created theme={"dark"}
{
  "data": {
    "id": "c6a83f10-27d9-4b54-8e1f-0a95d3c72b68",
    "agent_id": "7d2a6c15-3f89-4b02-9e64-8a15d7c0b3f2",
    "parent_budget_id": "3f1c8a92-5d41-4c8e-9f2b-7a6e1d0c4b83",
    "budget_cents": 20000,
    "spent_cents": 0,
    "remaining_cents": 20000,
    "currency": "USD",
    "status": "active",
    "created_at": "2026-08-22T09:14:20Z"
  }
}
```

Hand the child id to the sub-agent and it can spend $200 of the parent's $500, whatever it is
told to do. A child that asks for more than the parent has left is refused with
`parent_budget_exhausted`, again before anything exists.

## What a budget is not

* It is not a time limit. A budget has no expiry and no close endpoint, so it stops spend by
  running out, not by ending. Size it for one run.
* It is not per counterparty or per action type. Narrow those with a policy rule; the budget
  counts every authorized action the run makes.
* It is not shared between agents. A budget belongs to the agent that opened it, and a child
  budget must belong to the same agent as its parent.
* It is not money. Kordio holds no funds. A budget is authority to spend on your own rails, and
  spend on actions and payments draws down the same number.

## Next steps

<CardGroup cols={2}>
  <Card title="Roll a policy out safely" icon="flask-conical" href="/agents/guides/policy-rollout">
    Add rules to the run once the envelope is in place.
  </Card>

  <Card title="Spend tokens" icon="ticket" href="/agents/spend-tokens">
    Narrow one payment inside the budget to one vendor, once.
  </Card>

  <Card title="How a decision is reached" icon="shield" href="/agents/concepts">
    Where the budget sits in the evaluation order.
  </Card>

  <Card title="Errors" icon="triangle-alert" href="/agents/errors">
    `budget_ceiling`, `parent_budget_exhausted`, and the rest.
  </Card>
</CardGroup>
