Customer management API
Read decisions and policy revisions, validate policy changes, and automate workspace operations.
On this page
Authenticate and select a workspace
The base URL is https://api.kastra.ai/api/v1. Customer management uses a user JWT or an authorized operator/device credential, with current workspace membership and route-specific permissions. For automation, create a scoped operator token through the CLI and store it as KASTRA_TOKEN. An environment runtime API key does not grant policy-authoring access.
Send Authorization: Bearer ... and X-Organization-ID: WORKSPACE_UUID. Use an environment UUID in environment_id or X-Environment-ID on routes supporting environment filters; policy authoring also accepts an environment name through X-Environment. Always set the intended environment on writes. Workspace and environment display names are not interchangeable with their UUIDs.
curl --fail-with-body --get 'https://api.kastra.ai/api/v1/environments' \
-H "Authorization: Bearer $KASTRA_TOKEN" \
-H "X-Organization-ID: $KASTRA_WORKSPACE_ID"
The response has success: true and a data array. Select the environment you intend to inspect and retain its id; use its name where a policy predicate or MCP argument requires a name.
Decision history
GET /api/v1/decisions returns data: [] for an empty result and a pagination object. GET /api/v1/decisions/{decisionID} reads one authorized decision.
curl --fail-with-body --get 'https://api.kastra.ai/api/v1/decisions' \
-H "Authorization: Bearer $KASTRA_TOKEN" \
-H "X-Organization-ID: $KASTRA_WORKSPACE_ID" \
--data-urlencode "environment_id=$KASTRA_ENVIRONMENT_ID" \
--data-urlencode 'decision=DENY' \
--data-urlencode 'source=claude_code' \
--data-urlencode 'limit=20'
| Query | Meaning |
|---|---|
environment_id | Authorized environment UUID; omitted scope includes all authorized environments |
decision | ALLOW, DENY, or PENDING_REVIEW |
source | Recorded origin, such as claude_code, codex_cli, mcp, or proxy |
from, to | RFC3339 time bounds |
model, provider, workload_type, jurisdiction | Request context filters |
email | Actor email filter; MCP calls this argument actor_email |
search | Text across reason, model, workload type, and actor email |
attr.x-kastra-attr-tool | Example per-attribute filter; supply the attribute’s value |
shadow_mode | true or false |
shadow_deny, degraded, content_scan | Strict boolean filters for shadow denials, billing degradation, or scan findings |
effect | Metered effect: rate_limit or spend_cap |
limit, cursor | Page size and opaque continuation token |
Pass pagination.next_cursor unchanged to the next request and retain the original filters. Stop when it is empty. Treat total_count as a lower bound when total_count_capped is true. Do not interpret pending history as authorization; use the checkpoint’s effective decision.
GET /api/v1/decisions/export accepts the shared context filters, format=json or format=csv, and max_results up to 10,000. Export requires the corresponding entitlement; list-only search and cursor are not export pagination. Narrow large time windows rather than assuming an export contains every matching record. Evidence exports.
Policies
Policy reads require workspace membership. Policy writes require ADMIN or OWNER and applicable plan access. Paths below are relative to /api/v1.
| Method and path | Input / result |
|---|---|
GET /policies | Optional environment scope, limit (default 20, maximum 100), offset; data array and meta.total, meta.limit, meta.offset |
GET /policies/active | Explicit environment scope; reads its active policy |
GET /policies/{policyID} | Reads a policy by UUID |
GET /policies/{policyID}/revisions | Reads revision history |
GET /policies/{policyID}/revisions/{revisionNumber} | Reads one revision |
GET /policies/{policyID}/revisions/{rev1}/diff/{rev2} | Compares two revisions |
GET /policies/{policyID}/export | Exports the policy |
POST /policies/validate | Validates a candidate body; does not activate it |
POST /policies | Creates a policy from name, description, deny, required_attributes, default_decision |
PUT /policies/{policyID} | Updates editable fields and optional change_note; inspect the resulting revision |
POST /policies/{policyID}/activate | Body: {"revision_number":1}; use the actual reviewed revision |
POST /policies/{policyID}/deactivate | Deactivates the policy |
POST /policies/{policyID}/revisions/{revisionNumber}/rollback | Rolls back to a selected revision |
POST /policies/{policyID}/promote | target_environment (name), activate_immediately (boolean), optional change_note |
DELETE /policies/{policyID} | Deletes the selected policy |
The API receives the policy body directly, without the .kastra file’s metadata / spec wrapper. For example, save this as candidate.json:
{
"name": "docs-validation-example",
"default_decision": "allow",
"deny": [{
"reason": "Block only the harmless documentation marker",
"jurisdiction": "*",
"model_prefix": "*",
"effect": "deny",
"conditions": [{
"attribute": "x-kastra-attr-tool-input",
"operator": "contains",
"value": "kastra-docs-deny"
}]
}]
}
curl --fail-with-body 'https://api.kastra.ai/api/v1/policies/validate' \
-H "Authorization: Bearer $KASTRA_TOKEN" \
-H "X-Organization-ID: $KASTRA_WORKSPACE_ID" \
-H 'X-Environment: docs-test' \
-H 'Content-Type: application/json' \
--data-binary @candidate.json
Create docs-test first or substitute an existing test environment. Check the returned validation result and warnings, not just HTTP success. A candidate, a saved revision, and an active revision are distinct states. Follow policy as code for a CLI workflow and policy syntax for operators and effects.
Policy Builder
The Build policies with AI guide walks through conversation, tests and review. These console API routes require a user bearer session with current Admin or Owner membership. Send X-Organization-ID: WORKSPACE_UUID and explicitly select the environment with X-Environment-ID: ENVIRONMENT_UUID. Sessions are private to their author in that workspace and environment; responses use Cache-Control: no-store. Creating a session or sending messages requires AI authoring access. Committing requires policy-authoring access and the entitlements for the proposed effects.
Paths below are relative to /api/v1/policies/builder.
| Method and path | Input / result |
|---|---|
GET /sessions | Lists the author’s unexpired sessions in the selected environment |
POST /sessions | {} starts an empty additive draft; optional paths selects scope. To edit the current base explicitly, supply base_policy_id and base_revision |
GET /sessions/{sessionID} | Reads the current session snapshot |
PUT /sessions/{sessionID} | Current version, complete document, and paths; optional challenge_suites selects independent challenges |
DELETE /sessions/{sessionID} | Discards the session; leaves any committed policy intact |
POST /sessions/{sessionID}/messages | Current version, a UUID id, and non-empty content; returns the session while generation continues |
GET /sessions/{sessionID}/events | Authenticated server-sent events containing session snapshots |
POST /sessions/{sessionID}/tests | Current version and examples; returns session and verification |
POST /sessions/{sessionID}/stop | Stops the running turn; no version field is required |
POST /sessions/{sessionID}/rebase | Current version; updates the base and invalidates previous verification |
POST /sessions/{sessionID}/commit | Reviewed version, draft_hash, suite_hash, and explicit publish boolean; returns a commit receipt |
Scope values are agent_prompt, agent_tool, proxy_prompt, proxy_output, mcp_local, and mcp_hosted. Editing replaces the supplied document and paths; it is not a partial patch. Omitting challenge_suites preserves the selection; [] clears it. Request objects reject unknown fields.
Successful Builder responses are direct JSON, without a success / data wrapper: listing returns an array; create, read, edit, message, stop and rebase return a session; tests return { "session": ..., "verification": ... }; deletion returns { "deleted": true }. A commit receipt contains policy_id, revision, published, and draft_hash. Inspect the receipt to confirm activation. publish: false is available only when creating a policy in an environment without an existing one.
Use the latest returned session version for each versioned request. Retrying a message uses the same UUID and content; reusing its ID with different content conflicts. Open event streams with authenticated fetch: each state event contains a complete session, and its event ID is the session version. Reconnect the stream or fetch the session after a disconnect; reconnecting must not resend the message. A matching commit retry returns the existing receipt.
Invalid input returns 400, missing or expired drafts 404, stale versions or conflicting state 409, and unavailable Builder configuration 503. Keep the draft on a conflict, reload, and rerun tests before reviewing again. These routes are documented here; they are not yet included in the downloadable OpenAPI schema.
Asynchronous AI authoring jobs
These administrator-gated asynchronous job APIs remain available alongside the conversational Builder:
| Operation | Start route | Poll route |
|---|---|---|
| Draft one rule | POST /api/v1/policies/draft-rule/jobs | GET /api/v1/policies/draft-rule/jobs/{id} |
| Summarize change | POST /api/v1/policies/draft-change-summary/jobs | GET /api/v1/policies/draft-change-summary/jobs/{id} |
| Narrow a flagged rule | POST /api/v1/decisions/{decisionID}/suggest-policy-fix/jobs | GET /api/v1/decisions/{decisionID}/suggest-policy-fix/jobs/{id} |
Use a user session and the required AI authoring entitlement. The single-rule request requires a non-empty description; preserve unknown result fields in API clients. Read the asynchronous job status and nested result; an accepted job is not an installed policy. Older synchronous drafting routes are deprecated and remain bounded by the request timeout. Keep drafting failures visible and inspect the error before retrying.
Audit and approvals
GET /api/v1/audit lists administrative events, with environment_id, event_type, entity_type, entity_id, actor_id, and RFC3339 from / to filters. It uses offset pagination: limit defaults to 50 (maximum 100), and offset defaults to zero. The response returns pagination.limit and pagination.offset; advance by the page size and stop on a short page.
Checkpoint lists use a different, opt-in cursor contract. See approval pagination and resolution, including the distinction between runtime readers and authorized human approvers.
Handle errors explicitly
Native errors use success: false, a human-readable error, and a stable code where documented. Handle authentication, authorization, invalid input, conflicts, entitlements, throttling, and service failure separately. A decision query rejected with 422 / invalid_request can require a smaller time range or additional filters. Preserve unknown response fields for forward compatibility.
The downloadable OpenAPI schema includes the runtime contract and selected management reads. This guide covers additional policy operations; internal administration routes are not customer integration endpoints.