Skip to Content
Workflow IntentAPI Reference

Workflow Intent API Reference

Workflow intent endpoints create, amend, complete, and read caller-declared workflow declarations.

Authorization: Bearer <project_api_key> Content-Type: application/json

Workflow intent is available on Production and Enterprise plans. Starter callers receive the standard plan.upgrade_required error envelope. See Plan Gating.

Route summary

RoutePurpose
POST /v1/workflowsDeclare a workflow.
POST /v1/workflows/{workflow_id}/amendAppend an amendment to the workflow declaration.
POST /v1/workflows/{workflow_id}/completeExplicitly complete a workflow.
GET /v1/workflowsList 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 }
FieldRequired?Meaning
workflow_idYesCaller-supplied workflow handle, unique per project. Use [A-Za-z0-9_-], 1 to 255 characters.
intentYesCaller-declared workflow intent.
intent.expected_callsConditionalExpected number of governed calls. Informational baseline for drift detection. Required if intent.max_calls is omitted.
intent.max_callsConditionalMaximum governed calls allowed for this workflow. Enforcement ceiling. Required if intent.expected_calls is omitted.
intent.expected_modelNoModel the caller expects the workflow to use. Used in projection methodology when supplied.
intent.expected_input_tokens_per_callNoCaller-declared input-token estimate per call. Used for cost projection.
intent.expected_output_tokens_per_callNoCaller-declared output-token estimate per call. Used for cost projection.
intent.max_duration_secondsNoMaximum active duration before Keel expires the workflow.
budget_envelope_idNoOptional 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" }
FieldMeaning
workflow_idCaller-supplied workflow handle.
decisionDeclaration result. accepted means the workflow can run subject to policy and call limits.
statusLifecycle status. New accepted declarations start as active.
versionDeclaration version. Starts at 1 and increments on each amendment.
actual_callsCount of calls currently attributed to the workflow.
projected_costCost projection calculated from caller-declared intent, when Keel has enough pricing inputs.
projected_cost.amount_microsProjected cost in USD microdollars.
projected_cost.currencyCurrency for the projection.
projected_cost.methodologyRecorded methodology for how the projection was calculated.
projected_cost.methodology.basisProjection basis. For workflow intent this records caller-declared workflow pricing.
projected_cost.methodology.provenanceProvenance of the input values. Workflow intent uses caller_declared_workflow.
projected_cost.methodology.expected_callsDeclared expected call count used in the projection.
projected_cost.methodology.input_tokens_per_call_estimatedDeclared input-token estimate per call used in the projection.
projected_cost.methodology.output_tokens_per_call_estimatedDeclared output-token estimate per call used in the projection.
projected_cost.methodology.pricing_table_idPricing table used for the calculation.
projected_cost.methodology.tokenizerTokenizer assumed for the estimate.
projected_cost.methodology.qualityMethodology quality label for the projection inputs.
declared_byPrincipal that declared the workflow.
declared_by.typePrincipal type, such as api_key or dashboard_session.
declared_by.idIdentifier for the declaring principal.
declared_viaClient-provided SDK metadata when present. These values are recorded as claims from the caller.
declared_via.sdkClaimed SDK name.
declared_via.sdk_versionClaimed SDK version.
declaration_signature_b64Signature over the canonical declaration artifact.
declared_atDeclaration timestamp.
expires_atExpiration 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 } }
FieldMeaning
workflow_idCaller-supplied workflow handle.
decisionrejected means the workflow was not admitted.
reason_codeStable reason code for the rejection.
projected_costProjection that caused the rejection, when available.
projected_cost.amount_microsProjected cost in USD microdollars.
projected_cost.currencyProjection currency.
projected_cost.methodologyRecorded calculation methodology.
projected_cost.methodology.basisProjection basis.
projected_cost.methodology.provenanceProvenance for the declared values.
decision_detailsNumeric inputs used for the decision.
decision_details.current_monthly_spend_usd_microsCurrent month-to-date spend before this declaration.
decision_details.monthly_cap_usd_microsProject monthly cap.
decision_details.projected_workflow_cost_usd_microsProjected 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" }
FieldRequired?Meaning
if_match_versionYesVersion the caller read before submitting the amendment. Keel rejects the amendment if the current version differs.
new_expected_callsNoReplacement expected call count.
new_max_callsNoReplacement maximum call count.
reason_providedNoCaller-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" } } }
FieldMeaning
workflow_idCaller-supplied workflow handle.
statusCurrent lifecycle status after the amendment.
versionNew declaration version after applying the amendment.
amendmentSigned amendment record.
amendment.idAmendment identifier.
amendment.applied_against_versionVersion the amendment was applied against.
amendment.previous_expected_callsPrevious expected call count.
amendment.new_expected_callsNew expected call count.
amendment.previous_max_callsPrevious maximum call count.
amendment.new_max_callsNew maximum call count.
amendment.reason_providedCaller-provided reason.
amendment.amendment_signature_b64Signature over the canonical amendment artifact.
amendment.created_atAmendment creation timestamp.
projected_costRecomputed projection after applying the amendment, when available.
projected_cost.amount_microsProjected cost in USD microdollars.
projected_cost.currencyProjection currency.
projected_cost.methodologyRecorded calculation methodology.
projected_cost.methodology.basisProjection basis.
projected_cost.methodology.provenanceProvenance 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 } } }
FieldMeaning
error.codeStable error or reason code.
error.messageHuman-readable summary.
error.details.current_versionCurrent workflow declaration version.
error.details.if_match_versionVersion 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" }
FieldRequired?Meaning
reason_providedNoCaller-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 } }
FieldMeaning
workflow_idCaller-supplied workflow handle.
statuscompleted after successful completion.
versionDeclaration version at completion.
actual_callsActual governed call count attributed to the workflow.
expected_callsEffective expected call count at completion.
max_callsEffective maximum call count at completion.
completed_atCompletion timestamp.
reconciliationCounter reconciliation result.
reconciliation.authoritative_actual_callsCount recomputed from permit records.
reconciliation.cached_actual_callsCounter stored on the workflow declaration before reconciliation.
reconciliation.counter_divergence_detectedWhether the cached count differed from the authoritative count.

