Skip to Content
Quickstart

Quickstart

Connect your application to Keel and inspect your first authorization decision.

Prerequisites

Sign up for Keel to get started. In the Keel dashboard , create a project, generate your API key, add a provider key, and set your first control.

After signup, use the Keel dashboard  to:

  1. Create a project.
  2. Copy the project API key.
  3. Add a provider key (OpenAI, Anthropic, etc.) to that same project.
  4. Set a starting control for that project.

For a first run, use the dashboard’s built-in template flow:

  • Starter: apply a template such as Provider Allowlist or Daily Budget Cap.
  • Production and Enterprise: apply a template first, or create a custom policy if you need it.

The quickstart stays template-first because it is the safest onboarding path. If you need custom authoring later, use the policy authoring reference together with Plans & Entitlements for the current tier boundaries.

Step 1: Install the SDK

pip install keel-sdk

Go users can install the generated/reference client separately with go get github.com/keelapi/keel-go/pkg/keelclient. See Go Reference Client.

Step 2: Set your environment variables

export KEEL_BASE_URL="https://api.keelapi.com" export KEEL_API_KEY="keel_sk_your_project_key" export KEEL_PROJECT_ID="your-project-uuid"

Step 3: Send a supported request to Keel

Use the Python or TypeScript provider wrapper — same interface you already know, with governance built in.

from keel_sdk.providers.openai import OpenAI client = OpenAI() response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Reply with exactly: keel works"}], max_tokens=16, temperature=0, ) print(response["choices"][0]["message"]["content"])

Expected output:

keel works

What just happened

That single call triggered four things transparently:

  1. Permit evaluated — Keel checked your project’s policies and budget before anything reached OpenAI. If the request was not allowed, the wrapper would have raised an SDK error and no provider call would have been made.
  2. Budget enforced — Token estimates were checked against your project’s spending limits.
  3. Provider executed — Keel proxied the request to OpenAI using your project’s stored provider key. Your app never handled raw OpenAI credentials.
  4. Audit recorded — Keel recorded the decision, routing, token usage, and execution evidence. You can view this in the dashboard or query it via the API.

Your code saw a standard OpenAI-shaped response. The governance was invisible.

One default does show: a request that sets no output limit is capped at 256 output tokens (finish_reason: "length" on the response). Set max_tokens on the request for longer answers. See output length.

Step 4: Inspect what Keel decided

The wrapper hides the governance details for ergonomics, but you can pull them up explicitly. This step is what to do after the first call when you want to see the permit, the policy match, and the audit record.

Get the permit ID

Every Keel-managed response carries governance metadata in response headers. The SDKs expose that metadata through their supported response helpers:

x-keel-permit-id: 31d66d92-4e90-4dc6-9278-0b86551e31bc

Capture the Permit and request identifiers after a successful call:

permit_id = response.keel.permit_id request_id = response.keel.request_id print("permit:", permit_id)

Fetch the permit record

export KEEL_PERMIT_ID="31d66d92-4e90-4dc6-9278-0b86551e31bc" curl -sS https://api.keelapi.com/v1/permits/$KEEL_PERMIT_ID \ -H "Authorization: Bearer $KEEL_API_KEY" \ | jq '{id, decision, decision_details, policy_id, policy_version, created_at}'

The readback uses the audit-record shape. Its most important fields are:

{ "id": "31d66d92-4e90-4dc6-9278-0b86551e31bc", "decision": "allow", "decision_details": { "decision": "allow", "code": "policy.active_policy_candidate", "reason": "Active policy authorized this action." }, "policy_id": "default", "policy_version": "1.0.0", "created_at": "2026-04-25T18:00:00Z" }

The fields you most often want:

  • decision — allow, deny, review, or throttle. Only allow authorizes dispatch.
  • decision_details.code — the machine-readable reason for the decision. Full list at Errors › Permit reason codes.
  • policy_id and policy_version — which policy and version the decision was evaluated against.

Replay the timeline

