> For the complete documentation index, see [llms.txt](https://skymerse.gitbook.io/notamify-api/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://skymerse.gitbook.io/notamify-api/notam-watcher/watcher-api-webhook-messages.md).

# Watcher API Webhook messages

The Notamify [NOTAM Watcher API](/notamify-api/notam-watcher/notam-watcher-api.md) sends a JSON `POST` request to the configured `webhook_url`. The `notam` object follows the same NOTAM DTO used by the [NOTAMs Active endpoint](/notamify-api/active-notams-endpoint.md) and other NOTAM endpoints, with additional Watcher metadata.

Messages are signed with webhook secret, read more: [Webhook Security](/notamify-api/notam-watcher/webhook-security.md).

***

### Message Types

Watcher can send two production webhook message types:

1. `kind = "interpretation"` Standard interpreted NOTAM delivery.
2. `kind = "lifecycle"` Additional lifecycle delivery when a NOTAM previously delivered to this listener is later cancelled (`NOTAMC`) or replaced (`NOTAMR`).

### Top-Level Fields

* `listener_id` Listener ID that received the message.
* `kind` Message type. Values:
  * `interpretation`
  * `lifecycle`
* `event_id` Unique event identifier that can be used for deduplication.
* `notam` The NOTAM payload. This follows the same Notam DTO structure used by other Notamify NOTAM endpoints.
* `change` Present only for `kind = "lifecycle"`. Describes which previously delivered NOTAM changed. Fields:
  * `changed_notam_id`: ID of the old NOTAM that was cancelled or replaced
  * `notam_type`: `C` or `R`
* `context` Optional additive metadata for the resolved location.
* `sent_at` UTC timestamp when Watcher sent the webhook.

### `notam` Object

The `notam` object uses the normal Notamify Notam DTO shape.

Notes:

* For `NOTAMR`, `notam` is the replacement NOTAM.
* For `NOTAMC`, `notam` is the cancellation NOTAM.
* Lifecycle `C/R` does not contain `interpretation`.

### `context.location`

`context.location` is optional location metadata included when Notamify can provide relevant context.

Fields:

* `ident`
* `icao`
* `iata_code`
* `name`
* `iso_country`
* `iso_country_name`
* `elevation_ft`
* `coordinates`
* `fir_icaos`

<details>

<summary>Coordinates Meaning</summary>

`context.location.coordinates` are reference coordinates for the resolved location record.

* For an airport location, they represent the airport reference point or airport center.
* For an FIR location, they represent the geographic center of the FIR.

These coordinates are contextual only. They are not the NOTAM geometry.

If you need the actual NOTAM geometry, use:

* `notam.interpretation.map_elements`

That is where obstacle points, polygons, lines, and altitude limits are represented.

</details>

### Parsing Webhook Messages with the Python SDK

The [Notamify Python SDK](https://github.com/skymerse/notamify-sdk-python) provides a typed `WatcherWebhookEvent` model for parsing incoming webhook payloads:

```python
from notamify_sdk import WatcherWebhookEvent

# Parse the incoming JSON payload
event = WatcherWebhookEvent.model_validate_json(raw_body)

print(f"Listener: {event.listener_id}")
print(f"Kind: {event.kind}")  # "interpretation" or "lifecycle"
print(f"NOTAM: {event.notam.notam_number}")

if event.kind == "interpretation":
    print(f"Description: {event.notam.interpretation.description}")
    print(f"Category: {event.notam.interpretation.category}")

if event.kind == "lifecycle" and event.change:
    print(f"Changed NOTAM: {event.change.changed_notam_id}")
    print(f"Change type: {event.change.notam_type}")  # "C" or "R"

if event.context:
    loc = event.context.location
    print(f"Airport: {loc.name} ({loc.icao})")
```

### Interpretation Message Example

Example production `interpretation` message:

```json
{
  "listener_id": "11111111-2222-4333-8444-555555555555",
  "kind": "interpretation",
  "event_id": "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee",
  "notam": {
    "id": "12345678-1234-4123-8123-123456789abc",
    "notam_number": "A1727/26",
    "notam_type": "N",
    "location": "CVG",
    "icao_code": "KCVG",
    "qcode": "QOBCE",
    "classification": "INTL",
    "starts_at": "2026-03-09T11:00:00Z",
    "ends_at": "2026-03-13T23:00:00Z",
    "issued_at": "2026-03-06T20:16:00Z",
    "is_estimated": false,
    "is_permanent": false,
    "message": "Example NOTAM text.",
    "icao_message": "Example ICAO NOTAM text.",
    "interpretation": {...}
  },
  "context": {
    "location": {
      "ident": "KCVG",
      "icao": "KCVG",
      "iata_code": "CVG",
      "name": "Example Airport",
      "iso_country": "US",
      "elevation_ft": 896,
      "coordinates": {
        "lat": 39.048801,
        "lon": -84.667801
      },
      "fir_icaos": ["KZID"]
    }
  },
  "sent_at": "2026-03-06T20:18:08.626671414Z"
}
```

### Lifecycle Message Example

#### Example `lifecycle` message for a replacement (`NOTAMR`):

```json
{
  "listener_id": "11111111-2222-4333-8444-555555555555",
  "kind": "lifecycle",
  "event_id": "bbbbbbbb-cccc-4ddd-8eee-ffffffffffff",
  "notam": {
    "id": "87654321-4321-4321-8321-cba987654321",
    "notam_number": "A0052/26",
    "notam_type": "R",
    "location": "HTDA",
    "icao_code": "HTDA",
    "qcode": "QSPXX",
    "classification": "INTL",
    "starts_at": "2026-03-06T12:05:00Z",
    "ends_at": "2026-03-20T15:30:00Z",
    "issued_at": "2026-03-06T12:07:00Z",
    "is_estimated": true,
    "is_permanent": false,
    "message": "ATC TRG INPR AT APP FREQ 119.600MHZ AND 120.00MHZ . PILOTS TO BE PATIENT AND COOPERATIVE.",
    "icao_message": "A0052/26 NOTAMR A0031/26\nQ) HTDC/QSPXX/IV/BO/AE/000/999/0652S03912E005\nA) HTDA\nB) 2603061205 C) 2603201530 EST\nE) ATC TRG INPR AT APP FREQ 119.600MHZ AND 120.00MHZ . PILOTS TO BE PATIENT AND COOPERATIVE."
  },
  "change": {
    "changed_notam_id": "old-notam-uuid",
    "notam_type": "R"
  },
  "context": {
    "location": {
      "ident": "HTDA",
      "icao": "HTDA",
      "iata_code": "",
      "name": "Julius Nyerere International Airport",
      "iso_country": "TZ",
      "iso_country_name": "Tanzania, United Republic of",
      "elevation_ft": 182,
      "coordinates": {
        "lat": -6.878111,
        "lon": 39.202625
      },
      "fir_icaos": [
        "HTDC"
      ]
    }
  },
  "sent_at": "2026-03-06T20:20:00.000000000Z"
}
```

Interpretation of this lifecycle message:

* `kind = "lifecycle"` means this is not a normal interpretation delivery.
* `notam` is the new replacement NOTAM (`A0052/26`).
* `change.changed_notam_id` is the old NOTAM that was replaced.
* `change.notam_type = "R"` means replacement.

For cancellation messages, the structure is the same, but `change.notam_type` and `notam.notam_type` are `C`.

Important Notes

* `kind` and `event_id` are included in production interpretation and lifecycle webhook deliveries.
* Lifecycle messages are sent only when `lifecycle.enabled = true`.
* Lifecycle routing is based on prior successful production webhook delivery of the original NOTAM.
* Lifecycle webhook deliveries do not increment shipment credits.
* `filters.notam_type = ["R"]` means the delivered NOTAM itself is a replacement NOTAM. It does not mean the old replaced NOTAM is sent as the main `notam` object.

#### Example `lifecycle` message for a cancellation (`NOTAMC`):

```json
{
  "listener_id": "11111111-2222-4333-8444-555555555555",
  "kind": "lifecycle",
  "event_id": "cccccccc-dddd-4eee-8fff-000000000000",
  "notam": {
    "id": "cancel-uuid",
    "notam_number": "A1572/26",
    "notam_type": "C",
    "location": "EWR",
    "icao_code": "KEWR",
    "qcode": "QMRXX",
    "classification": "INTL",
    "starts_at": "2026-03-04T10:18:00Z",
    "ends_at": "2026-03-07T10:18:00Z",
    "issued_at": "2026-03-04T10:18:00Z",
    "is_estimated": false,
    "is_permanent": false,
    "message": "A1572/26 NOTAMC A1553/26 \nQ) KZNY/QMRXX/IV/NBO/A/000/999/4042N07410W005 \nA) KEWR\nB) 2603041018\nE)  EWR RWY 11/29 CLSD\nCANCELED",
    "icao_message": "A1572/26 NOTAMC A1553/26\nQ) KZNY/QMRXX/IV/NBO/A/000/999/4042N07410W005\nA) KEWR\nB) 2603041018\nE) EWR RWY 11/29 CLSD\nCANCELED"
  },
  "change": {
    "changed_notam_id": "old-notam-uuid",
    "notam_type": "C"
  },
  "context": {
    "location": {
      "ident": "KEWR",
      "icao": "KEWR",
      "iata_code": "EWR",
      "name": "Newark Liberty International Airport",
      "iso_country": "US",
      "iso_country_name": "United States of America",
      "elevation_ft": 18,
      "coordinates": {
        "lat": 40.6925,
        "lon": -74.168667
      },
      "fir_icaos": [
        "KZNY"
      ]
    }
  },
  "sent_at": "2026-03-06T20:25:00.000000000Z"
}
```

Interpretation of this cancellation lifecycle message:

* `kind = "lifecycle"` means this is not a normal interpretation delivery.
* `notam` is the cancellation NOTAM itself.
* `change.changed_notam_id` is the old NOTAM that was cancelled.
* `change.notam_type = "C"` means cancellation.

Important Notes

* `kind` and `event_id` are included in production interpretation and lifecycle webhook deliveries.
* Lifecycle messages are sent only when `lifecycle.enabled = true`.
* Lifecycle routing is based on prior successful production webhook delivery of the original NOTAM.
* Lifecycle webhook deliveries do not increment shipment credits.
* `filters.notam_type = ["R"]` means the delivered NOTAM itself is a replacement NOTAM. It does not mean the old replaced NOTAM is sent as the main `notam` object.
