---
title: "Create a spend rule"
method: POST
path: "/spend-rules"
tags: ["Gateway"]
---

# Create a spend rule

`POST /spend-rules`

## Request

- Base URL: `https://api.phaseo.app/v1`
- URL: `https://api.phaseo.app/v1/spend-rules`
- Auth: HTTP bearer

Creates a spend rule and sets its notification destinations. Every metric is evaluated. Blocking is best effort and acts minutes after the threshold; use key limits and workspace budgets for exact caps. A workspace can have 50 spend rules.

## Request body

- SpendRuleInput: Spend rule fields to change; at least one. Give amounts as *_usd or as *_nanos (exact nanodollars), not both. Set a field to null to clear it.
  - `name` string, required
  - `enabled` boolean
  - `metric` 'spend' | 'budget_percent' | 'forecast' | 'spike' | 'balance', required: spend: spend reached threshold_amount. budget_percent: spend reached threshold_percents of the base. forecast: spend projected to the period end reaches the threshold. spike: spend over the last window_hours is at least spike_multiplier times the median of the same window over the previous 7 days, and at least spike_floor. balance: the workspace balance or runway falls below the threshold.
  - `scope_type` 'workspace' | 'key' | 'member' | 'department' | 'model' | 'provider'
  - `scope_id` string, nullable: Null for the workspace scope; otherwise a key id, a member's user id, a department id, a model id or a provider id.
  - `period` 'day' | 'week' | 'month' | 'rolling', required: day, week (from Monday) or month in the rule's time zone, or rolling over the last window_hours.
  - `window_hours` integer, nullable: Hours in a rolling window: the spike window for spike rules (at most 56, so that 3 windows fit in the previous 7 days) and the burn-rate lookback for balance rules. Null for calendar periods.
  - `time_zone` string, nullable: IANA time zone for calendar periods. Null uses the workspace's billing time zone.
  - `base_kind` 'amount' | 'workspace_budget' | 'key_limit' | 'previous_period', nullable: What threshold_percents are percentages of: amount (base_amount), workspace_budget or key_limit (the base_ref interval, which must match the period), or previous_period.
  - `base_ref` 'daily' | 'weekly' | 'monthly', nullable
  - `base_amount_usd` number, nullable
  - `base_amount_nanos` integer, nullable
  - `threshold_percents` integer[], nullable: Up to five whole percentages of the base, sorted. Each fires at most once per period; crossing several at once sends one alert naming the highest.
  - `threshold_amount_usd` number, nullable
  - `threshold_amount_nanos` integer, nullable
  - `runway_days` integer, nullable
  - `spike_multiplier` number, nullable
  - `spike_floor_usd` number, nullable
  - `spike_floor_nanos` integer, nullable
  - `action` 'notify' | 'block': notify, or notify and then block once spend reaches block_at_percent of the base or block_at_amount. Blocking is best effort and acts minutes after the threshold. While blocking is switched off for the gateway, a rule that would start blocking is refused with a 400, and a rule that already blocks only notifies.
  - `block_at_percent` integer
  - `block_at_amount_usd` number, nullable
  - `block_at_amount_nanos` integer, nullable
  - `block_free_models` boolean: Whether a block also stops requests to free models.
  - `cooldown_minutes` integer: Minimum minutes between alerts for rolling, spike and balance rules.
  - `destination_ids` string[]: Notification destinations for this rule's alerts. Empty means the workspace's Spend Alerts route.

## Response `201`

Spend rule created

- SpendRuleResponse
  - `data` SpendRule, required: A spend rule: thresholds in dollars or percent over a scope and a period that notify, or notify and then block. Amounts are returned in nanodollars and in USD.
    - `id` string, uuid, required
    - `workspace_id` string, uuid, required
    - `name` string, required
    - `enabled` boolean, required
    - `metric` 'spend' | 'budget_percent' | 'forecast' | 'spike' | 'balance', required: spend: spend reached threshold_amount. budget_percent: spend reached threshold_percents of the base. forecast: spend projected to the period end reaches the threshold. spike: spend over the last window_hours is at least spike_multiplier times the median of the same window over the previous 7 days, and at least spike_floor. balance: the workspace balance or runway falls below the threshold.
    - `evaluated` boolean, required: Whether the evaluator handles the rule's metric, period and base. Every rule the API accepts is evaluated.
    - `scope_type` 'workspace' | 'key' | 'member' | 'department' | 'model' | 'provider', required
    - `scope_id` string, nullable, required: Null for the workspace scope; otherwise a key id, a member's user id, a department id, a model id or a provider id.
    - `period` 'day' | 'week' | 'month' | 'rolling', required: day, week (from Monday) or month in the rule's time zone, or rolling over the last window_hours.
    - `window_hours` integer, nullable, required: Hours in a rolling window: the spike window for spike rules (at most 56, so that 3 windows fit in the previous 7 days) and the burn-rate lookback for balance rules. Null for calendar periods.
    - `time_zone` string, nullable, required: IANA time zone for calendar periods. Null uses the workspace's billing time zone.
    - `base_kind` 'amount' | 'workspace_budget' | 'key_limit' | 'previous_period', nullable, required: What threshold_percents are percentages of: amount (base_amount), workspace_budget or key_limit (the base_ref interval, which must match the period), or previous_period.
    - `base_ref` 'daily' | 'weekly' | 'monthly', nullable, required
    - `base_amount_nanos` integer, nullable, required
    - `base_amount_usd` number, nullable, required
    - `threshold_percents` integer[], nullable, required: Up to five whole percentages of the base, sorted. Each fires at most once per period; crossing several at once sends one alert naming the highest.
    - `threshold_amount_nanos` integer, nullable, required
    - `threshold_amount_usd` number, nullable, required
    - `runway_days` integer, nullable, required
    - `spike_multiplier` number, nullable, required
    - `spike_floor_nanos` integer, nullable, required
    - `spike_floor_usd` number, nullable, required
    - `action` 'notify' | 'block', required: notify, or notify and then block once spend reaches block_at_percent of the base or block_at_amount. Blocking is best effort and acts minutes after the threshold. While blocking is switched off for the gateway, a rule that would start blocking is refused with a 400, and a rule that already blocks only notifies.
    - `block_at_percent` integer, required
    - `block_at_amount_nanos` integer, nullable, required
    - `block_at_amount_usd` number, nullable, required
    - `block_free_models` boolean, required: Whether a block also stops requests to free models.
    - `cooldown_minutes` integer, required: Minimum minutes between alerts for rolling, spike and balance rules.
    - `destination_ids` string[], required: Notification destinations for this rule's alerts. Empty means the workspace's Spend Alerts route.
    - `created_by` string, uuid, nullable, required
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required

## Other responses

- `default`: Request failed

## Changes

- **2026-10-27** `58847d40a023`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/phaseo/apis/phaseo-gateway-api/changes/spend-rules/post.md)

---

[API](https://skmtc.dev/phaseo/apis/phaseo-gateway-api.md) - [All operations](https://skmtc.dev/phaseo/apis/phaseo-gateway-api/llms.txt) - [OpenAPI document](https://skmtc.dev/phaseo/apis/phaseo-gateway-api/revisions/5ca9bb220d25?raw)