For a chronological view of everything Keel did — permit, routing, dispatch, usage — fetch the timeline by request id:

export KEEL_REQUEST_ID="request-id-from-sdk-metadata" curl -sS https://api.keelapi.com/v1/requests/$KEEL_REQUEST_ID/timeline \ -H "Authorization: Bearer $KEEL_API_KEY" | jq

The response is a sequence of execution events. For the /v1/execute request above it is permit.allowed, routing.selected_model, usage.recorded, request.completed and permit.expiry_scheduled; some execution paths also record provider.request_sent and provider.response_received. See Timeline Replay for the full event taxonomy.

Where to go next

Use Concepts for the decision and evidence glossary. The advanced reference, How Keel Executes Authorized Actions, explains dispatch responsibilities and API differences.

Connect with an SDK or API

For Python and TypeScript, the provider wrappers above submit supported requests to Keel and handle the documented authorization, execution, and usage flow. Check the SDK support limits before migrating provider-specific features.

For raw API requests with provider-shaped input, use /v1/execute as shown below. API formats are not interchangeable: follow the endpoint’s required fields, freshness headers, and idempotency rules. Proxy response shapes vary by route, including the two Anthropic paths; see Proxy Execution. The decision-only /v1/permits API does not execute an action; its caller must enforce the decision and constraints. See the advanced reference if your integration requires it.

curl / raw API

These examples generate the required freshness headers with date and openssl. Managed execution also requires a provider key configured in Dashboard → Connectors .

If you prefer to test with curl before integrating the SDK:

export KEEL_API_KEY="keel_sk_your_project_key"
curl -sS https://api.keelapi.com/v1/execute \ -H "Authorization: Bearer $KEEL_API_KEY" \ -H "Content-Type: application/json" \ -H "X-Keel-Timestamp: $(date +%s)" \ -H "X-Keel-Nonce: $(openssl rand -hex 16)" \ -H "Idempotency-Key: quickstart-exec-001" \ -d '{ "provider": "openai", "model": "gpt-4o-mini", "input": { "messages": [ { "role": "user", "content": "Reply with exactly: keel works" } ], "max_tokens": 16, "temperature": 0 } }' | jq

Example response:

{ "id": "exec_req_01hy0m9m1d3z5r8j52x6m7n8pq", "object": "execution", "created_at": "2026-03-11T00:00:00Z", "status": "completed", "status_code": 200, "output": { "content": [ { "type": "text", "role": "assistant", "text": "keel works" } ] }, "output_assets": [], "routing": { "requested_provider": "openai", "requested_model": "gpt-4o-mini", "selected_provider": "openai", "selected_model": "gpt-4o-mini", "reason_code": "explicit_request", "fallback_occurred": false }, "governance": { "decision": "allow", "reason": "ok", "actions": [], "constraints": null, "budgets": null }, "usage": { "input_tokens": 13, "output_tokens": 3, "total_tokens": 16, "cost_usd_micros": 7, "estimated_final": false, "metrics": [ {"meter": "input_tokens", "quantity": 13, "unit": "tokens"}, {"meter": "output_tokens", "quantity": 3, "unit": "tokens"} ] }, "timing": { "started_at": "2026-03-11T00:00:00Z", "completed_at": "2026-03-11T00:00:01Z", "duration_ms": 731 }, "error": null, "resolved": { "provider": "openai", "model": "gpt-4o-mini", "alias": null } }

If you need the associated permit id for correlation, read the x-keel-permit-id response header. The body’s governance block stays focused on the decision outcome.

Next

  • See the full SDK reference for Python and TypeScript provider wrappers, streaming, error handling, and advanced client access.
  • If you need advanced external enforcement later, read the decision-only API reference.
  • If you want the full route matrix later, come back to Route comparison.

Acceptance checks

  • I know which route to start with.
  • I know whether Keel or my app executes the provider call.
  • I know whether the response is normalized or provider-native.
  • I know where to put idempotency.
  • I know how errors are shaped.
Last updated on Edit this page on GitHub