Python SDK · Event shape
The event model is a set of dataclasses:
@dataclass
class Event:
action: str # dotted verb, e.g. "user.lock"
id: str # uuid v4; auto-generated
occurred_at: datetime # auto-populated (UTC)
actor: Actor # who caused the event
tenant_id: str # optional; set when partitioning by your end-customer
target: Target # what was acted on
metadata: dict[str, Any] # freeform context
origin: Origin # IP, user-agent, request ID
result: Result # outcome
change: Change | None # before/after for mutations
idempotency_key: str # optional dedup key
@dataclass
class Actor: type: str; id: str; display_name: str; email: str
@dataclass
class Target: type: str; id: str
@dataclass
class Result: status: str; code: int; message: Any # status: "ok" | "error" | "denied"
@dataclass
class Origin: ip: str; user_agent: str; request_id: str
Fields use plain values with empty defaults (
"",0, empty nested dataclasses) rather than optionals: empty means "not set", and the serializer omits empty fields. Unlike the Node SDK, field names are snake_case everywhere and already match the JSON wire format.result.messageaccepts any value and special-cases exceptions (anExceptionserializes asstr(exc)). The JSON payload is byte-compatible with the Go and Node SDKs.