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

# Default Variables

> Read who is calling, from where, and how much the run has used with bud variables in an agent's prompts

## Overview

Every agent run comes with a set of **default variables**: who started the run, how it arrived, where the caller is,
which project the agent belongs to, and how many tokens the run has used so far. You read them in the agent's prompts
with `{{ bud.<name> }}`.

* They work in the **system prompt** and in **prompt messages**.
* The caller sends nothing extra. Bud fills them on the server from the request's credentials, network address and
  entry path.
* A value that does not apply to a run (for example the trigger fields on an API call) renders as an empty string.

```mermaid theme={null}
graph LR
    A[Caller] --> B[Gateway]
    B -- identity, IP location, User-Agent --> C[Agent runtime]
    C -- entry path, project, live counters --> D[Prompt rendered with bud values]
    D --> E[Model]
```

## Use them in a prompt

Write the variables into the prompt like any other template variable. This system prompt:

```jinja theme={null}
You are the support assistant for Acme.

{% if bud.user_country == "IN" %}
Quote prices in INR.
{% else %}
Quote prices in USD.
{% endif %}

The customer is in {{ bud.city | default("an unknown city", true) }} ({{ bud.timezone }}).
Their local time is {{ bud.local_time }}.
```

reaches the model like this for an API call from Chennai:

```text theme={null}
You are the support assistant for Acme.

Quote prices in INR.

The customer is in Chennai (Asia/Kolkata).
Their local time is 2026-10-07T15:41:35+05:30.
```

Your own prompt variables (`{{ customer_name }}`) are rendered in the same pass, so you can mix both freely.

### Add them in the agent builder

1. Open the agent and click the **System Prompt** node.
2. Click **Expand system prompt** and stay on the **System Prompt** tab.
3. Type `{{ bud.` — a **Governance context** list opens with every variable and a short description.
4. Keep typing to filter, then pick one with the arrow keys and **Enter** (or click it).
5. Click **Save**.

In the **Prompt Message** tab, type the variable by hand, for example `{{ bud.city }}`.

### Call the agent

Nothing changes in the request. Call the agent as usual and Bud fills the values:

```bash theme={null}
curl https://<gateway>/v1/responses \
  -H "Authorization: Bearer $BUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": {"id": "my-agent"},
    "input": "What is the price of the Pro plan?"
  }'
```

<Tip>
  To see exactly what an agent receives, add a block like this to its system prompt while you test, then ask the agent
  to repeat it:

  ```jinja theme={null}
  user_type: {{ bud.user_type }}
  channel: {{ bud.channel }}
  client_type: {{ bud.client_type }}
  user_country: {{ bud.user_country }}
  region: {{ bud.region }}
  city: {{ bud.city }}
  timezone: {{ bud.timezone }}
  continent: {{ bud.continent }}
  local_time: {{ bud.local_time }}
  principal: {{ bud.principal }}
  project_id: {{ bud.project_id }}
  trigger_origin: {{ bud.trigger_origin }}
  connection_id: {{ bud.connection_id }}
  provider: {{ bud.provider }}
  recursion_depth: {{ bud.recursion_depth }}
  tokens_so_far: {{ bud.tokens_so_far }}
  cost_so_far: {{ bud.cost_so_far }}
  ```
</Tip>

## Variable reference

| Variable | What it holds | Example |
| - | - | - |
| `bud.user_type` | Who started the run: `end_user`, `agent`, `trigger` or `channel` | `end_user` |
| `bud.channel` | How the run arrived: `api`, `a2a`, `trigger` or `channel` | `api` |
| `bud.client_type` | The caller's `User-Agent` header, trimmed and limited to 512 characters | `my-app/1.4` |
| `bud.user_country` | Country of the caller's IP address, as an ISO 3166-1 alpha-2 code | `IN` |
| `bud.region` | State or region of the caller's IP address, as its subdivision code (its name when there is no code) | `TN` |
| `bud.city` | City of the caller's IP address, in English | `Chennai` |
| `bud.timezone` | Time zone of the caller's IP address, as an IANA name | `Asia/Kolkata` |
| `bud.continent` | Continent code: `AF`, `AN`, `AS`, `EU`, `NA`, `OC` or `SA` | `AS` |
| `bud.local_time` | The caller's local time when the request arrived, ISO 8601 to the second with UTC offset | `2026-10-07T15:41:35+05:30` |
| `bud.principal` | Who invoked the run: your Bud user ID, the event's actor, or the chat user | `U04ABCD1234` |
| `bud.project_id` | The ID of the project the agent belongs to | `9b2f7c1a-4d3e-4b8a-9f6e-2c1d0a7b5e43` |
| `bud.trigger_origin` | The event that started a trigger run | `issues.opened` |
| `bud.connection_id` | The event connection that delivered the event or chat message | `conn_0192c3d4e5f67a8b9c0d1e2f3a4b5c6d` |
| `bud.provider` | The platform the event or chat message came from | `github` |
| `bud.tokens_so_far` | Input and output tokens used by this run's model calls so far (a number) | `792` |
| `bud.cost_so_far` | Cost of this run's model calls so far, in USD at public list prices (a number) | `0.0042` |
| `bud.recursion_depth` | How deep the run is in a chain of agents calling agents (a number; `0` at the top) | `1` |

