# Notification support matrix What each channel can actually prove, and what the platform refuses to claim. The grade column is not an opinion. `docs/notification/evidence-manifest.json` declares which claims each grade requires and which executable artifact proves each claim, and `./gradlew verifyNotificationEvidence` refuses a grade whose claims are not all backed by a file that exists. Raising a grade means adding the artifact first. Five channels read `Stable` until 2026-08-15. Nothing in the pipeline had ever sent a request to a provider — the sandbox job's whole body was two `echo` statements behind `continue-on-error` — and no test killed a worker mid-dispatch. The protocols are implemented and their contracts are proven against real PostgreSQL; the wire and the crash are not. That is what the grade now says. ## Channels | Channel | Reference implementation | Grade | Strongest evidence the platform records by default | |---|---|---|---| | Email | SMTP, Amazon SES API | Contract implemented / runtime unqualified | Provider acceptance; recipient mail-server delivery, bounce and complaint when the provider publishes events | | SMS | Twilio Programmable Messaging | Contract implemented / runtime unqualified | `accepted`/`queued`, `sent`, and carrier-DLR `delivered`/`undelivered` | | Mobile push (Android and cross-platform) | FCM, FID-first with legacy registration token compatibility | Contract implemented / runtime unqualified | FCM acceptance and explicit failures | | Mobile push (Apple) | APNs HTTP/2 provider API | Contract implemented / runtime unqualified | APNs acceptance | | Web Push | RFC 8030, RFC 8291, RFC 8292 | Contract implemented / runtime unqualified | Push-service acceptance; user-agent acknowledgement only where the service offers receipts | | In-app inbox | Own database | Optional stable | `PERSISTED`, `SEEN`, `READ` | | Webhook | HTTP client platform | Extension | Whatever the receiving HTTP contract states | ## What each grade requires | Grade | Requires | |---|---| | Stable | durable, multi-worker-safe, callback-supported, recoverable, provider-wire-qualified | | Contract implemented / runtime unqualified | durable, multi-worker-safe, callback-supported | | Optional stable | durable | | Extension | nothing; the receiving contract owns its own guarantees | | Experimental | nothing; the grade is the warning | `recoverable` was met on 2026-08-15 by `WorkerCrashRecoveryContractTest`, which walks the four phases a dispatch passes through — claimed, attempt written, request started, body committed — and asserts for each that the delivery becomes claimable again or becomes a question for the provider, never both and never neither. The remaining unmet claim, and what would meet it: - **provider-wire-qualified** — a real provider sandbox call producing an immutable, uploaded artifact with a correlation id. No request has ever left the process in CI. ## Evidence levels `NONE` → `PLATFORM_QUEUED` → `PROVIDER_ACCEPTED` → `NETWORK_OR_CARRIER_ACCEPTED` → `DEVICE_DELIVERED` → `USER_AGENT_DISPLAYED` → `USER_READ` | Provider signal | Highest evidence it may produce | |---|---| | Internal queue commit | `PLATFORM_QUEUED` | | SES `MessageId` | `PROVIDER_ACCEPTED` | | SES `Delivery` | `NETWORK_OR_CARRIER_ACCEPTED` | | Twilio `accepted` / `queued` | `PROVIDER_ACCEPTED` | | Twilio `sent` | `NETWORK_OR_CARRIER_ACCEPTED` | | Twilio `delivered` | `DEVICE_DELIVERED` | | FCM send success | `PROVIDER_ACCEPTED` | | APNs 2xx | `PROVIDER_ACCEPTED` | | Web Push `201` | `PROVIDER_ACCEPTED` | | Web Push receipt capability | `DEVICE_DELIVERED` | | In-app row commit | `PROVIDER_ACCEPTED` | | In-app `seen` endpoint | `USER_AGENT_DISPLAYED` | | In-app `read` endpoint, authenticated app receipt | `USER_READ` | Promotions the platform will not make, in code or in configuration: - FCM send success is not `DEVICE_DELIVERED`. - An APNs 2xx is not `DELIVERED`. - An SES `MessageId` is not `DELIVERED`. - An SMTP `250` is not inbox delivery. ## Submission outcomes `NOT_SUBMITTED`, `CONFIRMED_ACCEPTED`, `CONFIRMED_REJECTED`, `AMBIGUOUS`. `AMBIGUOUS` is a first-class stored state, not an error path. It means the request body was committed to the provider and the outcome could not be read. While an ambiguous attempt exists on a recipient delivery, automatic retry and automatic cross-channel fallback are both blocked. ## Not supported The platform will not claim any of the following, because no channel above can support them: - guaranteed delivery - guaranteed read - exactly-once human notification - unconditional multi-provider failover after an unread response - provider SDK types in the public API - audience selection, campaign segmentation or jurisdiction rulings ## Target model `FCM_FID` is the primary mobile push target. `FCM_REGISTRATION_TOKEN_LEGACY` and `APNS_DEVICE_TOKEN` are separate types with separate lifecycles; they are never flattened into one string field.