Timeline Replay
Keel provides a public route to reconstruct the full lifecycle of a governed request:
GET /v1/requests/{request_id}/timelineAuthentication: project API key.
The timeline is rebuilt from persisted decision records. It does not invent or interpolate data that was not persisted.
When to use it
- Compliance investigation — review the exact permit decision, policy match, and budget state at the time of a specific request
- Cost audit — inspect estimated and actual token/cost figures alongside the permit record
- Routing debug — confirm which provider and model were selected, whether fallback occurred, and what reason code drove the decision
- Incident triage — reconstruct an unexpected denial or throttle and identify its cause
Response shape
{
"project_id": "<uuid>",
"request_id": "req_demo_001",
"permit_ids": ["<permit_uuid>"],
"job_ids": [],
"session_ids": [],
"events": [
{
"timestamp": "2026-03-09T01:00:00Z",
"event_type": "permit.allowed",
"source": "permits",
"phase": "permit",
"provider": "openai",
"model": "gpt-4o-mini",
"permit_id": "<permit_uuid>",
"request_id": "req_demo_001",
"job_id": null,
"session_id": null,
"metadata": {
"decision": "allow",
"reason": "allowed"
}
},
{
"timestamp": "2026-03-09T01:00:02Z",
"event_type": "routing.selected_model",
"source": "execution_events",
"phase": "routing",
"provider": "openai",
"model": "gpt-4o-mini",
"permit_id": "<permit_uuid>",
"request_id": "req_demo_001",
"metadata": {
"reason_code": "routing_fitness"
}
}
]
}Event types
Depending on the request path and execution surface, the timeline may include the following events:
Permit phase
| Event | When it appears |
|---|---|
permit.allowed | Permit evaluation returned allow |
permit.denied | Permit evaluation returned deny |
permit.challenged | Permit evaluation returned challenge / review required |
Firewall phase
| Event | When it appears |
|---|---|
firewall.blocked | The prompt firewall blocked the request |
Routing phase
| Event | When it appears |
|---|---|
routing.selected_model | A provider/model was selected for dispatch |
fallback.triggered | The primary provider/model was unavailable and fallback started |
fallback.selected_model | A fallback provider/model was selected |
Execution phase
| Event | When it appears |
|---|---|
execution.started | Keel began dispatching the provider request |
provider.request_sent | The request was sent to the provider |
provider.response_received | The provider returned a response |
Usage and completion phase
| Event | When it appears |
|---|---|
usage.recorded | Final usage figures were persisted |
request.completed | The request completed successfully |
request.denied | The request was denied (execution surface) |
request.errored | The request encountered a provider or execution error |
Async job events (when the request was submitted as an async job)
| Event | When it appears |
|---|---|
job.submitted | Job was submitted |
job.queued | Job entered the queue |
job.processing | Job began processing |
job.completed | Job completed successfully |
job.failed | Job failed |
webhook.delivery_attempted | Webhook delivery was attempted |
webhook.delivery_succeeded | Webhook delivered successfully |
webhook.delivery_failed | Webhook delivery failed |
Realtime session events (when scaffolding was written for a realtime session)
| Event | When it appears |
|---|---|
session.started | Session opened |
session.ended | Session closed normally |
session.failed | Session closed with an error |
Limits
- Timelines are reconstructed from persisted data only. No synthetic latency or duration values are added.
- Successful prompt-firewall inspections do not appear as explicit timeline events — only
firewall.blockedevents are emitted. - Not every request path produces the full event set. Permit-first flows produce fewer events than Keel-managed execution routes, because Keel does not observe the provider call.
- When a provider does not return final usage for a stream, the timeline may show estimated or lower-bound usage rather than exact final figures.
Relationship to the permit record
The permit is the anchor record. Timeline events add chronological context but do not replace or modify the permit’s decision, reason code, constraints, or budget snapshot.
To fetch the raw permit record without the full timeline:
GET /v1/permits/{permit_id}To export a project audit bundle over a time window:
GET /v1/permits/export