Documentation Index: Fetch llms.txt first to discover every published page. This page is also available as Markdown at /python-types-reference.md.
Verified · 8/11/2026

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.

FieldTypeDescription
idstrThe inbound email's id.
event_idstrThe id of the webhook delivery (event.id) that produced this email.
received_atstrISO-8601 timestamp string from email.received_at.
senderReceivedEmailAddressThe From address, parsed strictly, falling back to the SMTP envelope sender if the header doesn't parse.
reply_targetReceivedEmailAddressThe address a reply should go to: the first Reply-To entry if present and valid, else sender.
received_bystrThe first SMTP RCPT TO recipient.
received_by_alllist[str]Every SMTP RCPT TO recipient.
subjectstr | NoneThe raw Subject header.
reply_subjectstrsubject normalized with a Re: prefix (idempotent, doesn't double-prefix).
forward_subjectstrsubject normalized with a Fwd: prefix (idempotent).
textstr | NoneThe parsed plain-text body.
threadReceivedEmailThreadThreading headers extracted from the parsed email.
attachmentslist[WebhookAttachment]Parsed attachment metadata.
authEmailAuthThe SPF/DKIM/DMARC verdict fields, as delivered on the raw event.
analysisEmailAnalysisServer-computed email analysis (bounce/report classification, etc.).
rawEmailReceivedEventThe 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.

FieldTypeDescription
addressstrLowercased email address.
namestr | NoneDisplay name, or None if the header carried none. Defaults to None.
@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 instead of comparing these fields.

ReceivedEmailThread#

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

FieldTypeDescription
message_idstr | NoneThe Message-Id header of the inbound email.
in_reply_tolist[str]Parsed In-Reply-To header values.
referenceslist[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#

FunctionSignatureDescription
receivereceive(*, body, headers, secret, tolerance_seconds=None) -> ReceivedEmailVerifies the webhook signature, parses and validates the body, and normalizes it into a ReceivedEmail in one call.
normalize_received_emailnormalize_received_email(event: EmailReceivedEvent) -> ReceivedEmailNormalizes an already-validated EmailReceivedEvent into a ReceivedEmail. Raises ValueError if email.smtp.rcpt_to is empty.
build_reply_subjectbuild_reply_subject(subject: str | None) -> strPrefixes a subject with Re:, idempotently. Returns "Re:" for an empty/None subject.
build_forward_subjectbuild_forward_subject(subject: str | None) -> strPrefixes a subject with Fwd:, idempotently. Returns "Fwd:" for an empty/None subject.
format_addressformat_address(address: ReceivedEmailAddress) -> strRenders "Name <addr>" when name is set, else the bare address.
parse_header_addressparse_header_address(value: str | None) -> ReceivedEmailAddress | NoneParses 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 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#

NameTypeContents
EMAIL_EVENT_TYPEStuple[str, ...]email.received, email.bounced, email.tls_report, email.dmarc_report, email.dmarc_failure
PAYMENT_EVENT_TYPEStuple[str, ...]payment.settled, payment.failed
INTERACTION_EVENT_TYPEStuple[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_TYPEStuple[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.

KeyTypeDescription
eventLiteral["payment.settled", "payment.failed"] (read-only)Canonical event name, overlaid from the X-Webhook-Event header.
typeLiteral["payment.settled", "payment.failed"] (read-only)The event name as carried in the raw stored body.
challenge_idstrThe x402 payment challenge this payment settles or fails.
networkstrSettlement network ("base" or "base-sepolia").
amountstrAmount in token base units (USDC has 6 decimals, so "10000" is 0.01).
assetstrThe checksummed token contract address.
payer_orgstr | NoneThe 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:

TypeAddsField
PaymentSettledEventevent/type narrowed to "payment.settled"settle_tx: str, the on-chain settlement transaction hash
PaymentFailedEventevent/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.

KeyTypeDescription
eventstrCanonical event name, overlaid from the header (e.g. interaction.x402.settled).
interactiondict[str, Any]The interaction payload body.
idstrInteraction 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.

FunctionNarrows toTrue when
is_payment_event(event)PaymentEventevent["event"] is "payment.settled" or "payment.failed"
is_payment_settled_event(event)PaymentSettledEventevent["event"] == "payment.settled"
is_payment_failed_event(event)PaymentFailedEventevent["event"] == "payment.failed"
is_interaction_x402_event(event)InteractionEventevent["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#

Was this page helpful?

© Primitive SDKs

Powered by Browzer