---
title: "Payment and Interaction Webhook Event Types"
canonical: "https://test.abhinandan.one/python-webhook-events/python-webhook-event-types"
markdown_url: "https://test.abhinandan.one/python-webhook-events/python-webhook-event-types.md"
publisher: "Primitive SDKs"
kind: "reference"
content_type: "reference"
category: "Python SDK"
parent: "python-webhook-events"
description: "Lists every webhook event type in primitive.events and documents the PaymentEvent, PaymentSettledEvent, PaymentFailedEvent, and InteractionEvent TypedDicts plus their type guards."
keywords: ["WEBHOOK_EVENT_TYPES", "primitive.events", "PaymentEvent", "PaymentSettledEvent", "PaymentFailedEvent", "InteractionEvent"]
last_modified: "2026-08-11T18:55:03.455428+00:00"
published_at: "2026-08-11T18:55:00.831468+00:00"
source_files:
  - "sdk-python/src/primitive/events.py"
sections:
  - {anchor: "event-type-catalog", title: "Event type catalog"}
  - {anchor: "is_known_webhook_event_type", title: "`is_known_webhook_event_type`"}
  - {anchor: "paymentevent", title: "`PaymentEvent`"}
  - {anchor: "paymentsettledevent", title: "`PaymentSettledEvent`"}
  - {anchor: "paymentfailedevent", title: "`PaymentFailedEvent`"}
  - {anchor: "interactionevent", title: "`InteractionEvent`"}
  - {anchor: "type-guards", title: "Type guards"}
  - {anchor: "next-steps", title: "Next steps"}
---

> Documentation index: https://test.abhinandan.one/llms.txt

# Payment and Interaction Webhook Event Types

Reference for the WEBHOOK_EVENT_TYPES catalog and the typed PaymentEvent, PaymentSettledEvent, PaymentFailedEvent, and InteractionEvent TypedDicts exported from primitive.events, plus their type-guard functions.

