Quickstart
Send your first governed AI call in under 60 seconds.
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.
Once approved, you will have access to the Keel dashboard where you:
- Create a project.
- Copy the project API key.
- Add a provider key (OpenAI, Anthropic, etc.) to that same project.
- 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 AllowlistorDaily Budget Cap. - Growth and above: 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
Python
pip install keel-sdkStep 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: Your first governed AI call
Use the provider wrapper — same interface you already know, with governance built in.
Python
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 worksWhat just happened
That single call triggered four things transparently:
- 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 a
KeelErrorand no provider call would have been made. - Budget enforced — Token estimates were checked against your project’s spending limits.
- Provider executed — Keel proxied the request to OpenAI using your project’s stored provider key. Your app never handled raw OpenAI credentials.
- 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.
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 the permit id in a response header:
x-keel-permit-id: permit_01jyf4m3n8q2r6t9v1w5x7y0zIn the SDK, the wrapper exposes the same value on the response object. Capture it after a successful call:
Python
permit_id = response.headers.get("x-keel-permit-id")
print("permit:", permit_id)Fetch the permit record
curl -sS https://api.keelapi.com/v1/permits/$KEEL_PERMIT_ID \
-H "Authorization: Bearer $KEEL_API_KEY" | jqThe returned permit captures the decision in full:
{
"id": "permit_01jyf4m3n8q2r6t9v1w5x7y0z",
"decision": "allow",
"reason_code": "ok",
"policy_id": "default",
"policy_version": "1.0.0",
"subject": {"type": "user", "id": "agent_42"},
"action": {"name": "ai.generate.text"},
"resource": {"type": "request", "attributes": {"provider": "openai", "model": "gpt-4o"}},
"budgets": {
"envelope_id": null,
"reserved": null,
"remaining": null
},
"constraints": null,
"created_at": "2026-04-25T18:00:00Z"
}The fields you most often want:
decision—allow,deny, orchallenge. The first governed call should produceallow.reason_code— a stable code describing why the decision came out that way. Full list at Errors › Permit reason codes.policy_idandpolicy_version— which policy and version the decision was evaluated against.budgets— the budget envelope state at decision time, when envelopes are configured.constraints— any constraints the policy imposed on the request (for example, a max output token cap).
Replay the timeline
For a chronological view of everything Keel did — permit, routing, dispatch, usage — fetch the timeline by request id:
curl -sS https://api.keelapi.com/v1/requests/$KEEL_REQUEST_ID/timeline \
-H "Authorization: Bearer $KEEL_API_KEY" | jqThe response is a sequence of execution events: permit.allowed, routing.selected_model, provider.request_sent, provider.response_received, usage.recorded, request.completed. See Timeline Replay for the full event taxonomy.
Where to go next
The objects above — permits, governed requests, routing records, execution events, and timelines — are the substrate every other Keel surface refers to. For the orienting glossary, see Concepts. For the full per-mode comparison and trade-offs, see Execution Modes.
Which route should I use?
For most teams, the provider wrappers shown above are the recommended starting point. They handle permits, budget, execution, and usage reporting automatically.
If you need more control, Keel also exposes direct API routes:
- Use
/v1/executeas the primary public runtime surface when you already have provider-shaped input and want normalized output. - Use
/v1/executionswhen you want Keel to execute through the provider-neutral contract. - Use
/v1/proxy/*when you want provider-native payloads and provider-native responses. - Use
/v1/permitswhen you want decision-first governance and your app will execute the provider call itself.
If you are new to Keel, start with the provider wrappers unless you specifically need permit-first, provider-shaped input, or provider-native proxy behavior.
Route comparison
| Route | Use when | Request shape | Response shape | Who executes provider call | Typical first-time user? | Idempotency location |
|---|---|---|---|---|---|---|
| SDK provider wrappers | You want the fastest integration with automatic governance. | Native provider SDK interface. | Native provider response. | Keel (via wrapper) | Yes | Managed by wrapper |
/v1/execute | You want the primary public runtime surface and already have provider-shaped input. | Provider-shaped body with provider, model, and input. | Normalized execution envelope plus resolved. | Keel | Sometimes, when you already have provider-shaped input | Idempotency-Key header |
/v1/executions | You want Keel to execute and your request is provider-neutral. | Canonical operation plus messages or inputs. | Normalized execution envelope. | Keel | Sometimes | Idempotency-Key header |
/v1/proxy/* | You want provider-native payloads and provider-native responses. | Provider-native payload. | Provider-native response. | Keel | Usually no, unless you need provider parity | Idempotency-Key header |
/v1/permits | You want decision-first governance and your app will call the provider itself. | Canonical permit body with project_id, subject, action, and resource. | Permit decision object only. | Your app | Sometimes, for permit-first integrations | JSON body idempotency_key |
curl / raw API
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 "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
}
}' | jqExample 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 all provider wrappers, streaming, error handling, and advanced client access.
- If you want a decision-first integration later, start with the permit-first flow.
- 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.