---
title: "Python SDK Type Reference"
canonical: "https://test.abhinandan.one/python-types-reference"
markdown_url: "https://test.abhinandan.one/python-types-reference.md"
publisher: "Primitive SDKs"
kind: "reference"
content_type: "reference"
category: "Python SDK"
description: "Lists every generated dataclass, enum, and TypedDict in primitivedotev's Python SDK, covering ReceivedEmail, auth verdicts, and webhook event types."
keywords: ["ReceivedEmail python", "EmailReceivedEvent primitive", "WEBHOOK_EVENT_TYPES", "PaymentEvent TypedDict", "ReceivedEmailAddress", "is_payment_settled_event"]
last_modified: "2026-08-21T18:22:43.359885+00:00"
published_at: "2026-08-11T18:55:08.554783+00:00"
source_files:
  - "sdk-python/src/primitive/events.py"
  - "sdk-python/src/primitive/received_email.py"
sections:
  - {anchor: "receivedemail-and-its-parts", title: "`ReceivedEmail` and its parts"}
  - {anchor: "receivedemailaddress", title: "`ReceivedEmailAddress`"}
  - {anchor: "receivedemailthread", title: "`ReceivedEmailThread`"}
  - {anchor: "helper-functions-that-operate-on-these-types", title: "Helper functions that operate on these types"}
  - {anchor: "webhook-event-type-catalog", title: "Webhook event-type catalog"}
  - {anchor: "event-type-tuples", title: "Event-type tuples"}
  - {anchor: "paymentevent-and-its-subtypes", title: "`PaymentEvent` and its subtypes"}
  - {anchor: "interactionevent", title: "`InteractionEvent`"}
  - {anchor: "type-guards", title: "Type guards"}
  - {anchor: "next-steps", title: "Next steps"}
---

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

# Python SDK Type Reference

Browse the generated dataclasses, enums, and TypedDicts the Python SDK uses to represent normalized emails, auth results, forward analysis, and webhook events.

