---
title: "Create Policy"
method: POST
path: "/admin/organizations/{organization_id}/usage-policies"
tags: ["usage-policies-admin"]
---

# Create Policy

`POST /admin/organizations/{organization_id}/usage-policies`

Put a policy in force for one organization, charging it for the activity before it.

Platform admin only. A policy is immutable, so this only ever activates a new
one: when a policy already governs the same template and scope, the response is
a conflict naming it, and changing that budget means retiring it first.

The new policy is charged for the organization's prior activity as part of the same
act, so it can be created already partly — or wholly — spent, and the response says
by how much. A charge that cannot be computed refuses the whole call and creates
nothing, because a policy blind to its history under-reports and under-enforces with
nothing on the surface to show it.

## Path parameters

- `organization_id` string, uuid, required

## Request body

- CreateUsagePolicyRequest — Body of the policy create; the path addresses the organization. Rejects unknown fields, which most schemas here do not. ``budget`` is the one optional field and its default is unlimited, so a misspelled key would otherwise validate and create a policy imposing no limit at all — on the surface that exists to impose one. Forbidding extras rather than requiring ``budget`` also covers any optional field added later.
  - `policy_template_id` string, uuid, required — The template whose limit behaviour this policy applies.
  - `scope` 'user' | 'organization' | 'automation', required — Whose consumption a policy counts. ``AUTOMATION`` counts the whole organization exactly as ``ORGANIZATION`` does and differs only in which activity it accepts: human and automated work draw on separate budgets, so each of those two scopes counts one of them and refuses the other.
  - `budget` integer, nullable — Credits the scope may consume per window. Null imposes no limit, which is distinct from 0 forbidding all usage.

## Response `201`

Successful Response

- CreateUsagePolicyResponse — The policy now in force, and what charging its history to it did. Two objects rather than one flattened shape: the counts describe the act, and the same policy read back later carries no trace of them. An admin needs both, because a budget can be spent before its first live start — the history is charged as it is created.
  - `policy` UsagePolicyAdminResponse, required — A policy as stored, in force while ``retired_at`` is null. Nulls are serialized rather than dropped: an absent ``retired_at`` would be indistinguishable from a retired policy whose key the reader simply did not find.
    - `id` string, uuid, required
    - `policy_template_id` string, uuid, required
    - `organization_id` string, uuid, required
    - `scope` 'user' | 'organization' | 'automation', required — Whose consumption a policy counts. ``AUTOMATION`` counts the whole organization exactly as ``ORGANIZATION`` does and differs only in which activity it accepts: human and automated work draw on separate budgets, so each of those two scopes counts one of them and refuses the other.
    - `budget` integer, nullable — Credits the scope may consume per window. Null imposes no limit, which is distinct from 0 forbidding all usage.
    - `created_at` string, date-time, required
    - `retired_at` string, date-time, nullable — When the policy left force; null while it is in force.
  - `backfill` UsagePolicyBackfillResponse, required — What a backfill run examined and what it changed. The counts are what makes a repeat legible. A second run over an unchanged log reports the same events examined and counted, ``attributions_written`` of zero, and the same counters rebuilt to the same totals — so an admin can tell "already done" from "nothing to do", which the policy row itself cannot say. ``refusals`` is listed rather than counted: each entry names an event whose activity may belong in the budget this policy now binds against, and finding out needs the event.
    - `policy_id` string, uuid, required
    - `events_examined` integer, required — Events of the policy's resource kind the run read, from the start of the window the policy was created in onwards.
    - `events_counted` integer, required — Of those, the events this policy counts, by its selector and its scope.
    - `attributions_written` integer, required — Ledger rows this run added; zero when every charge was already recorded.
    - `counters_rebuilt` integer, required — Window counters set to the sum of their window's attributions.
    - `refusals` UsagePolicyBackfillRefusalResponse[], required — Events the run declined to judge, each with its reason.
      - `usage_event_id` string, uuid, required — The event left unjudged.
      - `reason` 'actor_identity_unrecorded', required — Why a backfill would not judge one event.

## Other responses

- `422` — Validation Error

## Changes

> 57 revisions in range; 9 not diffed.

- **2026-09-12** `d5665d7046eb` — 5 breaking, 2 warning, 2 info
  - removed the required property `created_at` from the response with the `201` status
  - removed the required property `id` from the response with the `201` status
  - removed the required property `organization_id` from the response with the `201` status
  - removed the required property `policy_template_id` from the response with the `201` status
  - …5 more
- **2026-09-11** `c5e1ea955e81` — 1 warning, 1 info
  - added the new `automation` enum value to the `scope` response property for the response status `201`
  - added the new `automation` enum value to the request property `scope`
- **2026-09-10** `47dcc19b4add` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/traversal/apis/fastapi/changes/admin/organizations/:organization_id/usage-policies/post.md)

---

[API](https://skmtc.dev/traversal/apis/fastapi.md) · [All operations](https://skmtc.dev/traversal/apis/fastapi/llms.txt) · [OpenAPI document](https://skmtc.dev/traversal/apis/fastapi/revisions/c3bd9dec6c16?raw)
