# SendGrid Ingress
Use the README as the primary setup lane. This guide keeps the SendGrid-specific
details focused: raw MIME delivery, duplicate fingerprinting, and replay
recovery posture.
## Mount Path
Mount the package-local plug on one obvious SendGrid route:
```elixir
forward "/inbound/:tenant_id/sendgrid",
MailglassInbound.Ingress.Plug,
provider: :sendgrid,
router: MyApp.MailglassInboundRouter
```
The ingress plug is verify-first:
1. verify SendGrid basic auth
2. resolve tenant scope
3. normalize the raw MIME `email` part into `%MailglassInbound.InboundMessage{}`
4. persist one canonical row plus one raw evidence row
5. dispatch the matched mailbox only after durable persistence commits
## Configuration
Configure the shipped SendGrid seam through `:mailglass_inbound`:
```elixir
config :mailglass_inbound, :sendgrid,
basic_auth: {"sendgrid-user", "sendgrid-pass"}
```
This slice ships shared-secret basic auth only. Signed multipart verification
did not ship in this slice.
## Raw MIME Requirement
SendGrid must post the raw MIME `email` part. The package fails closed if
SendGrid only sends parsed multipart fields, because replay and duplicate truth
depend on the original MIME artifact.
## Persistence And Execution
Verified SendGrid ingress writes two truths before mailbox execution starts:
- one canonical normalized row in `mailglass_inbound_records`
- one linked raw evidence row in `mailglass_inbound_evidence`
Mailbox execution happens after persistence and records append-only internal
execution lineage. Oban-backed execution is the durable path.
Task.Supervisor fallback is bounded best-effort only — with no durable enqueue
and no automatic retry. execution outcomes do not control provider retries;
the provider receives `200` once receive truth is safely committed.
## Duplicate Handling
SendGrid duplicate collapse is provider-specific. The package does not overload
`provider_message_id`, because SendGrid may omit it for raw MIME delivery.
Instead, duplicates collapse on:
`(tenant_id, provider, raw_mime_fingerprint)`
That fingerprint is derived from the stored raw MIME evidence, so retries and
manual resends with the same MIME do not create a second receive truth or a
second mailbox execution.
## Replay Honesty
Replay is an internal package capability that reruns the originally matched
mailbox against stored canonical plus raw evidence truth.
Replay is not:
- a fresh provider receive
- a re-ingestion of stored provider payloads as new events
- a silent reroute to a different mailbox
- a widened public provider surface
If the record predates execution-lineage capture, or if fresh history only
contains `:no_match`, replay fails with an explicit operator-facing error
instead of guessing a mailbox. Recovery in fallback mode depends on replay or
operator action over the stored truth rather than automatic retries.