# `Mailglass.IdempotencyKey`
[🔗](https://github.com/szTheory/mailglass/blob/v1.0.0/lib/mailglass/idempotency_key.ex#L1)

Generates deterministic idempotency keys for webhook deduplication and
event-ledger entries.

Keys follow the format locked in CORE-05:

  * `for_webhook_event(provider, event_id)` → `"provider:event_id"`
  * `for_provider_message_id(provider, message_id)` → `"provider:msg:message_id"`

The `msg:` infix on provider-message-id keys keeps the two namespaces
disjoint so a webhook event id and a provider message id that happen to
share a string value never collide in the `UNIQUE` partial index.

## Sanitization (T-IDEMP-001)

Keys are derived from provider-supplied strings that reach us across a
trust boundary. Two normalization passes run on every key:

  1. Non-ASCII-printable bytes (outside `0x20..0x7E`) are stripped —
     control characters, DEL, and UTF-8 continuation bytes alike. The
     resulting key is pure printable ASCII.
  2. The key is truncated at `512` bytes so malicious or malformed
     input cannot balloon the row size or the unique-index b-tree.

Both passes happen before the key is written to the events ledger, so
the ledger itself never sees a non-printable byte.

# `for_provider_message_id`
*since 0.1.0* 

```elixir
@spec for_provider_message_id(atom(), String.t()) :: String.t()
```

Builds an idempotency key for a provider-assigned message id.

## Examples

    iex> Mailglass.IdempotencyKey.for_provider_message_id(:sendgrid, "SG.abc")
    "sendgrid:msg:SG.abc"

# `for_webhook_event`
*since 0.1.0* 

```elixir
@spec for_webhook_event(atom(), String.t()) :: String.t()
```

Builds an idempotency key for a webhook event.

## Examples

    iex> Mailglass.IdempotencyKey.for_webhook_event(:postmark, "evt_abc")
    "postmark:evt_abc"

    iex> Mailglass.IdempotencyKey.for_webhook_event(:postmark, "evt abc")
    "postmark:evtabc"

# `for_webhook_event`
*since 0.1.0* 

```elixir
@spec for_webhook_event(atom(), String.t(), non_neg_integer()) :: String.t()
```

Builds a per-batch-event idempotency key for SendGrid batch payloads.

Each event in a batch gets `"#{provider}:#{provider_event_id}:#{index}"`
so duplicate inserts of the same batch event collapse via the
`mailglass_events.idempotency_key` partial UNIQUE index (Phase 2 Plan
05 DDL). Single-event Postmark payloads use `for_webhook_event/2`
without the index suffix.

Phase 4 extension per CONTEXT line 343 (Plan 04-06).

## Examples

    iex> Mailglass.IdempotencyKey.for_webhook_event(:sendgrid, "evt_X", 0)
    "sendgrid:evt_X:0"

    iex> Mailglass.IdempotencyKey.for_webhook_event(:postmark, "abc-123", 5)
    "postmark:abc-123:5"

---

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