AUTH4D-301 user.deleted webhook event
This fragment extends AUTH4D-158 (repository file docs/api-spec/auth4d-158.md) and AUTH4D-216 (repository file docs/api-spec/auth4d-216.md). It adds a
sixth Flows event type, user.deleted, which webhook endpoints can subscribe to. No endpoint,
signature header or migration changes: webhook_endpoints.event_types is a JSON array and
fan-out matches types generically.
Event
FlowEventTypeSchema and FlowEventSchema in packages/contracts accept user.deleted. The
envelope is the same as for every other event. data is strict:
| Field | Type | Meaning |
|---|---|---|
data.reason |
"user_request" | "inactive_guest" |
Why the lifecycle deletion job removed the user (DeletionReason). |
{
"event_id": "evt_Q2xk3n1y0b9VqT7mH4sR8uWcE6aJpLzN5dGfXiKoYv0",
"version": 1,
"type": "user.deleted",
"tenant_id": "ten_0123456789abcdef",
"application_id": null,
"user_id": "usr_0123456789abcdef",
"occurred_at": "2026-10-17T02:10:04.512Z",
"context": {},
"data": { "reason": "user_request" }
}
When it is published
- The lifecycle deletion job (
packages/lifecyclepurgePhase) publishes it once, after the D1 batch that removes the user, identities, memberships, consent and session indexes has committed and completed the job. Only the worker whose fenced completion write succeeded publishes. - Nothing is published while a job is in the revoke phase, deferred by a late session revocation,
failed, or cancelled. Orphaned-user cleanup (
orphanedUserTask) publishes nothing. application_idis alwaysnullandcontextis{}, likemember.changed. Endpoints with an application filter therefore never receiveuser.deleted; only project-wide endpoints do.event_idis derived from the tenant, the type and the deletion job id, so re-publishing the same job yields the same id and consumers dedupe onwebhook-id.
Delivery
Delivery is best effort, as for every other event. LifecycleContext.events is optional; the API
Worker wires it in createScheduledTaskDispatcher to publishFlowEvent. When the port is
missing nothing is published. When it throws, the job still completes and a
lifecycle.publish_failed log line carries only type and tenant_id. The deletion phases, the
user.deleted audit event and the retention tasks are unchanged.
Console
The webhook form lists user.deleted as Player deleted and selects it by default for new
endpoints.
Source: docs/api-spec/auth4d-301.md