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; 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 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. |
@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. |
@dataclass(frozen=True)
class ReceivedEmailAddress:
address: str
name: str | None = None
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 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. |
@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. |
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 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; this section is the type catalog reference. See also Payment and Interaction Webhook Event Types 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.
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 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." |
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"])
Next steps#
Full reference for the payment and interaction event catalog, with per-field detail and guard usage.
Receiving and Parsing Inbound EmailSee the full receive flow that produces a ReceivedEmail, with worked examples.
Handling Webhook EventsDispatch every webhook event family with handle_webhook_event.
Python SDK Error ReferenceLook up the errors these types and helpers can raise.
Was this page helpful?