Skip to main content
The management REST API is the same surface the langwatch CLI and LangWatch dashboard use, exposed for scripts, CI pipelines, SDKs, and terraform providers. It runs on the LangWatch control plane (https://app.langwatch.ai), separately from the data-plane gateway (https://gateway.langwatch.ai). Authentication uses existing LangWatch API tokens (Authorization: Bearer lwp_... or X-Auth-Token: lwp_...). No new token format.
The CLI (langwatch virtual-keys ...) is built on this API. If you’re scripting from Node or Python, consider using the CLI rather than calling the REST directly, it handles pagination, JSON formatting, and error messages for you.

Base

Self-hosted: replace the base with your control plane’s DNS.

Virtual keys

List

Returns every key visible to your credential: keys scoped to your project, its team, or the whole organization. Response:
purpose: "langy" marks a product-managed key (auto-provisioned by LangWatch). Those rows appear in listings but refuse every mutation; filter on purpose when scripting bulk operations.

Create

Body (only name is required):
  • scopes defaults to the calling project. Team- and org-scoped keys need a scoped API key holding virtualKeys:manage at each requested scope; legacy project keys can only mint keys for their own project.
  • An org- or team-scoped key also needs a place for its traces and spend to land: pass trace_project_id (requires virtualKeys:manage on that project; it is a destination, not a scope, and grants no access to the key), or the organization’s governance project is used, and creation refuses with trace_project_required when neither exists.
  • budget creates a budget atomically with the key and manages exactly that row on later updates: the key can never exist without the cap you asked for. Windows: DAY, WEEK, MONTH.
  • routing_mode is one of NONE (default: no silent failover), FALLBACK_ALL, or POLICY (requires routing_policy_id).
Response 201:
The secret field is returned only this once. Persist it immediately.

Get

Response: { "virtual_key": {...} }. No secret.

Spend

Requires gatewayUsage:view. Both window params are optional; the default window is the current UTC calendar month. Reads the same cost path (trace_summaries) the dashboard reads, so this number and the UI agree by construction. Response:
On deployments without a ClickHouse spend source the endpoint answers 412 with error.code = "spend_source_unavailable" rather than a $0.00 that cannot be told apart from a zero-spend key.

Update

Body (all fields optional):
scopes replaces the whole visibility set and requires virtualKeys:manage at every NEW scope. budget: null archives the key’s own cap (spend history is retained); omitting budget leaves it alone.

Rotate

Response 200:
The previous secret keeps authenticating for a 24 h grace window so in-flight clients can roll over.

Revoke

Idempotent: revoking an already-revoked VK returns 200 with the same status. Revocation also archives the key’s own budgets.

Budgets

List

Returns every non-archived budget in your organization across all six scope types (ORGANIZATION, TEAM, PROJECT, VIRTUAL_KEY, PRINCIPAL, GROUP), with live spent_usd from the spend ledger. scope_type is an optional comma-separated filter. Response:
  • spend_available: false means spend could not be totalled server-side; do not read spent_usd as real spend in that case.
  • GROUP rows are per-member allowances: limit_usd is what EACH member may spend, spent_usd is the group’s summed spend, and member_count says how many members the allowance currently covers.
  • provider_key names the ModelProvider the budget is pinned to; null counts every provider.

Create

Body:
GROUP budgets track spend per member, which requires the ClickHouse spend ledger; deployments without it answer 400 with error.code = "group_budget_requires_clickhouse".

Update

Updatable fields: name, description, limit_usd, on_breach, timezone. Scope and window are immutable after create.

Archive

Soft archive: preserves ledger history, stops enforcement on new requests. Returns the archived row with archived_at set.

Provider bindings

The /providers routes are gone (410). Gateway provider bindings were folded into the platform-wide ModelProvider in iteration 110: configure credentials, rate limits, and fallback priority via /api/gateway-platform/v1/model-providers or the Advanced (Gateway) tab in the dashboard. Budgets reference providers by ModelProvider id (provider_key).

Cache rules

Organization-scoped overrides that modulate cache behaviour for requests routed through the gateway. Evaluated first-match-wins by priority DESC; a matched rule wins over the per-VK default but loses to a per-request X-LangWatch-Cache header. Full contract in Cache control.

List

Requires gatewayCacheRules:view. Returns rules sorted priority DESC, excluding archived.
mode_enum is echoed in upper case alongside the lower-case action.mode so Prometheus, dashboards can filter by it without parsing the JSON action.

Get

Requires gatewayCacheRules:view. Returns 404 for archived rules, use the audit log to inspect removed rules.

Create

Requires gatewayCacheRules:create. At least one matcher is required (rules that match every request must be declared explicitly, unsupported in v1). Matchers across non-null fields are ANDed.
Matcher fields (all optional, ANDed): Action fields: Response 201: { "cache_rule": { ...full row } }.

Update

Requires gatewayCacheRules:update. Partial update; matchers and action replace the stored value when provided (not merged field-by-field). Omitting them leaves the stored value untouched. Name, description, priority, enabled update independently.

Archive

Requires gatewayCacheRules:delete. Soft archive: sets archivedAt. The rule stops matching new requests. Returns the archived row (200, not 204) so scripts can confirm the archivedAt timestamp.

Errors

All responses follow the OpenAI-compatible envelope:
error.code carries the machine code your integration can branch on (validation_error, trace_project_required, routing_policy_required, scope_org_mismatch, group_budget_requires_clickhouse, spend_source_unavailable, …). See API: Errors for the full type enum. Common management-side types:

Audit

Every write emits a row in the platform-wide AuditLog (gateway shape, targetKind, targetId, before, after). Visible under /settings/audit-log with the Source = “Gateway” badge; filter by Target (virtual_key, budget, cache_rule) to scope. Writes via scoped API keys are attributed to the key’s owning user; legacy project keys carry no user, so their writes record the machine principal svc_<projectId>. See Audit log for the full schema, REST export path, and migration note from the v3.0 gateway-only table.

Rate limits

Management endpoints are rate-limited to 100 req/min/token. For bulk operations, use the --format json CLI with xargs -P4 (the CLI sleeps 250 ms between retries on 429).

Shared service layer

Both the REST API and the tRPC routers (virtualKeys.*, gatewayBudgets.*, gatewayUsage.*) call the same service classes and pre-flight asserts on the server: VirtualKeyService, GatewayBudgetService, GatewayUsageService, and the scope-authorization helpers in virtualKey.authz. The only difference between REST and tRPC is the DTO shape (snake_case vs camelCase) and how the caller is identified (API credential vs browser session). Behaviour is identical, and the integration suite in langwatch/src/app/api/gateway-platform/__tests__/ pins the refusals (trace_project_required, per-scope permission_denied, group_budget_requires_clickhouse, …) so the two surfaces cannot drift apart again.

OpenAPI

All REST routes are annotated with hono-openapi’s describeRoute schemas, which generate into the repository-wide OpenAPI 3.1 document (docs/api-reference/openapiLangWatch.json) rendered by the API-reference docs. The CLI’s VirtualKeysApiService and GatewayBudgetsApiService DTOs are hand-authored against the same schemas; a future improvement is to regenerate them from the OpenAPI output so drift fails at build time.

See also

  • langwatch CLI: higher-level access.
  • RBAC: which scopes your token needs.
  • Security: how your API tokens and resulting writes are protected.