# `Mailglass.Events.Event`
[🔗](https://github.com/szTheory/mailglass/blob/v0.1.0/lib/mailglass/events/event.ex#L1)

Append-only row in `mailglass_events`.

Exposes `changeset/1` for INSERTS only. There is NO update/delete
helper — the `mailglass_events_immutable_trigger` (Plan 02) raises
SQLSTATE 45A01 on any UPDATE/DELETE; `Mailglass.Repo.transact/1`
(Plan 01) translates to `Mailglass.EventLedgerImmutableError`.

Absence of an update helper also prevents code that looks like it
could work but blows up in production.

## Atom Sets

- `:type` — full Anymail taxonomy + mailglass internal `:dispatched`
  and `:suppressed` (D-14 project-level).
- `:reject_reason` — `:invalid | :bounced | :timed_out | :blocked |
  :spam | :unsubscribed | :other` (nullable; D-14).

## Relationships

`delivery_id` is a logical `:binary_id` reference — NO FK to
`mailglass_deliveries` (ARCHITECTURE §4.3; Pitfall 4 in RESEARCH).
Orphan webhooks insert with `delivery_id: nil` and are linked later
by `Mailglass.Events.Reconciler` (Plan 05).

## Idempotency

`:idempotency_key` is a nullable string backed by a partial UNIQUE
index (`mailglass_events_idempotency_key_idx WHERE idempotency_key IS
NOT NULL`). Plan 05's `Events.append/1` will pass
`on_conflict: :nothing, conflict_target: {:unsafe_fragment,
"(idempotency_key) WHERE idempotency_key IS NOT NULL"}` — replays are
no-ops.

# `t`

```elixir
@type t() :: %Mailglass.Events.Event{
  __meta__: term(),
  delivery_id: Ecto.UUID.t() | nil,
  id: Ecto.UUID.t() | nil,
  idempotency_key: String.t() | nil,
  inserted_at: DateTime.t() | nil,
  metadata: map(),
  needs_reconciliation: boolean() | nil,
  normalized_payload: map(),
  occurred_at: DateTime.t() | nil,
  reject_reason: atom() | nil,
  tenant_id: String.t() | nil,
  trace_id: String.t() | nil,
  type: atom() | nil
}
```

# `__reject_reasons__`
*since 0.1.0* 

```elixir
@spec __reject_reasons__() :: [atom()]
```

Closed reject_reason atom set.

# `__types__`
*since 0.1.0* 

```elixir
@spec __types__() :: [atom()]
```

Closed type atom set (Anymail taxonomy + internal). Cross-checked in api_stability.md.

# `changeset`
*since 0.1.0* 

```elixir
@spec changeset(map()) :: Ecto.Changeset.t()
```

Builds an INSERT-only changeset for a new `%Event{}`.

Intentionally the only public mutation point. No update or delete
helper is exposed — UPDATE and DELETE at the Repo layer raise
`Mailglass.EventLedgerImmutableError` via the DB trigger, and the
Elixir surface deliberately offers no path that looks like it could
work.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
