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

# Budgets

> Set spending limits on any scope (workspace, project, identity, API key, provider, or model) to control AI costs across the organization.

**Budgets** apply spending controls to a named target. Each budget defines a scope (what it applies to), period limits (how much can be spent or consumed per reset period), and an optional expiration date after which the budget stops enforcing.

When a cost or token limit is reached, requests matching the budget's scope are blocked until the period resets. Requests per minute limits block for one minute only, independent of the reset period. Multiple budgets at different levels can apply to the same request at once, and each is enforced independently; see [Budget levels and interaction](#budget-levels-and-interaction).

Common use cases:

* Cap monthly spend per customer or tenant by scoping a budget to an **Identity**.
* Prevent a staging or CI environment from consuming production credits by scoping a budget to its **API Key**.
* Limit exposure to an expensive provider or model during evaluation by scoping a budget to a **Provider** or **Model**.
* Enforce a hard workspace-wide cost ceiling as a safety net by scoping a budget to the **Workspace**.
* Control spend for a time-boxed experiment using a **One-time** reset period with an expiration date.

Navigate to **Settings > Organization > Budgets** to manage budgets.

<Info>
  Only workspace admins can create, edit, or delete budgets.
</Info>

## Overview

The Budgets table lists all budgets in the organization.

<Frame caption="The Budgets overview showing all budgets with their scope, limits, reset period, and expiration status.">
  <img src="https://mintcdn.com/orqai/OvVmCO0w8BIsvnx_/images/list-budget-411.png?fit=max&auto=format&n=OvVmCO0w8BIsvnx_&q=85&s=6a4308fb2cdae499149864ff6c3e95d9" alt="Budgets list showing budget rows with Target, Scope, Limits, Reset, Expires, and Updated columns." width="1434" height="621" data-path="images/list-budget-411.png" />
</Frame>

