DocumentationStart here

Quickstart: govern a model request

Send an OpenAI request through Kastra, block a test request, and resume one after approval.

Updated 2026-09-22 Read as Markdown
On this page

Prepare credentials and policy

You need an OpenAI API key and accessible model, a Kastra workspace with custom policies, proxy and approvals enabled, an administrator, and an authorized approver. Successful model requests use your provider account and may incur its normal charges. Install the operator CLI; use a POSIX shell with curl and uuidgen.

kastra auth login
kastra env create docs-proxy
kastra api-key create docs-proxy-test --env docs-proxy

Store the newly printed runtime key as KASTRA_API_KEY and the provider secret as OPENAI_API_KEY in your local environment. Record the runtime key ID for cleanup. Use a fresh docs-proxy environment; if the name exists, choose another and change the example policy’s metadata.environment. Select enforcement mode in the console.

Download and inspect model-proxy.kastra. It matches only the custom X-Kastra-Attr-Docs-Test marker: kastra-docs-deny blocks, kastra-docs-hold requires approval, and other values default to ALLOW. This caller-supplied marker demonstrates the policy flow; use trusted application context for real access controls.

curl --fail --output model-proxy.kastra https://docs.kastra.ai/examples/model-proxy.kastra
kastra policy validate model-proxy.kastra
kastra policy add model-proxy.kastra --activate

Save the policy ID and confirm its active revision in the console.

Prepare one harmless request

Save this as request.json, replacing the model placeholder with a model your OpenAI account can use:

{
  "model": "REPLACE_WITH_YOUR_OPENAI_MODEL",
  "messages": [{"role": "user", "content": "Reply with a short greeting."}],
  "stream": false
}

Define this shell function. It sends the same file bytes every time; keep the file and credentials unchanged during an approval cycle.

send_request() {
  curl -i https://api.kastra.ai/v1/chat/completions \
    -H "Authorization: Bearer $KASTRA_API_KEY" \
    -H "X-Upstream-API-Key: $OPENAI_API_KEY" \
    -H 'Content-Type: application/json' \
    -H 'X-Jurisdiction: US' \
    -H "X-Kastra-Attr-Docs-Test: $1" \
    -H 'X-Kastra-Hold-Mode: async' \
    -H "X-Kastra-Operation-Id: $2" \
    --data-binary @request.json
}

Jurisdiction is policy context, not a hosting-region selector. The runtime key determines the environment.

Prove an allow and a block

send_request kastra-docs-allow "$(uuidgen | tr '[:upper:]' '[:lower:]')"
send_request kastra-docs-deny "$(uuidgen | tr '[:upper:]' '[:lower:]')"

The first request should return a provider greeting and X-Policy-Decision: ALLOW. The second should return a policy denial before contacting the provider, with no generated greeting. Inspect its status, structured error, decision header and matching rule in Activity. Authentication errors and provider failures are not successful policy tests.

Hold and resume the same action

operation_id=$(uuidgen | tr '[:upper:]' '[:lower:]')
send_request kastra-docs-hold "$operation_id"

Expect HTTP 202, X-Policy-Decision: HOLD, and error.code: hold_pending with a checkpoint ID. Open Approvals, inspect this request in docs-proxy, and approve it. Once approved, run the same command once, preserving the same operation ID, request bytes, credentials, path and attributes:

send_request kastra-docs-hold "$operation_id"

Expect the greeting after authorization. A new operation ID represents a new action, so it requires a new approval. To test refusal, generate a new ID, send a HOLD request, and deny its checkpoint. Its identical resubmission must remain denied. Expiry is configured to deny after five minutes. Full replay contract.

Review and clean up

Find the ALLOW, DENY and approval resolution in Activity. Deactivate the example policy and revoke only the runtime key created above:

kastra policy deactivate POLICY_ID
kastra api-key revoke KEY_ID
unset KASTRA_API_KEY OPENAI_API_KEY operation_id

Substitute the recorded IDs. Keep the test environment and decision records if you need them for review. Continue with content guardrails or the Anthropic integration.