## Values by entry path

What each variable holds depends on how the agent was started.

| # | Variable | API key | Studio / Playground | Background (`background: true`) | Sub-agent (A2A) | Trigger | Bot chat |
| - | - | - | - | - | - | - | - |
| 1 | `user_type` | `end_user` | `end_user` | `end_user` | `agent` | `trigger` | `channel` |
| 2 | `channel` | `api` | `api` | `api` | `a2a` | `trigger` | `channel` |
| 3 | `client_type` | Caller's User-Agent | Playground server's User-Agent | As submitted | Empty | Empty | Empty |
| 4 | `user_country` | From the caller's IP | From the Playground server's IP | As submitted | Empty | Empty | Empty |
| 5 | `region` | From the caller's IP | From the Playground server's IP | As submitted | Empty | Empty | Empty |
| 6 | `city` | From the caller's IP | From the Playground server's IP | As submitted | Empty | Empty | Empty |
| 7 | `timezone` | From the caller's IP | From the Playground server's IP | As submitted | Empty | Empty | Empty |
| 8 | `continent` | From the caller's IP | From the Playground server's IP | As submitted | Empty | Empty | Empty |
| 9 | `local_time` | When the request arrived | When the request arrived, in the Playground server's time zone | When the request was submitted | Empty | Empty | Empty |
| 10 | `principal` | Empty | Your Bud user ID | As submitted | Empty | The event actor's ID on the provider | The chat user's platform ID |
| 11 | `project_id` | Agent's project | Agent's project | Agent's project | Sub-agent's project | Agent's project | Agent's project |
| 12 | `trigger_origin` | Empty | Empty | Empty | Empty | Event type | Empty |
| 13 | `connection_id` | Empty | Empty | Empty | Empty | Connection ID | Connection ID |
| 14 | `provider` | Empty | Empty | Empty | Empty | `github`, `linear`, … | `slack`, `teams`, … |
| 15 | `tokens_so_far` | `0`, then live | `0`, then live | `0`, then live | `0`, then live (its own calls only) | `0`, then live | `0`, then live |
| 16 | `cost_so_far` | Empty, then live | Empty, then live | Empty, then live | Empty, then live (its own calls only) | Empty, then live | Empty, then live |
| 17 | `recursion_depth` | `0` | `0` | `0` | `1` (`2` for a sub-agent's sub-agent) | `0` | `0` |

* **API key** — a `/v1/responses` call authenticated with an API key. `principal` is empty because a key is not a
  person.
* **Studio / Playground** — testing the agent while signed in. The Playground server sends the request to the gateway
  on your behalf, so the location values and `client_type` describe that server, not your browser, and the location
  values can be empty.
* **Background** — a `/v1/responses` call with `background: true`. The values are captured when the request is
  submitted and reused when the run executes, so `local_time` is the submission time even if the run starts later.
  `principal` is your user ID when you submit while signed in, and empty for an API key.
* **Sub-agent (A2A)** — the agent is called by another agent as a tool. It reports its own project and counts only its
  own model calls; the calling agent's usage is not included.
* **Trigger** — an event from an [event connection](/connectors/guides/event-connections) starts the run.
  `trigger_origin` is the provider's event, such as `issues.opened` or `pull_request.closed` for GitHub and
  `Issue.create` for Linear. `principal` is the actor's ID on that provider, for example the GitHub user's numeric ID.
* **Bot chat** — a message to the agent in Slack or Teams. `principal` is the chat user's ID on that platform, such as
  a Slack `U…` ID.

<Note>
  Every `/v1/responses` call reports `channel` as `api`, including calls from Studio and the Playground. Each turn of a
  conversation is a separate request: the values are filled again and the counters start from zero.
</Note>

<Note>
  To try prompts that use location or `client_type`, call the agent through the API from the client you care about
  rather than from the Playground.
</Note>

## Location values

`user_country`, `region`, `city`, `timezone` and `continent` come from the caller's public IP address, looked up in the
IP location database bundled with the gateway (MaxMind GeoLite2 City). `local_time` is the current time in that
`timezone`.

* Only the fields the database knows are filled; the rest stay empty. Many addresses are known only at country level,
  which is common for VPNs, mobile networks and cloud or anycast addresses. For example, `8.8.8.8` gives `US`,
  `America/Chicago` and `NA` with no region or city.
* Private and internal addresses are never looked up, so their location values are empty.
* `region` is the code of the state or region within the country, such as `TN` for Tamil Nadu, not the full name.

## Live counters

`tokens_so_far` and `cost_so_far` change while the run is in progress. When an agent calls tools, it makes several
model calls in one run, and each call sees the totals of the calls before it:

```jinja theme={null}
Tokens used so far: {{ bud.tokens_so_far }}
```

| Model call | What the model sees |
| - | - |
| First call | `Tokens used so far: 0` |
| Call after a tool result | `Tokens used so far: 792` |

* The stored conversation and the response's `instructions` keep the text from the start of the run. Traces show the
  text each model call actually received.
* The counters cover one request. A new turn of a conversation, and a sub-agent's run, start from zero.
* `tokens_so_far` counts input and output tokens.
* `cost_so_far` uses public list prices in USD, so it is an estimate, not your bill. It stays empty until a call can be
  priced, and stays empty for models without a public price.
* Very small costs print in scientific notation (`1.23e-05`). Format them for the model with
  `{{ "%.6f" | format(bud.cost_so_far) if bud.cost_so_far else "n/a" }}`.

A common use is a budget guard:

```jinja theme={null}
{% if bud.cost_so_far and bud.cost_so_far > 0.05 %}
You are close to this run's budget. Finish the task now and summarise what is left.
{% endif %}
```

## Empty values and safe templates

A value that is missing renders as an empty string. Write templates that still read well when it is.

* **Give a fallback** with `default(..., true)` or `or`. The `true` matters: without it, `default` leaves an empty
  value unchanged.

  ```jinja theme={null}
  {{ bud.city | default("unknown", true) }}
  {{ bud.city or "unknown" }}
  ```

* **Branch on a value** with `{% if %}`:

  ```jinja theme={null}
  {% if bud.user_type == "channel" %}
  Keep replies short; this is a chat conversation.
  {% endif %}

  {% if bud.recursion_depth >= 2 %}
  Do not call other agents.
  {% endif %}
  ```

* **Check spelling.** A name that is not in the [list above](#variable-reference), such as `{{ bud.citty }}`, also
  renders as an empty string, with no error.

<Warning>
  Comparing an empty `cost_so_far` with a number fails the request with `Template rendering failed`. It is empty at the
  start of every run, so always check it first: `{% if bud.cost_so_far and bud.cost_so_far > 0.5 %}`.
  `tokens_so_far` and `recursion_depth` are always numbers and are safe to compare.
</Warning>

## Reserved names

`bud`, `ctx`, `headers` and `meta` are reserved, including any dotted name that starts with them (such as `bud.city`).
You cannot use them as names for your own input variables; the agent builder marks such a variable as a reserved name
and will not save until you rename it.

A plain `{{ city }}` is an ordinary input variable that the caller fills, not the default value. Use `{{ bud.city }}`
for the value Bud provides.

## Limits

* Default variables are rendered in the system prompt and prompt messages only. The request's `input`, tool
  descriptions and input/output schemas are passed through as written.
* `client_type` is whatever `User-Agent` the caller sends. Use it to adapt the reply, not to make security decisions.
* `local_time` is fixed when the request arrives and does not advance during a long run.
* In background runs, default variables are rendered in plain-text prompt messages only, not in messages that contain
  images or files.

## Next Steps

<CardGroup cols={2}>
  <Card title="Version Management" icon="code-branch" href="/prompts-agents/guides/version-management">
    Roll out prompt changes that use default variables safely.
  </Card>

  <Card title="Context Compaction" icon="compress" href="/prompts-agents/guides/context-compaction">
    Keep long agent conversations inside the context window.
  </Card>

  <Card title="Event Connections" icon="bolt" href="/connectors/guides/event-connections">
    Start agents from events and fill the trigger variables.
  </Card>

  <Card title="Create a Response" icon="terminal" href="/api-sdk/responses/create-response">
    Call an agent through the Responses API.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.