> ## Documentation Index
> Fetch the complete documentation index at: https://developers.fireblocks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Compliance Events & Webhooks

> How Fireblocks Identity & Compliance screening outcomes reach your systems through webhooks: which events to subscribe to, how to read them, and how to handle them reliably.

Compliance events are how your systems learn what screening decided, so you can build your own flows on top of it: notify your compliance team, update your back office, or decide what to do with a rejected or frozen transaction. Fireblocks sends an event at every status change in the transaction lifecycle, including each screening outcome. See [About Identity & Compliance](/docs/identity-and-compliance-overview) for where screening runs in that lifecycle.

## Which events to subscribe to

| What you want to know | Event | Subscription |
| - | - | - |
| A transaction started screening, was rejected, or was frozen, for any check | `transaction.status.updated` | Webhooks v2. In Webhooks v1, this event is `TRANSACTION_STATUS_UPDATED`. |
| A Sumsub or GTR Travel Rule message needs more data, or is missing | `TRLink.RequiredActions`, `TRLink.NoTRMessage` | The **Compliance** notification category, a separate subscription |
| An on/off-ramp order changed | `order.updated` | Webhooks v2 |

Bring Your Own Screening does not send its own event when a transaction starts waiting for your verdict. Watch for the transaction's `transaction.status.updated` events, or poll it.

## Step 1: Subscribe

* **Webhooks v2 events:** set up an endpoint and subscribe to the events you need. See [Webhooks Overview](/reference/webhooks-overview).
* **Compliance notifications:** in the Console, go to **Settings** > **Notifications** > **Webhooks** > **Create Webhook**, and select **Compliance**.

Verify each event's signature before processing it. See [Validating webhooks](/reference/validating-webhooks).

## Step 2: Read screening outcomes

The `data` object of `transaction.status.updated` is the full transaction. Read its `status` and `subStatus`:

| Outcome | What to read | Meaning |
| - | - | - |
| Screening | `status` is `PENDING_AML_SCREENING` | Screening is running. Despite the name, this covers every check: Address Registry Screening, AML/KYT, Bring Your Own Screening, and Travel Rule. |
| Rejected | `status` is `REJECTED`, `subStatus` is `REJECTED_AML_SCREENING` | A check rejected the transaction. The values are the same for every check. |
| Frozen (incoming) | `subStatus` is `AUTO_FREEZE` or `FROZEN_MANUALLY` | The Outcome (Post-Screening Policy) froze the funds, or someone froze them manually. |

See [Statuses](/reference/statuses) and [Sub-statuses](/reference/sub-statuses) for the full lists.

Because `subStatus` does not say which check made the decision, read the `complianceResults` object when the event includes it. It carries each check's provider, verdict, and risk level. For the full AML/KYT and Travel Rule result, including the raw provider payload, call [`GET /screening/transaction/{txId}`](/api-reference/compliance/provides-all-the-compliance-details-for-the-given-screened-transaction):

```ts theme={"system"}
app.post("/webhooks/fireblocks", async (req, res) => {
  const event = req.body;
  res.sendStatus(200); // acknowledge first, then process

  if (event.eventType !== "transaction.status.updated") return;

  const tx = event.data;
  if (tx.status === "REJECTED" && tx.subStatus === "REJECTED_AML_SCREENING") {
    const { data } = await fireblocks.compliance.getScreeningFullDetails({ txId: tx.id });
    await notifyCompliance(tx.id, data.aml?.verdict, data.aml?.risk);
  }
});
```

## Step 3: Handle Travel Rule notifications (Sumsub, GTR)

These are not Webhooks v2 events. They are audit-log notifications in the **Compliance** category (`categoryId: "7"`). You receive every notification in this category, and tell them apart by `note.messageType`.

`note` is a JSON-encoded string, so parse it before reading it. Only `screeningMessage` and `messageType` are always present; every other field appears only when the screening result has a value for it.

**Required actions.** When a Travel Rule message needs more data, you receive a `messageType` of `TRLink.RequiredActions`, once per destination:

| Field | Description |
| - | - |
| `trmId` | The Travel Rule message. |
| `customerIntegrationId` | The integration to call the resolve endpoints with. |
| `requiredActions` | The actions to take, each with a `type`, `description`, and `data`. Guaranteed non-empty. |
| `destinationScreeningId` | The destination this applies to. Present when the notification comes from destination screening; absent when it comes from an incoming Travel Rule message. When it is absent, deduplicate on the envelope `id` instead. It is for tracking and deduplication only: to link a message to a specific destination, match by `amount` and `destination`. |

**Missing message.** When a transaction that needs a Travel Rule message does not have one, you receive a `messageType` of `TRLink.NoTRMessage`, with the `txId`, `destinationScreeningId`, and destination details. Create and link a message, or accept or reject the transaction with a manual decision.

For both flows, see the [Sumsub](/docs/sumsub) and [GTR](/docs/gtr) guides.

## Step 4: Act on the outcome

* **Rejected or frozen transaction:** bypass, rescreen, or unfreeze it. See [Transaction Screening Operations](/docs/transaction-screening-operations).
* **Transaction waiting for your verdict:** submit `ACCEPT` or `REJECT`. See [Bring Your Own Screening](/reference/bring-your-own-screening-check-developer-guide).

## Handling events reliably

* **Acknowledge fast.** Return `200` right away, and process the event asynchronously.
* **Do not rely on order.** Fireblocks does not guarantee that events arrive in the order they happened. Compare against the transaction's current state rather than the previous event.
* **Deduplicate.** The same change can arrive more than once. Deduplicate on the notification `id`. For Travel Rule notifications, use `destinationScreeningId` when it is present, or the envelope `id` when it is not.
* **Re-fetch before acting.** Before bypassing, rescreening, unfreezing, or submitting a verdict, read the current state from the API. The transaction may have moved on.
* **Recover missed events.** Webhooks v2 can resend notifications for up to 30 days. See [Resending & troubleshooting webhook notifications](/reference/webhooks-resend-troubleshooting).

## On the Help Center

There is no Help Center equivalent for this guide. Console users see screening outcomes on the transaction itself.
