Events

The `Event` object and its typed accessors.

Your handler receives one Event per delivery. Branch on event.type, read data via the typed accessor (for webhook / email) or via the event.data dict (for slack / cron until typed accessors land in a future SDK release).

@client.on_event()
async def handle(event):
    if event.type == "webhook.received":
        handle_webhook(event.webhook)       # WebhookData | None
    elif event.type == "email.received":
        handle_email(event.email)           # EmailData | None
    elif event.type == "slack.event":
        handle_slack(event.data)            # dict — see Slack data below
    elif event.type == "cron.fired":
        handle_cron(event.data)             # dict — see Cron data below

Fields

AttributeTypeNotes
idstrULID. Stable across live + replay.
typestrwebhook.received, email.received, slack.event, or cron.fired.
originstrlive or replay. Use for idempotency.
created_atdatetimeTimezone-aware UTC.
channelChannel.id, .label, .type.
datadict[str, Any]Raw source payload — always populated.
prompt_readystrLLM-ready text rendering.
prompt_ready_versionintRenderer version stamp.
webhookWebhookData | NonePopulated when type == "webhook.received".
emailEmailData | NonePopulated when type == "email.received".

WebhookData

event.webhook.method          # "POST"
event.webhook.path            # "/v1/webhook/…"
event.webhook.headers         # dict[str, str]
event.webhook.body_encoding   # "json" | "text" | "base64"
event.webhook.body            # Any | None — JSON-decoded when encoding is "json"
event.webhook.body_raw_b64    # str — raw bytes base64-encoded, when "base64"
event.webhook.body_size_bytes # int

EmailData

event.email.from_             # str — (note the trailing underscore; `from` is a Python keyword)
event.email.to                # list[str]
event.email.cc                # list[str]
event.email.bcc               # list[str]
event.email.subject           # str
event.email.text              # str
event.email.html              # str
event.email.message_id        # str — RFC 5322 Message-ID; used for threading replies
event.email.attachments       # list[Attachment]
event.email.received_at       # datetime | None

Attachment

attachment.filename           # str
attachment.content_type       # str
attachment.size_bytes         # int
attachment.url                # str | None — signed, short-lived (future)

Slack data

For slack.event, the shape under event.data is:

event.data["team_id"]        # str — workspace id (e.g. "T0123…")
event.data["team_name"]      # str — optional
event.data["event_type"]     # str — "app_mention", "message", "reaction_added", etc.
event.data["event_ts"]       # str — Slack's per-event timestamp (also used for threading)
event.data["channel_id"]     # str — channel the message was in (for message-shaped events)
event.data["user_id"]        # str — author
event.data["text"]           # str — message text
event.data["thread_ts"]      # str — set when the message is already in a thread
event.data["raw"]            # dict — the full outer Slack event payload, preserved verbatim

Typed event.slack accessor is planned; for now, read the dict.

Cron data

For cron.fired, the shape under event.data is:

event.data["schedule_id"]    # str — the schedule's UUID
event.data["cron_expr"]      # str — e.g. "0 14 * * THU"
event.data["timezone"]       # str — IANA name, e.g. "America/New_York"
event.data["scheduled_at"]   # str — ISO-8601 UTC, what the schedule asked for
event.data["fired_at"]       # str — ISO-8601 UTC, when the scheduler actually ran
event.data["payload"]        # dict — the static JSON attached to the schedule

fired_at - scheduled_at tells you if the scheduler was lagged (rare; useful for observability).

Immutability

Event is a frozen dataclass. Safe to share across tasks, safe to cache, safe to hash.

Prompt-ready

A small convenience: event.prompt_ready is a pre-rendered text string useful when a model is the next hop. Ignore it if you don’t need it. See Prompt-ready.