Workflow Intent API Reference
Workflow intent endpoints create, amend, complete, and read caller-declared workflow declarations.
Authorization: Bearer <project_api_key>
Content-Type: application/jsonWorkflow intent is available on Production and Enterprise plans. Starter callers
receive the standard plan.upgrade_required error envelope. See Plan Gating.
Route summary
| Route | Purpose |
|---|---|
POST /v1/workflows | Declare a workflow. |
POST /v1/workflows/{workflow_id}/amend | Append an amendment to the workflow declaration. |
POST /v1/workflows/{workflow_id}/complete | Explicitly complete a workflow. |
GET /v1/workflows | List workflow declarations in the authenticated project. |
GET /v1/workflows/{workflow_id} | Fetch one workflow declaration with state and amendment history. |
POST /v1/workflows
Declares a workflow before it runs. The caller supplies workflow_id and an intent object. workflow_id is idempotent per project.
If the same workflow_id is declared again with the same canonical intent, Keel returns the existing declaration. If the same workflow_id is declared with a different canonical intent, Keel returns 409 with workflow_intent.idempotency_conflict.
Request
{
"workflow_id": "invoice-batch-2026-05-13",
"intent": {
"expected_calls": 10000,
"max_calls": 12000,
"expected_model": "gpt-5-mini",
"expected_input_tokens_per_call": 4000,
"expected_output_tokens_per_call": 500,
"max_duration_seconds": 86400
},
"budget_envelope_id": null
}| Field | Required? | Meaning |
|---|---|---|
workflow_id | Yes | Caller-supplied workflow handle, unique per project. Use [A-Za-z0-9_-], 1 to 255 characters. |
intent | Yes | Caller-declared workflow intent. |
intent.expected_calls | Conditional | Expected number of governed calls. Informational baseline for drift detection. Required if intent.max_calls is omitted. |
intent.max_calls | Conditional | Maximum governed calls allowed for this workflow. Enforcement ceiling. Required if intent.expected_calls is omitted. |
intent.expected_model | No | Model the caller expects the workflow to use. Used in projection methodology when supplied. |
intent.expected_input_tokens_per_call | No | Caller-declared input-token estimate per call. Used for cost projection. |
intent.expected_output_tokens_per_call | No | Caller-declared output-token estimate per call. Used for cost projection. |
intent.max_duration_seconds | No | Maximum active duration before Keel expires the workflow. |
budget_envelope_id | No | Optional budget envelope to bind to the workflow for dollar enforcement in addition to call-count enforcement. Use null or omit when no envelope is bound. |
At least one of intent.expected_calls or intent.max_calls must be set.
Accepted response
{
"workflow_id": "invoice-batch-2026-05-13",
"decision": "accepted",
"status": "active",
"version": 1,
"actual_calls": 0,
"projected_cost": {
"amount_micros": 22500000,
"currency": "USD",
"methodology": {
"basis": "caller_declared_workflow_x_point_pricing",
"provenance": "caller_declared_workflow",
"expected_calls": 10000,
"input_tokens_per_call_estimated": 4000,
"output_tokens_per_call_estimated": 500,
"pricing_table_id": "openai_v3.2_2026-04-15",
"tokenizer": "cl100k_base",
"quality": "authoritative"
}
},
"declared_by": {
"type": "api_key",
"id": "ak_01jz8k3q4s9t2v6w8x0y1z2a3b"
},
"declared_via": {
"sdk": "keel-python",
"sdk_version": "0.4.1"
},
"declaration_signature_b64": "base64-signature",
"declared_at": "2026-05-13T14:21:00Z",
"expires_at": "2026-05-14T14:21:00Z"
}| Field | Meaning |
|---|---|
workflow_id | Caller-supplied workflow handle. |
decision | Declaration result. accepted means the workflow can run subject to policy and call limits. |
status | Lifecycle status. New accepted declarations start as active. |
version | Declaration version. Starts at 1 and increments on each amendment. |
actual_calls | Count of calls currently attributed to the workflow. |
projected_cost | Cost projection calculated from caller-declared intent, when Keel has enough pricing inputs. |
projected_cost.amount_micros | Projected cost in USD microdollars. |
projected_cost.currency | Currency for the projection. |
projected_cost.methodology | Recorded methodology for how the projection was calculated. |
projected_cost.methodology.basis | Projection basis. For workflow intent this records caller-declared workflow pricing. |
projected_cost.methodology.provenance | Provenance of the input values. Workflow intent uses caller_declared_workflow. |
projected_cost.methodology.expected_calls | Declared expected call count used in the projection. |
projected_cost.methodology.input_tokens_per_call_estimated | Declared input-token estimate per call used in the projection. |
projected_cost.methodology.output_tokens_per_call_estimated | Declared output-token estimate per call used in the projection. |
projected_cost.methodology.pricing_table_id | Pricing table used for the calculation. |
projected_cost.methodology.tokenizer | Tokenizer assumed for the estimate. |
projected_cost.methodology.quality | Methodology quality label for the projection inputs. |
declared_by | Principal that declared the workflow. |
declared_by.type | Principal type, such as api_key or dashboard_session. |
declared_by.id | Identifier for the declaring principal. |
declared_via | Client-provided SDK metadata when present. These values are recorded as claims from the caller. |
declared_via.sdk | Claimed SDK name. |
declared_via.sdk_version | Claimed SDK version. |
declaration_signature_b64 | Signature over the canonical declaration artifact. |
declared_at | Declaration timestamp. |
expires_at | Expiration timestamp when max_duration_seconds was declared; otherwise null. |
Rejected response
{
"workflow_id": "invoice-batch-2026-05-13",
"decision": "rejected",
"reason_code": "workflow_intent.declaration_exceeds_budget_cap",
"projected_cost": {
"amount_micros": 22500000,
"currency": "USD",
"methodology": {
"basis": "caller_declared_workflow_x_point_pricing",
"provenance": "caller_declared_workflow"
}
},
"decision_details": {
"current_monthly_spend_usd_micros": 810000000,
"monthly_cap_usd_micros": 825000000,
"projected_workflow_cost_usd_micros": 22500000
}
}| Field | Meaning |
|---|---|
workflow_id | Caller-supplied workflow handle. |
decision | rejected means the workflow was not admitted. |
reason_code | Stable reason code for the rejection. |
projected_cost | Projection that caused the rejection, when available. |
projected_cost.amount_micros | Projected cost in USD microdollars. |
projected_cost.currency | Projection currency. |
projected_cost.methodology | Recorded calculation methodology. |
projected_cost.methodology.basis | Projection basis. |
projected_cost.methodology.provenance | Provenance for the declared values. |
decision_details | Numeric inputs used for the decision. |
decision_details.current_monthly_spend_usd_micros | Current month-to-date spend before this declaration. |
decision_details.monthly_cap_usd_micros | Project monthly cap. |
decision_details.projected_workflow_cost_usd_micros | Projected workflow cost being evaluated. |
POST /v1/workflows/{workflow_id}/amend
Appends an amendment to an active workflow declaration. The caller must send if_match_version so Keel can reject lost updates.
Request
{
"if_match_version": 1,
"new_expected_calls": 15000,
"new_max_calls": 20000,
"reason_provided": "ticket volume higher than expected today"
}| Field | Required? | Meaning |
|---|---|---|
if_match_version | Yes | Version the caller read before submitting the amendment. Keel rejects the amendment if the current version differs. |
new_expected_calls | No | Replacement expected call count. |
new_max_calls | No | Replacement maximum call count. |
reason_provided | No | Caller-provided reason for the amendment. |
At least one of new_expected_calls or new_max_calls must be supplied.
Response
{
"workflow_id": "invoice-batch-2026-05-13",
"status": "active",
"version": 2,
"amendment": {
"id": "wam_01jz8m0n1p2q3r4s5t6v7w8x9y",
"applied_against_version": 1,
"previous_expected_calls": 10000,
"new_expected_calls": 15000,
"previous_max_calls": 12000,
"new_max_calls": 20000,
"reason_provided": "ticket volume higher than expected today",
"amendment_signature_b64": "base64-signature",
"created_at": "2026-05-13T16:05:00Z"
},
"projected_cost": {
"amount_micros": 45000000,
"currency": "USD",
"methodology": {
"basis": "caller_declared_workflow_x_point_pricing",
"provenance": "caller_declared_workflow"
}
}
}| Field | Meaning |
|---|---|
workflow_id | Caller-supplied workflow handle. |
status | Current lifecycle status after the amendment. |
version | New declaration version after applying the amendment. |
amendment | Signed amendment record. |
amendment.id | Amendment identifier. |
amendment.applied_against_version | Version the amendment was applied against. |
amendment.previous_expected_calls | Previous expected call count. |
amendment.new_expected_calls | New expected call count. |
amendment.previous_max_calls | Previous maximum call count. |
amendment.new_max_calls | New maximum call count. |
amendment.reason_provided | Caller-provided reason. |
amendment.amendment_signature_b64 | Signature over the canonical amendment artifact. |
amendment.created_at | Amendment creation timestamp. |
projected_cost | Recomputed projection after applying the amendment, when available. |
projected_cost.amount_micros | Projected cost in USD microdollars. |
projected_cost.currency | Projection currency. |
projected_cost.methodology | Recorded calculation methodology. |
projected_cost.methodology.basis | Projection basis. |
projected_cost.methodology.provenance | Provenance for the declared values. |
Version conflict
{
"error": {
"code": "workflow_intent.amendment_version_conflict",
"message": "Workflow declaration version does not match if_match_version.",
"details": {
"current_version": 3,
"if_match_version": 1
}
}
}| Field | Meaning |
|---|---|
error.code | Stable error or reason code. |
error.message | Human-readable summary. |
error.details.current_version | Current workflow declaration version. |
error.details.if_match_version | Version supplied by the caller. |
POST /v1/workflows/{workflow_id}/complete
Explicitly completes a workflow. Completion closes the workflow and reconciles actual calls against the declaration and amendments.
Request
{
"reason_provided": "batch finished"
}| Field | Required? | Meaning |
|---|---|---|
reason_provided | No | Caller-provided completion reason. |
Response
{
"workflow_id": "invoice-batch-2026-05-13",
"status": "completed",
"version": 2,
"actual_calls": 18433,
"expected_calls": 15000,
"max_calls": 20000,
"completed_at": "2026-05-13T18:30:00Z",
"reconciliation": {
"authoritative_actual_calls": 18433,
"cached_actual_calls": 18433,
"counter_divergence_detected": false
}
}| Field | Meaning |
|---|---|
workflow_id | Caller-supplied workflow handle. |
status | completed after successful completion. |
version | Declaration version at completion. |
actual_calls | Actual governed call count attributed to the workflow. |
expected_calls | Effective expected call count at completion. |
max_calls | Effective maximum call count at completion. |
completed_at | Completion timestamp. |
reconciliation | Counter reconciliation result. |
reconciliation.authoritative_actual_calls | Count recomputed from permit records. |
reconciliation.cached_actual_calls | Counter stored on the workflow declaration before reconciliation. |
reconciliation.counter_divergence_detected | Whether the cached count differed from the authoritative count. |
GET /v1/workflows
Lists workflows in the authenticated project.
Query parameters
| Parameter | Meaning |
|---|---|
status | Optional lifecycle status filter: active, completed, expired, or rejected. |
created_at_gte | Optional inclusive lower bound for creation time. |
created_at_lte | Optional inclusive upper bound for creation time. |
limit | Optional page size. |
cursor | Optional pagination cursor from the previous response. |
Response
{
"data": [
{
"workflow_id": "invoice-batch-2026-05-13",
"status": "active",
"version": 2,
"actual_calls": 9412,
"expected_calls": 15000,
"max_calls": 20000,
"declared_at": "2026-05-13T14:21:00Z",
"expires_at": "2026-05-14T14:21:00Z"
}
],
"next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wNS0xM1QxNDoyMTowMFoifQ"
}| Field | Meaning |
|---|---|
data | Page of workflow summaries. |
data[].workflow_id | Caller-supplied workflow handle. |
data[].status | Lifecycle status. |
data[].version | Current declaration version. |
data[].actual_calls | Current attributed call count. |
data[].expected_calls | Effective expected call count. |
data[].max_calls | Effective maximum call count. |
data[].declared_at | Declaration timestamp. |
data[].expires_at | Expiration timestamp, if any. |
next_cursor | Cursor for the next page, or null when no next page exists. |
GET /v1/workflows/{workflow_id}
Fetches the declaration, current state, amendment history, and drift indicators for one workflow.
Response
{
"workflow_id": "invoice-batch-2026-05-13",
"status": "active",
"version": 2,
"actual_calls": 9412,
"expected_calls": 15000,
"max_calls": 20000,
"drift": {
"expected_calls_exceeded": false,
"max_calls_exceeded": false
},
"declaration": {
"declared_at": "2026-05-13T14:21:00Z",
"declaration_signature_b64": "base64-signature",
"canonical_intent_hash": "sha256:abc123"
},
"amendments": [
{
"id": "wam_01jz8m0n1p2q3r4s5t6v7w8x9y",
"applied_against_version": 1,
"new_expected_calls": 15000,
"new_max_calls": 20000,
"reason_provided": "ticket volume higher than expected today",
"created_at": "2026-05-13T16:05:00Z"
}
]
}| Field | Meaning |
|---|---|
workflow_id | Caller-supplied workflow handle. |
status | Lifecycle status. |
version | Current declaration version. |
actual_calls | Current attributed call count. |
expected_calls | Effective expected call count. |
max_calls | Effective maximum call count. |
drift | Current drift indicators. |
drift.expected_calls_exceeded | Whether actual calls are above the effective expected call count. |
drift.max_calls_exceeded | Whether actual calls have reached the effective maximum call count. |
declaration | Declaration evidence metadata. |
declaration.declared_at | Declaration timestamp. |
declaration.declaration_signature_b64 | Signature over the canonical declaration artifact. |
declaration.canonical_intent_hash | Hash of the canonical declaration intent. |
amendments | Ordered amendment history. |
amendments[].id | Amendment identifier. |
amendments[].applied_against_version | Version the amendment was applied against. |
amendments[].new_expected_calls | Expected call count after the amendment. |
amendments[].new_max_calls | Maximum call count after the amendment. |
amendments[].reason_provided | Caller-provided amendment reason. |
amendments[].created_at | Amendment creation timestamp. |
Workflow headers on governed requests
Subsequent governed requests are associated with a workflow by sending:
X-Keel-Workflow-Id: <workflow_id>See Header Convention for direct API and orchestrator guidance.