From an API key to a verified action in ten minutes
Five calls. Everything below runs on the free Developer plan against https://api.commitlayer.ai. The same flow is guided step by step inside the app after you sign in.
1Get two API keys
Start free, then open Settings → API keys and create one key per side of the line: a Runtime recorder key for the agent (it records actions and can do nothing else) and an Evidence key for the pipeline that relays your systems of record (it pushes facts for the sources you bind it to and cannot record). The key that records an action can never also supply the facts that verify it. Each key is shown once; send it as X-API-Key.
export COMMITLAYER_RECORDER_KEY=spk_... # Runtime recorder: the agent's side export COMMITLAYER_EVIDENCE_KEY=spk_... # Evidence, bound to stripe, shopify, crm, messaging: the pipeline's side curl https://api.commitlayer.ai/v1/contracts -H "X-API-Key: $COMMITLAYER_RECORDER_KEY"
2Choose a contract
A contract says what a valid action looks like: the precondition that must hold, the limits on its parameters, the effects that must appear in your systems within a window, the effects that must never appear, and the later records that undo it. The library ships refund_workflow, payout_workflow, stripe_b2b_transfer (a transfer sent with stripe pay or the Treasury send_money tool, read back from Stripe's own record), deploy_workflow, access_grant_workflow and appointment_reschedule_workflow. Select the one nearest your agent as-is for the first run and edit it once the flow works; the two below show the same shape pointed at different systems.
curl -X POST https://api.commitlayer.ai/v1/contracts/refund_workflow/select -H "X-API-Key: $COMMITLAYER_RECORDER_KEY"
# the parts that matter (full YAML in the app under Contracts)
action:
window: "15m"
preconditions:
- state_before_action: {source: shopify, type: order_status, where: {status: eligible}}
limits:
- param_max: {param: amount, max: 250}
expected_effects:
- effect_observed: {source: stripe, type: refund_created}
- effect_observed: {source: shopify, type: order_cancelled}
- effect_observed: {source: crm, type: note_added}
- effect_observed: {source: messaging, type: customer_notified}
forbidden_effects:
- effect_count_exceeds: {source: stripe, type: refund_created, max: 1}
- effect_attr_exceeds: {source: stripe, type: refund_created, attr: amount, max: 250}
# the same shape for a release agent: deploy_workflow
action:
window: "30m"
preconditions:
- state_before_action: {source: monitoring, type: service_health, where: {status: healthy}}
limits:
- param_in: {param: environment, values: [staging, production]}
aggregate_limits:
- aggregate_exceeds: {source: ci, type: pipeline_completed, max: 3, over: "1h", by: agent}
expected_effects:
- effect_observed: {source: ci, type: pipeline_completed, where: {status: success}}
- effect_observed: {source: monitoring, type: health_check, where: {status: healthy}}
- effect_observed: {source: changes, type: change_recorded}
forbidden_effects:
- effect_observed: {source: monitoring, type: health_check, where: {status: unhealthy}}
reversed_by:
- effect_observed: {source: ci, type: rollback_completed, within: "24h"}3Record the action
Call this where your agent performs the action, with the agent's own parameters. It is idempotent on action_id, so retries are safe. The response carries a provisional verdict; the final one settles when the window closes.
curl -X POST https://api.commitlayer.ai/v1/actions \
-H "X-API-Key: $COMMITLAYER_RECORDER_KEY" -H "Content-Type: application/json" \
-d '{
"action_id": "act_0001",
"contract_id": "refund_workflow",
"workflow_id": "cancel_and_refund",
"agent_id": "support-agent",
"action_type": "refund_order",
"subject_id": "order_10231",
"subject_kind": "order",
"requested_at": "2026-10-01T10:42:00Z",
"params": {"amount": 180.0, "currency": "USD", "reason": "customer_cancelled"}
}'4Push facts from your systems
Facts are what actually happened, read from the systems of record, never from the agent. Push them from webhooks, a nightly export, or a read-only connector. If a ticket and an order are the same case, send a link so a notification on either counts. Facts are never metered.
curl -X POST https://api.commitlayer.ai/v1/facts \
-H "X-API-Key: $COMMITLAYER_EVIDENCE_KEY" -H "Content-Type: application/json" \
-d '{
"facts": [
{"source": "shopify", "subject_id": "order_10231", "fact_type": "order_status", "occurred_at": "2026-10-01T10:41:30Z", "attributes": {"status": "eligible"}},
{"source": "stripe", "subject_id": "order_10231", "fact_type": "refund_created", "occurred_at": "2026-10-01T10:42:07Z", "attributes": {"amount": 180.0, "currency": "USD"}},
{"source": "crm", "subject_id": "order_10231", "fact_type": "note_added", "occurred_at": "2026-10-01T10:42:09Z"},
{"source": "messaging", "subject_id": "tick_1", "fact_type": "customer_notified", "occurred_at": "2026-10-01T10:42:11Z"}
],
"links": [{"subject_id": "tick_1", "source": "messaging", "linked_subject_id": "order_10231", "linked_source": "shopify"}]
}'5Read the verdict
Two lenses: outcome (did the intended change land?) and policy (was the action valid and within limits?). Each carries reason codes that name the missing or forbidden effect, the facts that decided it, and a hash chained to the previous verdict. UNKNOWN means the evidence to decide is not there yet; it is never counted as a failure.
curl https://api.commitlayer.ai/v1/actions/act_0001 -H "X-API-Key: $COMMITLAYER_RECORDER_KEY"
{
"status": "settled",
"lenses": {
"outcome": {"verdict": "FAIL", "reason_codes": ["expected_effect_missing:shopify.order_cancelled"], ...},
"policy": {"verdict": "FAIL", "reason_codes": ["expected_effect_missing:shopify.order_cancelled"], ...}
},
"checks": {
"preconditions": [{"description": "latest shopify.order_status before the action had status=eligible", "ok": true}],
"limits": [{"description": "amount at most 250", "ok": true}],
"expected_effects": [{"description": "stripe.refund_created observed within 15m", "ok": true},
{"description": "shopify.order_cancelled observed within 15m", "ok": false, "message": "Shopify order remained active despite a successful payment refund"}, ...],
"forbidden_effects": [{"description": "more than 1 stripe.refund_created", "ok": true}, ...]
},
"summary": {"expected": 4, "observed": 3, "headline": "FAIL — Shopify order remained active despite a successful payment refund"}
}Next
After the first verdict: dry runs, webhooks, the completion gate and settlement. Then the API reference for every endpoint.