Every type on this page is importable from the top-level `primitive` package unless otherwise noted. Types generated from the webhook JSON Schema (`EmailReceivedEvent`, `EmailAuth`, `EmailAnalysis`, `ForwardAnalysis`, and friends) are covered in depth on [Payment and Interaction Webhook Event Types](https://test.abhinandan.one/python-webhook-events/python-webhook-event-types.md); this page is the map of everything else: the normalized `ReceivedEmail` shape and the shared event-type catalog.

## `ReceivedEmail` and its parts

`primitive.receive(...)` and `primitive.normalize_received_email(...)` return a `ReceivedEmail` dataclass. It is the SDK-normalized representation of an inbound email; see [Receiving and Parsing Inbound Email](https://test.abhinandan.one/python-receive-email.md) for the full receive flow and field-by-field usage. This page documents only the dataclass shapes.

| Field | Type | Description |
|---|---|---|
| `id` | `str` | The inbound email's id. |
| `event_id` | `str` | The id of the webhook delivery (`event.id`) that produced this email. |
| `received_at` | `str` | ISO-8601 timestamp string from `email.received_at`. |
| `sender` | `ReceivedEmailAddress` | The From address, parsed strictly, falling back to the SMTP envelope sender if the header doesn't parse. |
| `reply_target` | `ReceivedEmailAddress` | The address a reply should go to: the first `Reply-To` entry if present and valid, else `sender`. |
| `received_by` | `str` | The first SMTP `RCPT TO` recipient. |
| `received_by_all` | `list[str]` | Every SMTP `RCPT TO` recipient. |
| `subject` | `str \| None` | The raw `Subject` header. |
| `reply_subject` | `str` | `subject` normalized with a `Re:` prefix (idempotent, doesn't double-prefix). |
| `forward_subject` | `str` | `subject` normalized with a `Fwd:` prefix (idempotent). |
| `text` | `str \| None` | The parsed plain-text body. |
| `thread` | `ReceivedEmailThread` | Threading headers extracted from the parsed email. |
| `attachments` | `list[WebhookAttachment]` | Parsed attachment metadata. |
| `auth` | `EmailAuth` | The SPF/DKIM/DMARC verdict fields, as delivered on the raw event. |
| `analysis` | `EmailAnalysis` | Server-computed email analysis (bounce/report classification, etc.). |
| `raw` | `EmailReceivedEvent` | The full validated `email.received` event this email was normalized from. |

```python
@dataclass(frozen=True)
class ReceivedEmail:
    id: str
    event_id: str
    received_at: str
    sender: ReceivedEmailAddress
    reply_target: ReceivedEmailAddress
    received_by: str
    received_by_all: list[str]
    subject: str | None
    reply_subject: str
    forward_subject: str
    text: str | None
    thread: ReceivedEmailThread
    attachments: list[WebhookAttachment]
    auth: EmailAuth
    analysis: EmailAnalysis
    raw: EmailReceivedEvent
```

### `ReceivedEmailAddress`

A single parsed address with an optional display name.

| Field | Type | Description |
|---|---|---|
| `address` | `str` | Lowercased email address. |
| `name` | `str \| None` | Display name, or `None` if the header carried none. Defaults to `None`. |

```python
@dataclass(frozen=True)
class ReceivedEmailAddress:
    address: str
    name: str | None = None
```

> **Note:** `sender` and `reply_target` are display-quality fields, not authorization anchors. To decide whether an email is really from a domain you trust, use [`is_trusted_sender`](https://test.abhinandan.one/python-sender-trust.md) instead of comparing these fields.

### `ReceivedEmailThread`

Threading headers extracted from the parsed email, used to build reply/forward headers.

| Field | Type | Description |
|---|---|---|
| `message_id` | `str \| None` | The `Message-Id` header of the inbound email. |
| `in_reply_to` | `list[str]` | Parsed `In-Reply-To` header values. |
| `references` | `list[str]` | Parsed `References` header values. |

```python
@dataclass(frozen=True)
class ReceivedEmailThread:
    message_id: str | None
    in_reply_to: list[str]
    references: list[str]
```

### Helper functions that operate on these types

| Function | Signature | Description |
|---|---|---|
| `receive` | `receive(*, body, headers, secret, tolerance_seconds=None) -> ReceivedEmail` | Verifies the webhook signature, parses and validates the body, and normalizes it into a `ReceivedEmail` in one call. |
| `normalize_received_email` | `normalize_received_email(event: EmailReceivedEvent) -> ReceivedEmail` | Normalizes an already-validated `EmailReceivedEvent` into a `ReceivedEmail`. Raises `ValueError` if `email.smtp.rcpt_to` is empty. |
| `build_reply_subject` | `build_reply_subject(subject: str \| None) -> str` | Prefixes a subject with `Re:`, idempotently. Returns `"Re:"` for an empty/`None` subject. |
| `build_forward_subject` | `build_forward_subject(subject: str \| None) -> str` | Prefixes a subject with `Fwd:`, idempotently. Returns `"Fwd:"` for an empty/`None` subject. |
| `format_address` | `format_address(address: ReceivedEmailAddress) -> str` | Renders `"Name <addr>"` when `name` is set, else the bare address. |
| `parse_header_address` | `parse_header_address(value: str \| None) -> ReceivedEmailAddress \| None` | Parses a single RFC 5322 header address (From/Sender/Reply-To). Lenient about quirky headers but strict about the resulting address; returns `None` rather than a bad guess if nothing parseable is found. |

> **Warning:** `parse_header_address` is intentionally lenient for display purposes. It is not a safe authorization anchor, do not gate access decisions on its output. Use [`is_trusted_sender`](https://test.abhinandan.one/python-sender-trust.md) for that.

## Webhook event-type catalog

These live in `primitive.events` (also re-exported from the top-level `primitive` package for the guard functions and `WEBHOOK_EVENT_TYPES`). Full event-handling flow is documented on [Handling Webhook Events](https://test.abhinandan.one/python-webhook-events.md); this section is the type catalog reference. See also [Payment and Interaction Webhook Event Types](https://test.abhinandan.one/python-webhook-events/python-webhook-event-types.md) for the complete `PaymentEvent`/`InteractionEvent` reference.

The event name for every family arrives in the `X-Webhook-Event` HEADER, never in the body, the stored payload is sent verbatim with no envelope. An `email.*` body carries `event`, a `payment.*` body carries the name in `type`, and an `interaction.*` body is just `{"interaction": {...}}` with no event/type field at all. That's why every catalog and guard function below keys off the header-derived value, not a body field.

### Event-type tuples

| Name | Type | Contents |
|---|---|---|
| `EMAIL_EVENT_TYPES` | `tuple[str, ...]` | `email.received`, `email.bounced`, `email.tls_report`, `email.dmarc_report`, `email.dmarc_failure` |
| `PAYMENT_EVENT_TYPES` | `tuple[str, ...]` | `payment.settled`, `payment.failed` |
| `INTERACTION_EVENT_TYPES` | `tuple[str, ...]` | `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` | `tuple[str, ...]` | The union of all three tuples above, the full current catalog. |

`WebhookEventType` is a plain `str` type alias for any current catalog value, as carried in the `X-Webhook-Event` header.

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

Returns `True` if `event_type` is a value present in `WEBHOOK_EVENT_TYPES`.

### `PaymentEvent` and its subtypes

`PaymentEvent` is a `TypedDict` (`total=False`) representing a `payment.*` webhook body. The stored payload is flat, no envelope, no nested `payment` object, and carries the event name in `type`; the parser overlays a canonical `event` field (mirrored from the header) so consumers can branch on one field.

| Key | Type | Description |
|---|---|---|
| `event` | `Literal["payment.settled", "payment.failed"]` (read-only) | Canonical event name, overlaid from the `X-Webhook-Event` header. |
| `type` | `Literal["payment.settled", "payment.failed"]` (read-only) | 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 (`"base"` or `"base-sepolia"`). |
| `amount` | `str` | Amount in token base units (USDC has 6 decimals, so `"10000"` is 0.01). |
| `asset` | `str` | The checksummed token contract address. |
| `payer_org` | `str \| None` | The paying organization id, or `None` when not on-net. |

`PaymentSettledEvent` and `PaymentFailedEvent` are subclasses of `PaymentEvent` that narrow `event`/`type` to their respective literal, using `ReadOnly` (PEP 705) so a type checker rejects treating one as the other after a guard narrows it:

| Type | Adds | Field |
|---|---|---|
| `PaymentSettledEvent` | `event`/`type` narrowed to `"payment.settled"` | `settle_tx: str`, the on-chain settlement transaction hash |
| `PaymentFailedEvent` | `event`/`type` narrowed to `"payment.failed"` | `failure_reason: str`, human-readable failure reason |

### `InteractionEvent`

An `interaction.*` webhook body (`TypedDict`, `total=False`). The stored payload is just `{"interaction": {...}}` with no event/type field; the parser overlays a canonical `event` from the header.

| Key | Type | Description |
|---|---|---|
| `event` | `str` | Canonical event name, overlaid from the header (e.g. `interaction.x402.settled`). |
| `interaction` | `dict[str, Any]` | The interaction payload body. |
| `id` | `str` | Interaction id, when present. |

`InteractionX402Event` is a type alias for `InteractionEvent`, the same shape, named for the `interaction.x402.*` family specifically.

### Type guards

All guards accept `object` and narrow via `TypeGuard`, so they're safe to call on any parsed event value.

| 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."` |

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

event = handle_webhook_event(body=raw_body, headers=headers, secret=secret)

if is_payment_settled_event(event):
    print(event["challenge_id"], event["amount"], event["settle_tx"])
elif is_interaction_x402_event(event):
    print(event["event"], event["interaction"])
```