The `primitive.events` module defines the full catalog of `X-Webhook-Event` header values and the typed `TypedDict` shapes for `payment.*` and `interaction.*` webhook bodies. For dispatching on these events end to end, see [Handling Webhook Events](https://test.abhinandan.one/python-webhook-events.md); for the general contract (signature verification, header discriminator, forward compatibility) see [Webhook Events Overview](https://test.abhinandan.one/webhook-events.md).

Every webhook delivery carries its event name in the `X-Webhook-Event` HEADER, not in the body. The stored body is sent verbatim with no envelope:

- an `email.*` body carries `event`
- a `payment.*` body carries the name in `type`
- an `interaction.*` body is just `{"interaction": {...}}` with no `event`/`type` field at all

Because of that, the header is the only reliable discriminator across all three families, which is why `handle_webhook_event` keys on it.

## Event type catalog

`WEBHOOK_EVENT_TYPES` is a `tuple[str, ...]` containing every current catalog value. It is assembled from three grouped tuples, each also exported:

```python
from primitive.events import (
    EMAIL_EVENT_TYPES,
    PAYMENT_EVENT_TYPES,
    INTERACTION_EVENT_TYPES,
    WEBHOOK_EVENT_TYPES,
)
```

| Group | Values |
| --- | --- |
| `EMAIL_EVENT_TYPES` | `email.received`, `email.bounced`, `email.tls_report`, `email.dmarc_report`, `email.dmarc_failure` |
| `PAYMENT_EVENT_TYPES` | `payment.settled`, `payment.failed` |
| `INTERACTION_EVENT_TYPES` | `interaction.ack.acked`, `interaction.ack.canceled`, `interaction.ack.expired`, `interaction.ack.received`, `interaction.ack.requested`, `interaction.x402.challenge`, `interaction.x402.declined`, `interaction.x402.expired`, `interaction.x402.payment`, `interaction.x402.rejected`, `interaction.x402.settled`, `interaction.x402.verify_timeout` |

`WEBHOOK_EVENT_TYPES` is the concatenation of all three, in that order. `WebhookEventType` is a plain `str` alias for "any current catalog value."

> **Note:** This catalog is a snapshot. New event types can appear on the wire before an SDK release documents them; unrecognized values are handled gracefully rather than rejected (see [Webhook Events Overview](https://test.abhinandan.one/webhook-events.md) for the forward-compatibility contract).

### `is_known_webhook_event_type`

```python
def is_known_webhook_event_type(event_type: str | None) -> bool
```

Returns `True` if `event_type` is a value currently present in `WEBHOOK_EVENT_TYPES`. Returns `False` for `None` and for any value outside the catalog, including future event types your installed SDK version predates.

```python
from primitive.events import is_known_webhook_event_type

is_known_webhook_event_type("payment.settled")       # True
is_known_webhook_event_type("interaction.ack.acked")  # True
is_known_webhook_event_type("payment.refunded")       # False (not yet in the catalog)
```

## `PaymentEvent`

A `payment.*` webhook body. `total=False`, so every key is optional at the type level; the fields actually present depend on which subtype (`PaymentSettledEvent` or `PaymentFailedEvent`) you're looking at.

The stored payload is **flat**: no envelope, no nested `payment` object. It carries the event name in `type`, and the SDK's parser overlays a canonical `event` key (mirrored from the `X-Webhook-Event` header) so consumers can branch on a single field.

| Field | Type | Notes |
| --- | --- | --- |
| `event` | `Literal["payment.settled", "payment.failed"]` (`ReadOnly`) | Canonical event name, overlaid from the header. |
| `type` | `Literal["payment.settled", "payment.failed"]` (`ReadOnly`) | The event name as carried in the raw stored body. |
| `challenge_id` | `str` | The [x402 payment challenge](https://test.abhinandan.one/x402-payments-overview.md) this payment settles or fails. |
| `network` | `str` | Settlement network, e.g. `"base"` or `"base-sepolia"`. |
| `amount` | `str` | Token base units (USDC has 6 decimals, so `"10000"` is 0.01 USDC). |
| `asset` | `str` | The checksummed token contract address. |
| `payer_org` | `str \| None` | Paying organization id, or `None` when not on-net. |

Fields are marked `ReadOnly` (PEP 705) so that a subclass can narrow the `Literal` type without a type-checker error, a mutable `TypedDict` field is invariant and cannot be narrowed in a subclass otherwise.

## `PaymentSettledEvent`

A `payment.settled` webhook event. Subclasses `PaymentEvent` and narrows `event` and `type` to `Literal["payment.settled"]`, so a type checker rejects treating it as a failed event once a guard has narrowed to it.

| Field | Type | Notes |
| --- | --- | --- |
|...all `PaymentEvent` fields | | `event` / `type` narrowed to `"payment.settled"` |
| `settle_tx` | `str` | The on-chain settlement transaction hash. |

## `PaymentFailedEvent`

A `payment.failed` webhook event. Subclasses `PaymentEvent` and narrows `event` and `type` to `Literal["payment.failed"]`.

| Field | Type | Notes |
| --- | --- | --- |
|...all `PaymentEvent` fields | | `event` / `type` narrowed to `"payment.failed"` |
| `failure_reason` | `str` | Human-readable reason the payment failed. |

## `InteractionEvent`

An `interaction.*` webhook body, covering the `interaction.x402.*` and `interaction.ack.*` families. The stored payload is just `{"interaction": {...}}` with no `event`/`type` field; the SDK's parser overlays a canonical `event` key from the header.

| Field | Type | Notes |
| --- | --- | --- |
| `event` | `str` | Canonical event name, overlaid from the header (e.g. `"interaction.x402.settled"`). |
| `interaction` | `dict[str, Any]` | The raw interaction payload. |
| `id` | `str` | Present on some interaction bodies. |

`InteractionX402Event` is an alias for `InteractionEvent`, exported for callers who want a name that matches the `interaction.x402.*` subset specifically. There is no separate TypedDict shape for it; the alias is purely for readability at call sites.

## Type guards

Each guard is a `TypeGuard` predicate over `object`, so it narrows an already-parsed event value (for example, the return of `handle_webhook_event`) without requiring a prior `isinstance` check.

| Function | Narrows to | True when |
| --- | --- | --- |
| `is_payment_event(event)` | `PaymentEvent` | `event["event"]` is `"payment.settled"` or `"payment.failed"` |
| `is_payment_settled_event(event)` | `PaymentSettledEvent` | `event["event"] == "payment.settled"` |
| `is_payment_failed_event(event)` | `PaymentFailedEvent` | `event["event"] == "payment.failed"` |
| `is_interaction_x402_event(event)` | `InteractionEvent` | `event["event"]` starts with `"interaction.x402."` |

All four accept a plain `dict` or any object exposing an `.event` attribute; internally they read via `event.get("event")` for dicts and `getattr(event, "event", None)` otherwise, returning `None` (and hence `False`) for anything that isn't a string.

```python
from primitive import handle_webhook_event
from primitive.events import is_payment_settled_event, is_interaction_x402_event

event = handle_webhook_event(
    body=raw_body,
    headers=request.headers,
    secret=os.environ["PRIMITIVE_WEBHOOK_SECRET"],
)

if is_payment_settled_event(event):
    # event["amount"] is in token base units
    print(event["challenge_id"], event["amount"], event["settle_tx"])
elif is_interaction_x402_event(event):
    print(event["event"], event["interaction"])
```

> **Note:** `is_interaction_x402_event` matches only the `interaction.x402.*` prefix. The `interaction.ack.*` events are also `InteractionEvent`-shaped on the wire, but there is no dedicated guard for them in this module; branch on `event["event"]` directly if you need to distinguish them.
