RailGuardDocs

Approvals

What happens between flagged and paid.

When a request flags

No rule blocks, but at least one returns review — usually the amount is above the auto-approve threshold. Response is HTTP 202, status: "flagged".

How the agent waits

  • SDK default: spend() blocks and polls every 5s for up to 1 hour, then raises SpendPending.
  • Non-blocking: spend(wait=False, webhook_url=…) returns immediately; resume on webhook or poll(id).
  • Raw API: GET poll_url until status is not flagged.

Who gets told

ChannelWhoSetup
In-appAll Admins and ApproversNone — bell icon, live updates
Slack / TeamsYour channelSettings → paste your own incoming-webhook link (Team plan+)

Signed decision webhook

After an approver decides, RailGuard POSTs to the request's webhook_url:

Legacy x-ledger-* copies of these headers are also sent for older SDK downloads.

headers
content-type: application/json
x-railguard-timestamp: 1790676000
x-railguard-signature: hex(HMAC_SHA256(key = sha256_hex(api_key), msg = timestamp + "." + raw_body))
body
{
  "event": "spend.decided",
  "transaction_id": "a91e…",
  "status": "approved",
  "payment_status": "issued",
  "comment": "OK for Q3 invoices"
}

Retry policy: one delivery attempt. If it fails, an audit row records the failure and the agent must poll. Build your agent to poll as the fallback. Verify with guardrail.verify_webhook(raw_body, timestamp, signature) (rejects older than 5 minutes).

Locked intent

An approval belongs to one transaction: its merchant, amount and rail. The card is issued for that transaction only. Approving $250 does not authorise $300 — a changed amount is a new request that is evaluated and, if flagged, approved again.