GET /v1/workflows

Lists workflows in the authenticated project.

Query parameters

ParameterMeaning
statusOptional lifecycle status filter: active, completed, expired, or rejected.
created_at_gteOptional inclusive lower bound for creation time.
created_at_lteOptional inclusive upper bound for creation time.
limitOptional page size.
cursorOptional 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" }
FieldMeaning
dataPage of workflow summaries.
data[].workflow_idCaller-supplied workflow handle.
data[].statusLifecycle status.
data[].versionCurrent declaration version.
data[].actual_callsCurrent attributed call count.
data[].expected_callsEffective expected call count.
data[].max_callsEffective maximum call count.
data[].declared_atDeclaration timestamp.
data[].expires_atExpiration timestamp, if any.
next_cursorCursor 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" } ] }
FieldMeaning
workflow_idCaller-supplied workflow handle.
statusLifecycle status.
versionCurrent declaration version.
actual_callsCurrent attributed call count.
expected_callsEffective expected call count.
max_callsEffective maximum call count.
driftCurrent drift indicators.
drift.expected_calls_exceededWhether actual calls are above the effective expected call count.
drift.max_calls_exceededWhether actual calls have reached the effective maximum call count.
declarationDeclaration evidence metadata.
declaration.declared_atDeclaration timestamp.
declaration.declaration_signature_b64Signature over the canonical declaration artifact.
declaration.canonical_intent_hashHash of the canonical declaration intent.
amendmentsOrdered amendment history.
amendments[].idAmendment identifier.
amendments[].applied_against_versionVersion the amendment was applied against.
amendments[].new_expected_callsExpected call count after the amendment.
amendments[].new_max_callsMaximum call count after the amendment.
amendments[].reason_providedCaller-provided amendment reason.
amendments[].created_atAmendment 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.

Last updated on Edit this page on GitHubÂ