Behaviour contract for outbound delivery adapters.
Content responsibility
Adapters receive a pre-planned %Chimeway.Delivery{} struct with all rendered
content already present. Adapters MUST NOT call back into notifier modules to
render content — all content must arrive pre-populated on the delivery struct
before deliver/2 is called.
Configuration
Adapter config (API keys, base URLs, from addresses, etc.) must be read at
call time via Application.get_env/3. Never read config in module attributes
or at compile time — this supports test overrides via Application.put_env
and runtime environment switching.
Return contract
{:ok, meta}— the provider accepted the delivery.metais a compact map written tochimeway_delivery_attempts.provider_response. Adapters MUST redact sensitive fields (password, token, secret, api_key, auth) frommetabefore returning.{:error, reason_class, detail}— delivery failed.reason_classMUST be one of:temporary | :permanent | :bounced::temporary— transient failure; the dispatcher may retry.:permanent— non-retriable rejection by the provider.:bounced— the address or identity is unreachable (e.g. hard bounce).detailis a compact map with no PII and no full provider response bodies.
Outcome classification
Outcome classification (:succeeded, :failed, :rejected, :bounced) is
the dispatcher's responsibility, not the adapter's. Adapters return raw results;
Chimeway.Dispatch.Sync maps them to Chimeway's delivery state machine.
Summary
Callbacks
Deliver a single delivery to its outbound channel.
Map a parsed provider webhook payload into a normalized canonical Chimeway outcome. Required for adapters supporting async feedback loops.
Extract the delivery identity from a parsed provider webhook payload. Required for adapters supporting async feedback loops.
Extract a stable provider-assigned event ID from a parsed provider webhook payload.
Used for idempotent deduplication of provider retries (Phase 33 D-05 / A4).
Optional: adapters without stable event IDs omit this callback; those callbacks
get provider_event_id = nil (no dedup — the partial unique index ignores NULLs).
Verify the cryptographic signature of an incoming webhook from the provider. Required for adapters supporting async feedback loops.
Callbacks
@callback deliver(delivery :: Chimeway.Delivery.t(), config :: keyword()) :: {:ok, map()} | {:error, atom(), map()}
Deliver a single delivery to its outbound channel.
delivery is a pre-planned %Chimeway.Delivery{} struct with all content
populated. config is a keyword list of adapter-specific options read from
Application.get_env/3 at call time.
@callback normalize_feedback(parsed_payload :: map()) :: {:ok, %{status: :delivered | :bounced | :failed}} | :error
Map a parsed provider webhook payload into a normalized canonical Chimeway outcome. Required for adapters supporting async feedback loops.
@callback resolve_delivery(parsed_payload :: map()) :: {:ok, %{delivery_id: binary()}} | {:ok, %{provider_message_id: String.t()}} | :error
Extract the delivery identity from a parsed provider webhook payload. Required for adapters supporting async feedback loops.
Extract a stable provider-assigned event ID from a parsed provider webhook payload.
Used for idempotent deduplication of provider retries (Phase 33 D-05 / A4).
Optional: adapters without stable event IDs omit this callback; those callbacks
get provider_event_id = nil (no dedup — the partial unique index ignores NULLs).
@callback verify_webhook(raw_body :: binary(), headers :: list(), config :: keyword()) :: :ok | {:error, :unauthorized}
Verify the cryptographic signature of an incoming webhook from the provider. Required for adapters supporting async feedback loops.