# Quickstart: govern a model request

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

Updated: 2026-09-22

Canonical: https://docs.kastra.ai/start/model-proxy

## 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](https://docs.kastra.ai/reference/cli); use a POSIX shell with `curl` and `uuidgen`.

```sh
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](https://docs.kastra.ai/examples/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.

```sh
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:

```json
{
  "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.

```sh
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

```sh
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

```sh
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:

```sh
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](https://docs.kastra.ai/proxy/approvals).

## 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:

```sh
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](https://docs.kastra.ai/proxy/content-guardrails) or the [Anthropic integration](https://docs.kastra.ai/proxy/anthropic).
