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

# Webhook events

> The events a webhook sends and the fields in each payload.

A webhook sends three kinds of event: `compliance.violation`, `compliance.resolved` and the monitor events. Each is a JSON body with an `event` field. [Webhooks](https://docs.allocait.app/guides/webhooks.md) explains when they fire and how to set up an endpoint.

## Events

### `compliance.violation`

Sent when at least one rule is newly breached. Each list carries only the new breaches: `violations` for holdings in a class outside its band, `class_violations` for the class totals themselves, and `size_violations` for holdings outside their size range. Any list can be empty.

Each breached row carries `outside_by`: `edge` says which side of the band or range the figure is on (`above_max` or `below_min`), and `pct_points` says how far outside it is, in percentage points of portfolio value. On a `violations` row it describes the holding's class, so every row of one class repeats the same figure. `outside_by` has no dollar amount, share count or side to take. It's a fact about your own rule, worked out from Allocait's stored closing prices.

A `violations` row mixes two shares. `current_pct` and `holding_pct` are the holding's own share of portfolio value (the same number), and `class_pct` is the share of its whole class. `status`, `min_pct`, `max_pct` (repeated as `band_min_pct` and `band_max_pct`) and `outside_by` belong to the class, so `class_pct` minus `max_pct` gives `outside_by.pct_points`, and `current_pct` minus `max_pct` doesn't. `priced_count` and `unpriced_count` count the class's holdings with and without a stored closing price, and `prices_complete` is `true` when none is missing. A `class_violations` row has the same counts, and its `current_pct` is the class's share.

A band or size status allows a tolerance of 0.01 of a point. A figure within 0.01 of an edge counts as inside, and `pct_points` is reported as measured, without the tolerance taken off.

A row carries no price and no dollar figure, because the check runs on those stored prices. It keeps its status, the percentages it was judged on, and the band or size range. For dollars, run a compliance check with your own prices.

A `size_violations` row carries `side`, `long` or `short`, from the sign of the holding's shares. A short's `current_pct` is its size without the minus sign, and its range is the class's short size range. A holding that stays outside its range but changes side between two checks is not announced again.

Illustrative example. The figures are hypothetical, not a recommendation.

```json theme={null}
{
  "event": "compliance.violation",
  "portfolio_id": "uuid",
  "scenario_id": "uuid",
  "timestamp": "2026-04-22T13:30:00.000Z",
  "violations": [
    {
      "symbol": "ALFA",
      "asset_class": "Equities",
      "status": "ABOVE_MAX",
      "priced_count": 2,
      "unpriced_count": 0,
      "prices_complete": true,
      "current_pct": 52.196,
      "holding_pct": 52.196,
      "class_pct": 74.3385,
      "min_pct": 31,
      "max_pct": 59,
      "band_min_pct": 31,
      "band_max_pct": 59,
      "outside_by": { "edge": "above_max", "pct_points": 15.3385 }
    },
    {
      "symbol": "BRAV",
      "asset_class": "Equities",
      "status": "ABOVE_MAX",
      "priced_count": 2,
      "unpriced_count": 0,
      "prices_complete": true,
      "current_pct": 22.1426,
      "holding_pct": 22.1426,
      "class_pct": 74.3385,
      "min_pct": 31,
      "max_pct": 59,
      "band_min_pct": 31,
      "band_max_pct": 59,
      "outside_by": { "edge": "above_max", "pct_points": 15.3385 }
    }
  ],
  "class_violations": [
    {
      "asset_class_id": "uuid",
      "asset_class": "Equities",
      "status": "ABOVE_MAX",
      "priced_count": 2,
      "unpriced_count": 0,
      "prices_complete": true,
      "current_pct": 74.3385,
      "min_pct": 31,
      "max_pct": 59,
      "band_min_pct": 31,
      "band_max_pct": 59,
      "outside_by": { "edge": "above_max", "pct_points": 15.3385 }
    }
  ],
  "size_violations": [
    {
      "symbol": "ALFA",
      "asset_class_id": "uuid",
      "asset_class": "Equities",
      "status": "ABOVE_MAX",
      "current_pct": 52.196,
      "size_min_pct": 5,
      "size_max_pct": 45,
      "outside_by": { "edge": "above_max", "pct_points": 7.196 },
      "side": "long"
    }
  ]
}
```

### `compliance.resolved`

Sent when one or more entries from the last check no longer appear in this one. All three lists are always present, and any can be empty:

* `resolved_symbols` lists holdings that no longer appear in `violations` because their class is back inside its band, or the class no longer has a band. A holding that left `violations` while its class is still outside its band (sold out of it, for example) is not listed here. It is listed on the night its class is back inside, and the class is in `resolved_classes` that same night.
* `resolved_classes` lists asset class ids whose total no longer appears in `class_violations`.
* `resolved_size_symbols` lists holdings that no longer appear in `size_violations`.

If any holding has no price that evening, Allocait announces nothing as resolved, and the next complete check sends it.

Illustrative example. The figures are hypothetical, not a recommendation.

```json theme={null}
{
  "event": "compliance.resolved",
  "portfolio_id": "uuid",
  "scenario_id": "uuid",
  "timestamp": "2026-04-22T13:30:00.000Z",
  "resolved_symbols": ["ALFA", "ECHO"],
  "resolved_classes": ["uuid"],
  "resolved_size_symbols": ["BRAV"]
}
```

### Monitor events

`monitor.confirmed`, `monitor.retraced` and `monitor.cleared` report the daily close against a level you recorded on a monitor. `monitor.confirmed` means a close settled through the level. `monitor.retraced` means the day's low or high went through the level but the close settled back. `monitor.cleared` reports that a close cleared a break that had been confirmed. [Monitors](https://docs.allocait.app/concepts/monitors.md) explains the modes, the levels and what a break means. A `NEAR` monitor sends none of these events.

Allocait compares each new daily close with the level on the monitor, plus its confirmation buffer (`buffer_pct`, a percent). No screen or API sets a buffer, so it reads 0. A `RANGE` monitor uses both levels. `consecutive_closes` counts the closes in a row that have settled through the level, and it is 0 on a `monitor.cleared` event.

Each payload carries `event`, `portfolio_id`, `monitor_id`, `monitor_name`, `symbol`, `comparison_mode`, `session_date`, `close`, `level_1`, `level_2` (null when the monitor has one level), `buffer_pct`, `consecutive_closes` and `timestamp`.

Illustrative example. The figures are hypothetical, not a recommendation.

```json theme={null}
{
  "event": "monitor.confirmed",
  "portfolio_id": "uuid",
  "monitor_id": "uuid",
  "monitor_name": "Levels",
  "symbol": "ALFA",
  "comparison_mode": "BELOW",
  "session_date": "2026-04-22",
  "close": 93.5,
  "level_1": 97,
  "level_2": null,
  "buffer_pct": 0,
  "consecutive_closes": 1,
  "timestamp": "2026-04-22T21:40:00.000Z"
}
```

A monitor event is stored when the close check finds it. If no endpoint accepts it and a failure could clear by itself, Allocait sends it again after each following weekday's run for up to seven days, with the same `session_date`, `close` and levels and a new `timestamp`. Identify a repeat by `event`, `monitor_id`, `symbol` and `session_date`.

## Next steps

> [card] [Verify deliveries](https://docs.allocait.app/api/webhook-verification.md)
>
> Check the signature on each delivery.

> [card] [Webhooks](https://docs.allocait.app/guides/webhooks.md)
>
> When events fire, retries and endpoint setup.

> [card] [Monitors](https://docs.allocait.app/concepts/monitors.md)
>
> What the monitor events report.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.