> ## 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.

# Webhooks

> Event types, payloads, and signature verification for Ratio webhooks.

Ratio sends HTTP POST callbacks to your registered webhook URL when events occur. Use webhooks for real-time event-driven integration without polling.

## Configuration

| Setting     | Details                                              |
| ----------- | ---------------------------------------------------- |
| **URL**     | Your HTTPS endpoint, configured during onboarding    |
| **Secret**  | HMAC-SHA256 key used for signature verification      |
| **Timeout** | 5 seconds — return HTTP `200` to acknowledge receipt |

## Event types

| Event                  | Trigger                                 |
| ---------------------- | --------------------------------------- |
| `settlement.confirmed` | Swap settled on-chain successfully      |
| `settlement.failed`    | Swap execution failed (on-chain revert) |
| `quote.expired`        | Firm quote expired without execution    |
| `system.state_change`  | A corridor's system state changed       |

## Payloads

### settlement.confirmed

Sent when a swap is confirmed on-chain.

```json theme={null}
{
  "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

Sent when a swap execution fails due to an on-chain revert.

```json theme={null}
{
  "event": "settlement.failed",
  "timestamp": "2026-02-27T10:00:33Z",
  "data": {
    "execution_id": "EX-9911-USD-IDR",
    "quote_id": "QT-8822-USD-IDR",
    "pair": "USD-IDR",
    "status": "FAILED",
    "reason": "ON_CHAIN_REVERT",
    "tx_hash": "0xKAIA...DEF"
  }
}
```

### quote.expired

Sent when a firm quote reaches its `expiry_timestamp` without being executed.

```json theme={null}
{
  "event": "quote.expired",
  "timestamp": "2026-02-27T10:01:00Z",
  "data": {
    "quote_id": "QT-8823-USD-SGD",
    "pair": "USD-SGD",
    "expired_at": "2026-02-27T10:00:45Z"
  }
}
```

### system.state\_change

Sent when a corridor transitions between system states.

```json theme={null}
{
  "event": "system.state_change",
  "timestamp": "2026-02-27T10:05:00Z",
  "data": {
    "corridor": "MYR-IDR",
    "previous_state": "NORMAL",
    "new_state": "PROTECT",
    "reason": "VOLATILITY_ELEVATED"
  }
}
```

## Signature verification

Every webhook request includes a signature header you must verify before processing the payload:

```
X-Ratio-Signature: sha256=<hex_digest>
```

Compute the HMAC-SHA256 of the raw request body using your webhook secret, then compare the result against the header value. Reject any request where the signature does not match.

<CodeGroup>
  ```python Python theme={null}
  import hmac, hashlib

  def verify(body: bytes, signature: str, secret: str) -> bool:
      expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
      return hmac.compare_digest(f"sha256={expected}", signature)
  ```

  ```javascript JavaScript theme={null}
  const crypto = require('crypto');

  function verify(body, signature, secret) {
    const expected = crypto.createHmac('sha256', secret).update(body).digest('hex');
    return signature === `sha256=${expected}`;
  }
  ```
</CodeGroup>

## 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. Query the execution status directly via the API as a fallback.

## Best practices

<Tip>
  Always verify the `X-Ratio-Signature` header before processing any payload. Discard requests that fail verification.
</Tip>

* **Make your handler idempotent.** The same event may be delivered more than once. Use `execution_id` or `quote_id` to deduplicate.
* **Return `200` immediately, then process asynchronously.** Your handler must respond within 5 seconds. Offload any slow processing to a queue or background task.
* **Log the full payload.** Retain raw payloads for debugging and reconciliation.
