{"schema_version":"1.0","publisher":"Primitive SDKs","canonical_url":"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","article":{"id":"a01559d5-4b10-4a08-97d3-19867e5baa7f","article_slug":"python-webhook-event-types","parent_article_slug":"python-webhook-events","parent_article_title":"Handling Webhook Events","kind":"reference","published_at":"2026-08-11T18:55:00.831468+00:00","keywords":["WEBHOOK_EVENT_TYPES","primitive.events","PaymentEvent","PaymentSettledEvent","PaymentFailedEvent","InteractionEvent"],"meta_description":"Lists every webhook event type in primitive.events and documents the PaymentEvent, PaymentSettledEvent, PaymentFailedEvent, and InteractionEvent TypedDicts plus their type guards.","og_image_url":null,"source_file_paths":["sdk-python/src/primitive/events.py"],"recording_id":null,"replayable":false,"task_name":"Payment and Interaction Webhook Event Types","category":"Python SDK","summary":null,"description":"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.","content_kind":"repo_page","content_markdown":"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](python-webhook-events); for the general contract (signature verification, header discriminator, forward compatibility) see [Webhook Events Overview](webhook-events).\n\nEvery 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:\n\n- an `email.*` body carries `event`\n- a `payment.*` body carries the name in `type`\n- an `interaction.*` body is just `{\"interaction\": {...}}` with no `event`/`type` field at all\n\nBecause of that, the header is the only reliable discriminator across all three families, which is why `handle_webhook_event` keys on it.\n\n## Event type catalog\n\n`WEBHOOK_EVENT_TYPES` is a `tuple[str, ...]` containing every current catalog value. It is assembled from three grouped tuples, each also exported:\n\n```python\nfrom primitive.events import (\n    EMAIL_EVENT_TYPES,\n    PAYMENT_EVENT_TYPES,\n    INTERACTION_EVENT_TYPES,\n    WEBHOOK_EVENT_TYPES,\n)\n```\n\n| Group | Values |\n| --- | --- |\n| `EMAIL_EVENT_TYPES` | `email.received`, `email.bounced`, `email.tls_report`, `email.dmarc_report`, `email.dmarc_failure` |\n| `PAYMENT_EVENT_TYPES` | `payment.settled`, `payment.failed` |\n| `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` |\n\n`WEBHOOK_EVENT_TYPES` is the concatenation of all three, in that order. `WebhookEventType` is a plain `str` alias for \"any current catalog value.\"\n\n<Note>\n\nThis 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](webhook-events) for the forward-compatibility contract).\n\n</Note>\n\n### `is_known_webhook_event_type`\n\n```python\ndef is_known_webhook_event_type(event_type: str | None) -> bool\n```\n\nReturns `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.\n\n```python\nfrom primitive.events import is_known_webhook_event_type\n\nis_known_webhook_event_type(\"payment.settled\")       # True\nis_known_webhook_event_type(\"interaction.ack.acked\")  # True\nis_known_webhook_event_type(\"payment.refunded\")       # False (not yet in the catalog)\n```\n\n## `PaymentEvent`\n\nA `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.\n\nThe 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.\n\n| Field | Type | Notes |\n| --- | --- | --- |\n| `event` | `Literal[\"payment.settled\", \"payment.failed\"]` (`ReadOnly`) | Canonical event name, overlaid from the header. |\n| `type` | `Literal[\"payment.settled\", \"payment.failed\"]` (`ReadOnly`) | The event name as carried in the raw stored body. |\n| `challenge_id` | `str` | The [x402 payment challenge](x402-payments-overview) this payment settles or fails. |\n| `network` | `str` | Settlement network, e.g. `\"base\"` or `\"base-sepolia\"`. |\n| `amount` | `str` | Token base units (USDC has 6 decimals, so `\"10000\"` is 0.01 USDC). |\n| `asset` | `str` | The checksummed token contract address. |\n| `payer_org` | `str \\| None` | Paying organization id, or `None` when not on-net. |\n\nFields 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.\n\n## `PaymentSettledEvent`\n\nA `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.\n\n| Field | Type | Notes |\n| --- | --- | --- |\n|...all `PaymentEvent` fields | | `event` / `type` narrowed to `\"payment.settled\"` |\n| `settle_tx` | `str` | The on-chain settlement transaction hash. |\n\n## `PaymentFailedEvent`\n\nA `payment.failed` webhook event. Subclasses `PaymentEvent` and narrows `event` and `type` to `Literal[\"payment.failed\"]`.\n\n| Field | Type | Notes |\n| --- | --- | --- |\n|...all `PaymentEvent` fields | | `event` / `type` narrowed to `\"payment.failed\"` |\n| `failure_reason` | `str` | Human-readable reason the payment failed. |\n\n## `InteractionEvent`\n\nAn `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.\n\n| Field | Type | Notes |\n| --- | --- | --- |\n| `event` | `str` | Canonical event name, overlaid from the header (e.g. `\"interaction.x402.settled\"`). |\n| `interaction` | `dict[str, Any]` | The raw interaction payload. |\n| `id` | `str` | Present on some interaction bodies. |\n\n`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.\n\n## Type guards\n\nEach 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.\n\n| Function | Narrows to | True when |\n| --- | --- | --- |\n| `is_payment_event(event)` | `PaymentEvent` | `event[\"event\"]` is `\"payment.settled\"` or `\"payment.failed\"` |\n| `is_payment_settled_event(event)` | `PaymentSettledEvent` | `event[\"event\"] == \"payment.settled\"` |\n| `is_payment_failed_event(event)` | `PaymentFailedEvent` | `event[\"event\"] == \"payment.failed\"` |\n| `is_interaction_x402_event(event)` | `InteractionEvent` | `event[\"event\"]` starts with `\"interaction.x402.\"` |\n\nAll 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.\n\n```python\nfrom primitive import handle_webhook_event\nfrom primitive.events import is_payment_settled_event, is_interaction_x402_event\n\nevent = handle_webhook_event(\n    body=raw_body,\n    headers=request.headers,\n    secret=os.environ[\"PRIMITIVE_WEBHOOK_SECRET\"],\n)\n\nif is_payment_settled_event(event):\n    # event[\"amount\"] is in token base units\n    print(event[\"challenge_id\"], event[\"amount\"], event[\"settle_tx\"])\nelif is_interaction_x402_event(event):\n    print(event[\"event\"], event[\"interaction\"])\n```\n\n<Note>\n\n`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.\n\n</Note>\n\n## Next steps\n\n<CardGroup cols={2}>\n\n<Card title=\"Handling Webhook Events\" href=\"python-webhook-events\">\n\nParse and dispatch every webhook event family with handle_webhook_event.\n\n</Card>\n\n<Card title=\"Webhook Events Overview\" href=\"webhook-events\">\n\nThe shared signature-verification and forward-compatibility contract across all SDKs.\n\n</Card>\n\n<Card title=\"Creating and Paying Challenges\" href=\"python-x402-charge-and-pay\">\n\nCreate a payment challenge as a payee and settle it as a payer.\n\n</Card>\n\n<Card title=\"Python SDK Type Reference\" href=\"python-types-reference\">\n\nBrowse the generated dataclasses and enums used elsewhere in the SDK.\n\n</Card>\n\n</CardGroup>","canonical_base_url":"https://test.abhinandan.one","seo_indexing_enabled":true,"last_modified":"2026-08-21T18:22:43.359885+00:00","video_url":null,"voiceover_url":null,"tools_used":[],"demonstrated_by":[],"steps":[],"related_links":[],"intro":null,"prerequisites":[],"verification":[],"troubleshooting":[],"suggest_edit_url":"https://github.com/abhi-browzer/primitive-sdks/edit/main/sdk-python/src/primitive/events.py","raise_issue_url":"https://github.com/abhi-browzer/primitive-sdks/issues/new?title=Docs+feedback%3A+Payment+and+Interaction+Webhook+Event+Types&body=Page%3A+https%3A%2F%2Ftest.abhinandan.one%2Fpython-webhook-event-types","page_feedback_enabled":true,"verified_ref":null,"verified_at":"2026-08-11T18:38:45.205849+00:00"}}