Skip to main content
This scenario shows a complete refund flow through the agent control plane.

Scenario and trust boundaries

Action: refund on POST /v1/refunds Systems in path:
  1. Agent runtime (signs request)
  2. Your backend service or gateway (verifies + enforces)
  3. Clawb control plane (/v1/verify, /v1/check, control-plane APIs)
  4. External payments service
Required inbound headers:
  • X-Clawb-Agent-Id
  • X-Clawb-Timestamp (milliseconds)
  • X-Clawb-Nonce
  • X-Clawb-Signature

1) Receive inbound signed request

Extract raw request fields without mutation:
  • method
  • exact path
  • raw body bytes
  • signature headers
These values are the source of truth for verification.

2) Verify identity

Use local verification or online /v1/verify:
curl -sS -X POST "https://api.clawb.ai/api/v1/verify" \
  -H "Content-Type: application/json" \
  -H "X-Clawb-Api-Key: ck_live_replace_me" \
  -d '{
    "agent_id": "agt_replace_me",
    "method": "POST",
    "path": "/v1/refunds",
    "timestamp_ms": 1740137855000,
    "nonce": "2f8d8b19-5e0a-4f8b-b7d4-6dc15b1fe201",
    "body_sha256": "3adfd3eb02f15d4f4b5a9f5b2d18f8d1b6d8a7eac03f4b7a56ec8f8c2f2ff321",
    "signature_b64": "<base64-signature>"
  }'
Example response:
{
  "valid": true,
  "agent_id": "agt_replace_me",
  "verified_at": "2026-02-28T00:00:00Z"
}

3) Request policy decision

curl -sS -X POST "https://api.clawb.ai/api/v1/check" \
  -H "Content-Type: application/json" \
  -H "X-Clawb-Api-Key: ck_live_replace_me" \
  -d '{
    "agent_id": "agt_replace_me",
    "policy_id": "pol_refunds_v2",
    "action": "refund",
    "context": {"amount": 249.00, "currency": "USD", "reason": "duplicate_charge"}
  }'
Example response:
{
  "decision": "allow",
  "trace_id": "trc_01abc",
  "reasons": []
}
Decision branches:
  • allow: continue
  • challenge: pause and route to approval
  • deny: block

4) Optional: mint short-lived credential for bounded execution

Use this when the follow-on service call should be explicitly time/scoped.
curl -sS -X POST "https://api.clawb.ai/api/v1/identity/credentials/mint" \
  -H "Content-Type: application/json" \
  -H "X-Clawb-Api-Key: ck_live_replace_me" \
  -d '{
    "agent_id": "agt_replace_me",
    "provider": "payments",
    "audience": "clawb.provider",
    "ttl_seconds": 120,
    "one_time": true,
    "scopes": ["refund:execute"]
  }'
Example response:
{
  "ok": true,
  "credential": {
    "cred_id": "crd_01...",
    "token_type": "jwt",
    "expires_at": "2026-02-28T00:02:00Z"
  }
}
Use the returned credential only for the bounded operation.

5) Execute and record audit context

At execution time, record:
  • agent_id
  • policy_id
  • decision
  • trace_id (if present)
  • downstream service response status
Query recent events:
curl -sS "https://api.clawb.ai/api/v1/workspace/audit/events?agent_id=agt_replace_me&limit=20" \
  -H "X-Clawb-Api-Key: ck_live_replace_me"
Example response:
{
  "ok": true,
  "items": [
    {
      "event_id": "evt_01...",
      "action": "refund",
      "decision": "allow",
      "trace_id": "trc_01abc"
    }
  ]
}

6) Emergency mode (incident branch)

If compromise is suspected, pause minting and revoke active credentials:
curl -sS -X POST "https://api.clawb.ai/api/v1/identity/kill-switch/minting" \
  -H "Content-Type: application/json" \
  -H "X-Clawb-Api-Key: ck_live_replace_me" \
  -d '{"paused": true, "reason": "incident INC-404"}'

curl -sS -X POST "https://api.clawb.ai/api/v1/identity/kill-switch/revoke-all" \
  -H "Content-Type: application/json" \
  -H "X-Clawb-Api-Key: ck_live_replace_me" \
  -d '{"reason": "incident INC-404"}'
Example response:
{
  "minting_paused": true,
  "all_credentials_revoked_at": "2026-02-28T00:00:00Z"
}

Unified branch pseudocode

if not verify_result.get("valid"):
    return {"status": 401, "error": "invalid_signature"}

decision_value = (decision.get("decision") or "").lower()
if decision_value == "deny":
    return {"status": 403, "error": "policy_denied", "trace_id": decision.get("trace_id")}
if decision_value == "challenge":
    return {"status": 403, "error": "challenge_required", "challenge": decision.get("challenge")}

# allow branch
try:
    # optional: mint bounded credential
    # execute downstream service action
    return {"status": 200, "ok": True}
except TimeoutError:
    # transport issue, not policy issue
    return {"status": 502, "error": "provider_unavailable"}

Error matrix

SymptomLikely causeRequired action
invalid signature / verify failsmethod/path/body/timestamp mismatchRecompute canonical inputs from raw request bytes and exact path.
timestamp_out_of_rangeseconds vs milliseconds or clock skewSend ms timestamps and sync server clocks.
policy_deniedpolicy explicitly blocks actionStop execution and return safe deny response.
policy_challengestep-up condition matchedTrigger approval workflow and retry only after completion.
429 / quota errorsrate/usage limits exceededBackoff + retry for safe/idempotent flows only.
kill switch pausedincident control activeDo not mint new credentials until incident cleared.

Verification checklist

  • Verify uses exact path and raw body hash.
  • /v1/check is called immediately before action execution.
  • allow, challenge, and deny branches are all tested.
  • Credential mint TTL/one-time behavior tested.
  • Audit query returns expected events.
  • Incident kill switch path tested in staging.