Documentation Index
Fetch the complete documentation index at: https://docs.ratiofx.com/llms.txt
Use this file to discover all available pages before exploring further.
Settlement status is available two ways: synchronously in the POST /v1/execute response, and asynchronously via webhook callback. Use webhooks to drive downstream processing without polling.
Webhook setup
Provide your webhook endpoint URL during onboarding. Ratio sends HTTP POST requests to this URL when events occur.
| Configuration | Description |
|---|
| Webhook URL | Your HTTPS endpoint (e.g., https://yourapp.com/ratio/callback) |
| Webhook secret | HMAC-SHA256 key for verifying callback authenticity |
| Events | Settlement confirmations, quote expirations, system state changes |
Your webhook endpoint must:
- Accept
POST requests with Content-Type: application/json
- Return HTTP
200 within 5 seconds to acknowledge receipt
- Be idempotent — the same event may be delivered more than once
Return 200 immediately on receipt, then process the event asynchronously. This prevents timeouts from triggering Ratio’s retry logic.
Webhook events
settlement.confirmed
Fired when a swap settles on-chain. Includes full execution details.
{
"event": "settlement.confirmed",
"timestamp": "2026-02-27T10:00:32Z",
"data": {
"execution_id": "EX-9910-USD-IDR",
"quote_id": "QT-8821-USD-IDR",
"pair": "USD-IDR",
"status": "SETTLED",
"filled_rate": 16020.00,
"source_amount": 50000,
"destination_amount": 801000000,
"tx_hash": "0xKAIA...ABC",
"settled_at": "2026-02-27T10:00:32Z"
}
}
settlement.failed
Fired when a swap execution fails. This is rare and typically caused by an on-chain revert.
{
"event": "settlement.failed",
"timestamp": "2026-02-27T10:00:35Z",
"data": {
"execution_id": "EX-9911-USD-IDR",
"quote_id": "QT-8822-USD-IDR",
"pair": "USD-IDR",
"status": "FAILED",
"reason": "ON_CHAIN_REVERT"
}
}
quote.expired
Fired when a firm quote expires without being executed. Reserved inventory is released.
{
"event": "quote.expired",
"timestamp": "2026-02-27T10:01:15Z",
"data": {
"quote_id": "QT-8823-USD-IDR",
"pair": "USD-IDR",
"side": "BUY",
"amount": 50000,
"expiry_timestamp": "2026-02-27T10:01:15Z"
}
}
system.state_change
Fired when a corridor’s operating state changes (for example, from NORMAL to PROTECT).
{
"event": "system.state_change",
"timestamp": "2026-02-27T10:05:00Z",
"data": {
"corridor": "MYR-IDR",
"previous_state": "NORMAL",
"new_state": "PROTECT",
"allowed_directions": ["BUY", "SELL"]
}
}
Event types summary
| Event | Description |
|---|
settlement.confirmed | Swap has settled on-chain. Includes full execution details. |
settlement.failed | Swap execution failed (rare — typically due to on-chain revert). |
quote.expired | A firm quote expired without being executed. Inventory released. |
system.state_change | A corridor’s system state has changed. |
Signature verification
Every webhook request includes a signature header:
X-Ratio-Signature: sha256=<hex_digest>
Verify the signature before processing any event:
- Compute HMAC-SHA256 of the raw request body using your webhook secret.
- Compare the computed digest with the value in the
X-Ratio-Signature header.
- Reject the request if the signatures do not match.
Always verify the signature. Do not process webhook payloads that fail signature verification.
import hmac
import hashlib
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
Retry policy
If your endpoint does not return HTTP 200 within 5 seconds, Ratio retries with exponential backoff:
| Attempt | Delay |
|---|
| 1st retry | 10 seconds |
| 2nd retry | 30 seconds |
| 3rd retry | 2 minutes |
| 4th retry | 10 minutes |
| 5th retry | 1 hour |
After 5 failed attempts, the webhook is marked as failed. You can query settlement status directly via GET /v1/system/state as a fallback.
Best practices
- Verify the signature on every incoming request before processing the payload.
- Make your handler idempotent. The same event may be delivered more than once due to retries.
- Return
200 immediately, then process the event asynchronously to avoid triggering retries from slow processing.
- Log the
execution_id from every settlement.confirmed event for reconciliation and support escalation.