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 an API key
Start free, then open Settings → API keys and create one named after the system that will call the API. The key is shown once. Send it as X-API-Key on every request; keys are scoped to your workspace.
export COMMITLAYER_API_KEY=cl_live_... # from Settings → API keys curl https://api.commitlayer.ai/v1/contracts -H "X-API-Key: $COMMITLAYER_API_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, and the effects that must never appear. The library ships refund_workflow; select it as-is for the first run and edit it once the flow works.
curl -X POST https://api.commitlayer.ai/v1/contracts/refund_workflow/select -H "X-API-Key: $COMMITLAYER_API_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}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_API_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_API_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_API_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"}
}After the first verdict
Try before you store
POST /v1/actions/verify with dry_run: true evaluates an action and facts inside a transaction that is rolled back: nothing is stored and nothing is metered. Use it in tests and in the playground under Actions → Verify.
curl -X POST https://api.commitlayer.ai/v1/actions/verify -H "X-API-Key: $COMMITLAYER_API_KEY" -H "Content-Type: application/json" \
-d '{"dry_run": true, "action": {...}, "facts": [...]}'Get told when a verdict settles
Subscribe a webhook to action.verified under Settings → Webhooks. Every settled verdict is delivered with the standard envelope and an HMAC-SHA256 signature; Slack alerts use the same event.
{"id": "evt_...", "event": "action.verified", "created_at": "...",
"data": {"action_id": "act_0001", "contract_id": "refund_workflow", "outcome": "FAIL", "policy": "FAIL", "verdict_hash": "sha256:..."}}Record from OpenTelemetry instead
On the Scale plan the agent runtime can emit spans and CommitLayer records the action from them, with no API call in the agent's code. Internal agents → OpenTelemetry in the app shows the attributes to set.
Catch actions nobody recorded
GET /v1/actions/orphans?contract_id=refund_workflow lists effects of the kinds the contract watches that appeared in your systems with no recorded action behind them: the wrong-customer and injected-instruction cases. Growth plan and above.
Reference
All endpoints take X-API-Key (or a session) and return JSON. Errors are { error: { code, message, details } }; a plan limit answers 402 with the plan that unlocks it. Reason codes and verdict semantics are in the methodology.
| Method | Path | What it does |
|---|---|---|
| POST | /v1/actions | Record an action (idempotent on action_id); returns a provisional verdict |
| POST | /v1/facts | Push facts and subject links from systems of record |
| POST | /v1/actions/verify | Verify a recorded action, or an inline action and facts; dry_run available |
| GET | /v1/actions/{action_id} | The action with both lenses, checks, facts and hashes |
| GET | /v1/actions | List by contract, verdict and period, cursor-paginated |
| GET | /v1/actions/summary | PASS / FAIL / UNKNOWN / pending counts per workflow for a period |
| GET | /v1/actions/orphans | Effects with no recorded action behind them |
| GET | /v1/contracts | Library and workspace contracts |
| POST | /v1/contracts | Create a new version from YAML (validated) |
| POST | /v1/contracts/{id}/select | Make a contract active for the workspace |
| POST | /v1/contracts/{id}/dry-run | Evaluate a contract version over a period without storing anything |
| GET | /v1/public/verify/{hash} | Confirm a verdict hash exists and its chain is intact; no key needed |