Ecto schema for the mailglass_webhook_events table (V02 migration,
Plan 04-01). Mutable + prunable — UNLIKE mailglass_events which is
append-only via the SQLSTATE 45A01 trigger (CONTEXT D-15 split).
Stores raw webhook payloads from Postmark + SendGrid for:
- Idempotency: UNIQUE
(provider, provider_event_id)defends against replay (D-15 + PITFALLS MAIL-03). Plan 04-06'sMailglass.Webhook.Ingest.ingest_multi/3inserts withon_conflict: :nothing, conflict_target: [:provider, :provider_event_id]— a replay is a no-op SELECT-by-index, not an INSERT. - Audit: full raw payload available for support / debugging.
- GDPR erasure: targeted
DELETE FROM mailglass_webhook_events WHERE raw_payload->>'to' = ?without touching the append-only ledger (D-15).
The :raw_payload field is marked redact: true so Inspect output
(test failures, IEx) does NOT leak PII bytes. Mirrors accrue's
webhook_event.ex:48 convention.
Status state machine
:received → :processing → :succeeded | :failed → :dead. Plan 04-06
inserts at :processing and flips to :succeeded at the end of the
Multi; failures (outside Plan 04-06 scope) will surface the Plan 08
DLQ.
Summary
Functions
Closed set of valid :status atoms. Cross-checked in api_stability.md.
Builds a changeset for inserting a webhook_event row at ingest time.
Functions
Closed set of valid :status atoms. Cross-checked in api_stability.md.
@spec changeset(map()) :: Ecto.Changeset.t()
Builds a changeset for inserting a webhook_event row at ingest time.
Caller passes :provider, :provider_event_id, :event_type_raw,
:tenant_id, :raw_payload. Other fields default sensibly:
:statusdefaults to:processing(Plan 04-06 flips to:succeededafter the Multi commits):received_atdefaults toMailglass.Clock.utc_now/0