| Column      | Description                                                                                                                                                                                              |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Target**  | The entity the budget applies to (e.g. the workspace, a project name, an API key name, an identity name, a provider, or a model name).                                                                   |
| **Scope**   | The type of entity: Workspace, Project, Identity, API Key, Provider, or Model.                                                                                                                           |
| **Limits**  | The configured cost and token limits for the reset period. Click a budget row to [see current usage](#monitor-usage).                                                                                    |
| **Reset**   | The configured reset period: Daily, Weekly, Monthly, Yearly, or One-time (no automatic reset).                                                                                                           |
| **Expires** | Time remaining until the budget stops enforcing. Shown in red when expiration is approaching. After expiration the budget remains in the list but no longer enforces. Blank if the budget never expires. |
| **Updated** | The date the budget was last modified.                                                                                                                                                                   |

Use <kbd><Icon icon="bars-filter" /> Filter</kbd> to narrow the list by **Period** (Daily, Monthly, One-time, Weekly, Yearly), **Scope**, or **Status** (Active, Expired).

Use <kbd><Icon icon="arrow-up-arrow-down" /> Sort by</kbd> to reorder by **Expiry soon**, **Newest**, or **Recently updated**.

### Monitor usage

Click a budget row to open its detail page:

* A metric card for each configured limit (**Total spend**, **Tokens**, **Req per minute**) shows current usage, a progress bar, and percentage consumed.
* **Scope**, **Resets** period, **Renews in** countdown, and **Expires** date appear at the top.

<Frame caption="Budget detail page for an Identity-scoped budget, showing Total spend, Tokens, and Req per minute metric cards with progress bars, alongside configured alerts.">
  <img src="https://mintcdn.com/orqai/a4UhZBs_ZNzlIH_h/images/budget-details.png?fit=max&auto=format&n=a4UhZBs_ZNzlIH_h&q=85&s=5ad30c11ae0f012df8193de449dff6d7" alt="Budget detail page for identity Jarmila showing 66% used of $0.20 total spend, 89% used of 25,000 tokens, 0% used of 5 requests per minute, and two alerts configured at 75% and 90% usage." width="1419" height="616" data-path="images/budget-details.png" />
</Frame>

* Click <kbd>Adjust limits</kbd> to edit the budget's period limits or expiration date.
* Click <kbd><Icon icon="plus" /> Add threshold</kbd> in the **Alerts** section to [set up an alert](#create-an-alert).
* Click <kbd>Delete Budget</kbd> to permanently remove it. Enforcement stops immediately.

## Create a budget

<Steps>
  <Step title="Open the creation dialog">
    Click <kbd><Icon icon="plus" /> Create</kbd> in the top-right corner of the Budgets page.

    <Frame caption="The Create budget dialog with scope, period limits, expiration date, and Alerts fields.">
      <img src="https://mintcdn.com/orqai/a4UhZBs_ZNzlIH_h/images/create-budget-411-2.png?fit=max&auto=format&n=a4UhZBs_ZNzlIH_h&q=85&s=e43d7e82085e24b5124d03e2197a39b3" alt="Create budget dialog showing Scope, Period limits, and Expiration fields, with an empty Alerts section reading No alerts configured and a disabled Add threshold button." width="822" height="1102" data-path="images/create-budget-411-2.png" />
    </Frame>
  </Step>

  <Step title="Set the scope">
    Under **Scope**, select what the budget **Applies to**:

    | Option        | Description                                                                                                                    |
    | ------------- | ------------------------------------------------------------------------------------------------------------------------------ |
    | **Workspace** | Applies to the entire workspace.                                                                                               |
    | **Project**   | Applies to a specific project.                                                                                                 |
    | **Identity**  | Applies to a contact by external ID.                                                                                           |
    | **API Key**   | Applies to a specific API key.                                                                                                 |
    | **Provider**  | Applies to all requests to a provider. Matches the provider segment of the model reference (`openai` in `openai/gpt-5.6-sol`). |
    | **Model**     | Applies to a specific model, matched by the full model reference as sent in the request (`openai/gpt-5.6-sol`).                |

    For all scope types except **Workspace**, a second field appears to select the specific target (for example, which API key or which identity).

    <Info>
      Only one budget is allowed per target. If a budget already exists for the selected target, an inline error is shown and the budget cannot be created until a different target is selected.
    </Info>

    See [Budget levels and interaction](#budget-levels-and-interaction) for how budgets at different levels apply to the same request.
  </Step>

  <Step title="Configure period limits">
    Under **Period limits**, set the **Reset period** (required) and one or more of the following limits:

    | Limit                   | Unit     | Description                                   |
    | ----------------------- | -------- | --------------------------------------------- |
    | **Cost**                | USD      | Maximum spend allowed per reset period.       |
    | **Tokens limit**        | K Tokens | Token cap per reset period, in thousands.     |
    | **Requests per minute** | Req/min  | Rate cap enforced every minute, at all times. |

    Leave a limit at `0` to leave it unconfigured. At least one limit must be set to a non-zero value to create the budget.

    Reset periods follow a fixed schedule:

    | Period   | Resets at                                                     |
    | -------- | ------------------------------------------------------------- |
    | Daily    | Midnight UTC                                                  |
    | Weekly   | Monday midnight UTC                                           |
    | Monthly  | 1st of the month, midnight UTC                                |
    | Yearly   | January 1st, midnight UTC                                     |
    | One-time | Does not reset. Limit applies for the lifetime of the budget. |

    <Info>
      **Requests per minute** is a rolling rate cap enforced continuously, independent of the reset period.
    </Info>

    <Tip>
      If a budget is exhausted before the period resets, an admin can increase its limit to unblock further usage immediately. Existing usage is not removed; the higher limit simply allows the entity to consume more within the current period.
    </Tip>
  </Step>

  <Step title="Set an expiration date (optional)">
    Under **Expiration**, set an **Expiration date** if the budget should stop enforcing after a specific date. Leave it as **Never expires** for an indefinite budget.
  </Step>

  <Step title="Create the budget">
    Click <kbd>Create budget</kbd>. The budget appears in the list immediately and begins enforcing on the next request.
  </Step>
</Steps>

## Edit or delete a budget

Select the <kbd><Icon icon="ellipsis" /></kbd> menu on any budget row to:

* **Edit**: update the period limits or expiration date. The scope and target cannot be changed after creation.
* **Delete**: permanently removes the budget. Enforcement stops immediately.

Budgets can also be created and updated programmatically through the API using a **Management Key**. Regular API keys cannot manage Budgets.

## Configure alerts

**Alerts** notify configured notifiers when a budget's cost usage crosses a percentage threshold.

The gateway performs two separate checks. Before a request starts, it verifies the budget hasn't been fully consumed. Requests are blocked once usage reaches 100%.

Alert thresholds are evaluated after each request completes, which means a notification can lag slightly behind the moment the threshold was crossed.

**Example:** An 80% cost threshold on a project's budget, notifying the project administrator by email, gives time to react before the budget blocks traffic at 100%.

<Info>
  Alerts track cost usage only; token and requests-per-minute limits do not trigger them. A cost limit is required before adding an alert; <kbd>Add threshold</kbd> stays disabled until one is set. For alerts on latency, errors, or guardrail results, see the AI Studio [**Alerts**](/ai-studio/observability/alerts) page.
</Info>

### Create a notifier

Notifiers must exist before they can be configured in an alert. Create one under **Settings > Organization > Notifiers**, or with the [**Notifiers API**](/reference/notifiers/create-a-notifier).

<Frame caption="Notifiers configured in the organization, available for selection in a budget alert.">
  <img src="https://mintcdn.com/orqai/a4UhZBs_ZNzlIH_h/images/notifier-list.png?fit=max&auto=format&n=a4UhZBs_ZNzlIH_h&q=85&s=a7b6a15a7c4f9a6f29982abc0b14cae2" alt="Notifiers table showing a Webhook and an Email notifier, each with a Name, Destination, Type, and Updated date." width="1436" height="444" data-path="images/notifier-list.png" />
</Frame>

<Steps>
  <Step title="Open the notifier form">
    Click <kbd><Icon icon="plus" /> Notifier</kbd>.
  </Step>

  <Step title="Name the notifier">
    Enter a **Name** to identify it in the notifier list.
  </Step>

  <Step title="Choose a channel">
    Select **Send via** and configure the channel:

    | Channel     | Fields                                              |
    | ----------- | --------------------------------------------------- |
    | **Email**   | One or more recipient email addresses.              |
    | **Webhook** | A webhook URL, and optional custom request headers. |
    | **Slack**   | A Slack incoming webhook URL.                       |

    Sending to a **Microsoft Teams** channel? See the [Microsoft Teams example](#send-notifications-to-a-microsoft-teams-channel).
  </Step>

  <Step title="Save the notifier">
    Click <kbd>Create</kbd> (or <kbd>Save</kbd> when editing an existing notifier).
  </Step>
</Steps>

### Create an alert

The **Alerts** section is on the budget's create/edit panel, and on its detail page (open a budget by clicking its row in the Budgets list).

<Steps>
  <Step title="Add a threshold">
    In the **Alerts** section, click <kbd><Icon icon="plus" /> Add threshold</kbd>.
  </Step>

  <Step title="Set the threshold percentage">
    Enter a whole number between 1 and 100. Each budget allows only one alert per threshold percentage; duplicate thresholds are rejected.
  </Step>

  <Step title="Add notifiers">
    Click <kbd>Add notifiers</kbd> and search for one or more already-created notifiers to receive the alert. At least one notifier is required. Each alert allows a maximum of 10 notifiers.
  </Step>

  <Step title="Save the alert">
    Click <kbd>Add alert</kbd> (or <kbd>Save changes</kbd> when editing an existing alert).

    <Frame caption="A configured alert with a 90% threshold and a Webhook notifier.">
      <img src="https://mintcdn.com/orqai/a4UhZBs_ZNzlIH_h/images/create-alert-411.png?fit=max&auto=format&n=a4UhZBs_ZNzlIH_h&q=85&s=aa11d16091b70b4883929a3a9aa03827" alt="Create alert dialog showing a 90% threshold, a Webhook notifier chip, and Add notifiers, Cancel, and Add alert buttons." width="651" height="372" data-path="images/create-alert-411.png" />
    </Frame>
  </Step>
</Steps>

Existing alerts appear as rows in the **Alerts** section. Select <kbd><Icon icon="pencil" /></kbd> to edit a threshold or its notifiers, or <kbd><Icon icon="trash" /></kbd> to delete the alert.

## Examples

### Send notifications to a Microsoft Teams channel

Route alert notifications to a **Microsoft Teams** channel with a generic webhook notifier. Teams renders messages in its own card format, so the webhook points at a small relay that converts **Orq.ai**'s notification payload before forwarding it to the channel.

<Steps>
  <Step title="Create the Teams incoming webhook">
    In the target **Microsoft Teams** channel, open **Workflows** (or **Power Automate**), search for the **Post to a channel when a webhook request is received** template, complete the setup, and copy the webhook URL it generates.
  </Step>

  <Step title="Deploy a payload relay">
    **Orq.ai** sends the raw notification envelope as JSON, and Teams only accepts its own message card format, so the webhook cannot point at the Teams URL directly. Deploy a small relay that converts the payload:

    <Accordion title="Relay code" icon="code">
      ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
      export default {
        async fetch(request, env) {
          if (request.method !== "POST") {
            return new Response("Method not allowed", { status: 405 });
          }

          let envelope;
          try {
            envelope = await request.json();
          } catch {
            return new Response("Invalid JSON body", { status: 400 });
          }
          const links = envelope.links ?? [];

          const card = {
            "@type": "MessageCard",
            "@context": "http://schema.org/extensions",
            themeColor: severityColor(envelope.severity),
            title: envelope.title,
            text: envelope.body,
            sections: [
              {
                facts: [
                  { name: "Severity", value: envelope.severity },
                  ...links.map((link) => ({ name: link.text, value: link.url })),
                ],
              },
            ],
          };

          const teamsResponse = await fetch(env.TEAMS_WEBHOOK_URL, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(card),
          });

          if (!teamsResponse.ok) {
            // Relay the status so Orq.ai retries the delivery.
            console.log(`Teams rejected the message: ${teamsResponse.status}`);
            return new Response(`Teams rejected the message: ${teamsResponse.status}`, {
              status: teamsResponse.status,
            });
          }

          console.log(`Forwarded notification ${envelope.id} to Teams (${teamsResponse.status})`);
          return new Response("OK", { status: 200 });
        },
      };

      function severityColor(severity) {
        switch (severity) {
          case "critical":
            return "B71C1C";
          case "warning":
            return "F57C00";
          case "ok":
            return "2E7D32";
          default:
            return "1565C0";
        }
      }
      ```
    </Accordion>

    Set `TEAMS_WEBHOOK_URL` to the webhook URL from the previous step. The example deploys as a Cloudflare Worker; any HTTP endpoint that accepts JSON works.
  </Step>

  <Step title="Create the webhook notifier">
    Under **Settings > Organization > Notifiers**, click <kbd><Icon icon="plus" /> Notifier</kbd>, enter a **Name**, select **Webhook** as the **Send via** channel, and paste the relay URL as the **Webhook URL**. Custom request headers are optional.

    To create the notifier through the API:

    <CodeGroup>
      ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
      curl --location 'https://my.orq.ai/v2/notifiers' \
      --header "Authorization: Bearer $ORQ_API_KEY" \
      --header 'Content-Type: application/json' \
      --data-raw '{
          "display_name": "Teams alerts",
          "type": "NOTIFIER_TYPE_WEBHOOK",
          "webhook_url": "https://teams-relay.example.workers.dev"
      }'
      ```

      ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
      import { Orq } from "@orq-ai/node";

      const orq = new Orq({ apiKey: process.env.ORQ_API_KEY ?? "" });

      const notifier = await orq.notifiers.create({
        displayName: "Teams alerts",
        type: "NOTIFIER_TYPE_WEBHOOK",
        webhookUrl: "https://teams-relay.example.workers.dev",
      });
      ```

      ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
      from orq_ai_sdk import Orq
      import os

      orq = Orq(api_key=os.getenv("ORQ_API_KEY"))

      notifier = orq.notifiers.create(
          request={
              "display_name": "Teams alerts",
              "type": "NOTIFIER_TYPE_WEBHOOK",
              "webhook_url": "https://teams-relay.example.workers.dev",
          }
      )
      ```
    </CodeGroup>

    See the [API reference](/reference/notifiers/create-a-notifier) for the full parameter specification.
  </Step>

  <Step title="Attach the notifier to an alert">
    Add the notifier to a [budget alert](#create-an-alert) under **Add notifiers**, or to an **Observability** [Alert](/ai-studio/observability/alerts) under **Notify via**. Each alert accepts up to 10 notifiers.
  </Step>

  <Step title="Test delivery">
    Create a budget with a small cost limit and an alert at a low threshold, then make a request that crosses it. The alert fires on the next check and the message lands in the channel. If nothing arrives, check the relay logs first: they record every converted payload, including any rejection from Teams.
  </Step>
</Steps>

**Payload format**: **Orq.ai** POSTs the notification envelope as `application/json` with `X-Orq-Hook-ID`, `X-Orq-Event`, and `Idempotency-Key` headers, plus any headers configured on the notifier:

```json JSON theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "ntf_01JQ8Z2K4M6P8R0T2V4X6Z8B0D",
  "version": 1,
  "workspace_id": "ws_01JQ8Z0MB5W1YTVN3GCK7B0EXA",
  "timestamp": "2026-08-08T09:30:00Z",
  "title": "Budget usage has reached your notification threshold",
  "body": "Your usage today for workspace acme has reached $92.00 (92%) of your $100.00 budget. This alert is configured at 90% ($90.00).",
  "severity": "warning",
  "links": [
    { "text": "Manage budgets", "url": "https://my.orq.ai/budgets" }
  ],
  "tags": {
    "budget_id": "bgt_01JQ8Z0MB5W1YTVN3GCK7B0EXA",
    "scope_kind": "WORKSPACE",
    "period": "DAILY",
    "threshold_percent": "90"
  }
}
```

Teams rejects this envelope with HTTP `400` because it expects a message card with `@type` and `@context`. The relay converts the envelope into that format before forwarding.

**Troubleshooting**:

| Symptom                        | Cause                                                                                                                               |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| Teams responds with `400`      | The relay is missing or misconfigured. Confirm the relay actually maps the envelope to a MessageCard before checking anything else. |
| Nothing arrives in the channel | The webhook URL is stale or revoked, or the relay crashed. Check the relay logs for the delivered and rejected requests.            |
| Notifications arrive late      | Non-`2xx` responses are retried, so a failing relay delays delivery until it recovers.                                              |

## Budget levels and interaction

Budgets can be set at six levels: **Workspace**, **Project**, **Identity**, **API Key**, **Provider**, and **Model**.

When several budgets apply to the same request, every one of them is enforced independently and cumulatively. There is no precedence: a budget at one level never overrides a budget at another level, and a request must satisfy every budget that matches it.

Because only one budget is allowed per target (see [Create a budget](#create-a-budget)), a request can match at most one scoped budget per level, up to six in total. When a budget is exhausted, only requests that match it are rejected.

### How multiple budgets interact

* **Every applicable budget is enforced.** A request must pass every budget that matches it. No budget overrides another.
* **Each budget tracks its own usage.** A request counts toward every budget that applies to it, at the same time.
* **The most restrictive budget binds.** The effective cap for a dimension is the most restrictive applicable budget. Limits on different dimensions (cost, tokens, requests per minute) all apply simultaneously.
* **Requests per minute are enforced per budget**, each with its own rolling 60-second window.
* **Expired budgets stop enforcing.** After the expiration date a budget no longer blocks requests, even though it stays in the list.

### Worked examples

<AccordionGroup>
  <Accordion title="Workspace budget plus a per-project budget" icon="coins">
    The workspace has a \$1,000/month budget. The **Production** project has a \$300/month budget.

    * Requests to Production count toward both budgets; requests to other projects count only toward the workspace budget.
    * When Production reaches \$300, requests to Production are rejected until the monthly reset. Other projects keep working until the workspace budget is exhausted at \$1,000.
  </Accordion>

  <Accordion title="Per-identity budget inside a project with its own budget" icon="users">
    The **Support** project has a \$500/month budget. The identity `customer-acme` has a \$50/month budget.

    * The identity budget applies across all projects: the \$50/month cap is enforced whenever `customer-acme` makes a request, regardless of project.
    * Requests from `customer-acme` count toward both budgets. When the identity budget is exhausted, those requests are rejected even though the project budget has room; other identities keep working.
  </Accordion>

  <Accordion title="Per-model budget alongside a workspace-wide cap" icon="brain">
    The workspace has a \$2,000/month budget. The model `anthropic/claude-sonnet-5` has an \$800/month budget.

    * When the model budget is exhausted, requests to that model are rejected; other models keep working.
    * When total workspace spend reaches \$2,000, all requests are rejected until the monthly reset, regardless of model.
  </Accordion>
</AccordionGroup>

### When a limit is exceeded

A request that matches an exhausted budget is rejected with `429 Too Many Requests`. The response identifies the budget and the exceeded dimension (`cost`, `tokens`, or `requests`); the response headers report the limit, remaining usage, and seconds until reset. Only requests that match the exhausted budget are blocked.

| Dimension               | Behavior when exceeded                                                                                                                                                                         |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Cost** and **Tokens** | Counters update as requests complete. When a budget reaches 100%, the next matching request is rejected. Requests stay blocked until the budget's period resets, or an admin raises the limit. |
| **Requests per minute** | Requests are rejected for the rolling 60-second window only, independent of the reset period.                                                                                                  |

For budgets with a **One-time** reset period, there is no automatic reset: matching requests stay blocked until an admin raises the limit, deletes the budget, or its expiration date passes.

### Reset periods

Each budget has its own reset period and its own counters. Budgets at different levels can use different periods: **Daily**, **Weekly**, **Monthly**, **Yearly**, or **One-time** (no reset). See [Create a budget](#create-a-budget) for the reset schedule.

## See also

<CardGroup cols={2}>
  <Card title="Rate limits & quotas" icon="gauge" href="/ai-gateway/features/rate-limits">
    What the **AI Gateway** enforces itself versus upstream providers, and the 429 response contract.
  </Card>

  <Card title="API Keys" icon="key" href="/ai-gateway/configuration/api-keys">
    Create and manage AI Gateway API keys with optional spending limits.
  </Card>

  <Card title="Credits" icon="coins" href="/ai-studio/organization/credits">
    Manage the workspace credit balance, payment methods, and auto top-up.
  </Card>
</CardGroup>
