4.9 KiB
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 |
|---|---|---|---|
| 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
MessageIdis notDELIVERED. - An SMTP
250is 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.