Skip to content

Webhooks

RFQs emit terminal events over the same webhook pipeline as v1: same registration endpoints, same signing secret, same Textile-Signature header, same retry behaviour. Register once and subscribe to whichever events you want.

Registration, signature verification and retries are documented in v1 webhooks and work identically — this page only covers what the RFQ events look like.

Events

EventFires when
rfq.filledThe swap settled on chain.
rfq.failedThe signed order's deadline passed and the reported transaction never landed (failReason: "not_landed").
rfq.expiredThe firm quote lapsed without being executed.

Register them like any other event type:

json
POST /v1/webhooks
{
  "url": "https://your-app.com/webhooks/textile",
  "eventTypes": ["rfq.filled", "rfq.failed", "rfq.expired"]
}

The event

json
{
  "id": "rfq.filled:rfq_…",
  "type": "rfq.filled",
  "created": "2026-08-07T12:00:09.000Z",
  "data": {
    "apiVersion": 2,
    "rfq": {
      "id": "rfq_…",
      "chainId": 56,
      "sellToken": "0xCNGN",
      "buyToken": "0xUSDT",
      "sellAmount": "999900010",
      "buyAmount": "612000000",
      "feeAmount": "99990",
      "taker": "0xYourWallet",
      "txHash": "0xabc…",
      "filledAt": "2026-08-07T12:00:09.000Z",
      "failReason": null
    }
  }
}

Two things worth pointing at:

  • apiVersion: 2 is on every RFQ event. v1 events don't carry it. If one handler serves both, branch on this rather than on the event name prefix.
  • The amounts are what settled, not what you asked for: sellAmount is the actual debit including the fee, buyAmount is what was delivered, and feeAmount is the fee that turned one into the other. On an exact-input RFQ the debit is often less than the cap you requested.

Ordering and corrections

Delivery is at-least-once and events are deduped on Textile-Event-Id, same as v1 — so dedupe on it.

The correction case is specific to RFQ and worth handling: a fill that indexes late can arrive after an rfq.failed or rfq.expired for the same RFQ, for up to 24 hours. When that happens rfq.filled is the authoritative outcome and supersedes the earlier event.

Don't treat the first terminal event as final if you also hold a transaction you know was broadcast. A safe handler:

  • keys on rfq.id,
  • lets rfq.filled overwrite an earlier failed/expired,
  • never lets failed/expired overwrite a filled.

Not having webhooks

If you'd rather poll, GET /v2/rfq/{id} reports the same states and the same settled amounts. Webhooks are just cheaper.