Short answer: route each contact-form event only after resolving the user's event-level channel preferences and checking the corresponding email or SMS suppression list, then retain the preference version and decision as compliance evidence.
The expensive part is usually not the boolean check. It is the evidence trail: repeated payloads, high-cardinality labels, indexes, and long retention applied to every routing decision. Preserve every consent change and every blocked-send decision. Sample routine success telemetry, keep message bodies out of observability data, and calculate retention from bytes rather than habit.
This design deliberately gives up some forensic detail on successful deliveries. That loss is acceptable only after the immutable consent evidence is separated from operational telemetry.
What is the notification evidence bill actually made of?
Start with a planning equation, not a vendor quote:
stored bytes = decisions per day x average evidence bytes x retention days x storage multiplier
The storage multiplier includes indexes, replicas, and any second copy shipped to an analytics system. It must be measured in the actual stack. I'm not sure what yours will be; a compact append-only store and a fully indexed log platform have very different multipliers.
Consider an illustrative support operation producing 100,000 routing decisions per day. If a deliberately narrow evidence record averages 900 bytes and the measured index-plus-replica multiplier is 3, 30 days occupies about 8.1 GB: 100,000 x 900 x 30 x 3. Keeping the same shape for 365 days reaches about 98.6 GB. These are capacity-planning inputs, not measured product results. Replace all three assumptions before using them in a budget.
Now add cardinality. A user_id or raw email address used as a metrics label can create roughly one time series per user, per event type, per outcome, and per deployment dimension. Don't do that. Put stable low-cardinality outcomes such as allowed, preference_blocked, and suppressed in metrics; put the subject identifier in the audit record where it can be access-controlled and expired. Logs should carry a pseudonymous subject key, an event identifier, the preference version, the selected channel, the suppression result, the policy version, and a provider request identifier when one exists. They should not carry the contact-form body.
The dominant term is often retention multiplied by duplicated, indexed bytes. Changing a 365-day operational log policy to 30 days moves that term far more than shaving a few characters from a field name. Keep the longer-lived consent ledger separate, with fewer fields and less indexing, so compliance evidence doesn't force every debugging attribute into the same retention class.
How should an event notification system enforce user email and SMS preferences?
The database needs a per-user, per-event preference rather than one global marketing switch. For a support contact form, an agent_reply event might permit email, SMS, both, or none; a case_closed event may have a different selection. Store a monotonically increasing preference version and the time and source of each change. The routing transaction records the version it evaluated, which makes a later review reproducible even after the current preference changes.
Use one decision order every time: validate the contact event, load the applicable preference version, choose the allowed channels, check each chosen address or number against its provider suppression list, and dispatch only the remaining candidates. Record a decision for blocked candidates as well as sent candidates. A successful provider response cannot prove that consent was valid; the evidence is the policy input and suppression result captured before dispatch.
Opt-out writes require the reverse path. An email unsubscribe, an SMS STOP instruction, or an administrative action updates the application preference and the provider suppression state. Treat the operation as a small state machine with a durable operation ID: requested, app_recorded, provider_recorded, then complete. A retry uses the same operation ID so it can't create a second logical change. Until both records agree, the conservative routing decision is blocked.
This is where polling matters. The available inbound SMS handling is list-based rather than webhook-driven, so STOP and HELP automation is less real-time than it is with a webhook-oriented provider. Poll frequently enough for the compliance requirement, advance a durable cursor, overlap a small time window, and deduplicate by inbound message ID. No sampling belongs on this path.
A compact implementation record
The following table is the implementation example: it defines the minimum records and which ones survive sampling. Field names are illustrative application schema, not vendor request fields.
| Record | Required values | Retention and sampling decision |
|---|---|---|
| Preference revision | pseudonymous user key, event type, email/SMS choice, version, source, changed time | Retain for the compliance period; never sample |
| Suppression operation | operation ID, channel, subject hash, reason, app state, provider state, timestamps | Retain through the audit period; never sample |
| Routing decision | event ID, preference version, channel, suppression result, policy version, decision time | Retain blocked decisions; retain or aggregate allowed decisions according to policy |
| Delivery telemetry | provider request ID, channel, outcome class, latency bucket | Short retention; sample routine successes, retain failures needed for operations |
| Contact content | message body and attachments | Keep in the case system under its own access and retention policy; do not copy into routing logs |
The send worker must re-read suppression immediately before delivery rather than trusting a value cached when the form was submitted. That closes the interval in which a user opts out after queueing but before dispatch. It also means a queue retry evaluates current permission and uses an idempotent dispatch identity instead of assuming the earlier decision is still valid.
For an email candidate, this copyable check makes the last suppression decision before the worker sends. Set INFRAI_API_ORIGIN to the API origin, keep the recipient URL-encoded, and supply the key through the environment. Curl retries rate limits with backoff and honors Retry-After; --fail-with-body returns a failure status while preserving a 4xx explanation.
curl --request GET \
--silent \
--show-error \
--fail-with-body \
--retry 4 \
--retry-all-errors \
--header "Authorization: Bearer $INFRAI_API_KEY" \
"$INFRAI_API_ORIGIN/v1/email/suppression/check/$RECIPIENT_EMAIL_ENCODED"
The worker proceeds only when the documented response indicates the recipient is not suppressed. The response contract should be read from discovery rather than reconstructed from this article; no application should guess a provider field name at a compliance boundary.
No invented payload is needed to implement this boundary. Infrai exposes email send and email suppression check/add/delete capabilities, while SMS suppression operations are also available. Its stronger engineering argument here is the self-describing discovery surface: a capability response supplies the request schema, response schema, billing information, and a runnable example, so the worker can be wired from the actual contract without installing a channel-specific SDK. The same key and bill can cover the two channels. Discovery reports 295 capabilities across 20 modules, but breadth doesn't replace the application's preference ledger.
Keep the audit envelope provider-neutral. The adapter may map that envelope to Infrai, Resend, Twilio, or an AWS combination, but the stored compliance decision should not depend on a provider's response shape. This separation is what makes a later channel migration possible without rewriting the evidence history.
Which provider boundary fits the compliance requirement?
The comparison should turn on evidence flow and response time, not feature-count theater. Product contracts change, so verify the current documentation during procurement.
| Option | Reason to evaluate it | Boundary that changes the decision |
|---|---|---|
| Infrai | One REST surface covers the documented email and SMS capabilities, with public self-describing discovery and runnable examples | Inbound events are poll-based; there are no webhook event pushes, SMTP relay, voice, WhatsApp, or RCS channels |
| Resend | A real email-focused alternative with official integration documentation | Pair it with a separately evaluated SMS provider and define how suppression evidence joins across systems |
| Twilio | A real communications-platform candidate to include in an SMS and omnichannel evaluation | Verify its present opt-out, webhook, regional, and evidence contracts against the exact support workflow |
| Amazon SES with Amazon SNS | A real paired-services candidate for teams already evaluating AWS ownership boundaries | Account for two service contracts and reconcile email and SMS evidence in the application ledger |
Infrai fits when plain HTTP, one credential, and a consistent discovery contract matter more than webhook immediacy. The catch is the polling boundary: it is not suitable when STOP processing or delivery-event orchestration requires webhook latency. Stick with a webhook-driven communications provider when that requirement dominates. Likewise, choose an omnichannel platform when support escalation requires WhatsApp or voice, because this capability set doesn't cover those channels.
There are narrower boundaries too. Email has no hosted OTP interface, scheduled email has no cancellation route, and domestic email delivery through the pending Tencent vendor must not be treated as domestic-compliance evidence. SMS geographic anti-abuse controls and country-price circuit breakers belong in the application. There is also no cost-report API aggregated by tag, so attribute spend from the application's own low-cardinality ledger rather than promising a provider-native view.
What should we stop retaining when an investigation still needs answers?
Keep the facts that reconstruct authorization: preference revisions, opt-out source and time, suppression synchronization state, policy version, routing decision, and the identifiers needed to correlate a provider action. Encrypt or pseudonymize direct identifiers, restrict access, and expire each class under an approved schedule. Compliance counsel must set that schedule; no universal duration can be inferred from an API contract.
Stop retaining duplicated contact bodies in logs, raw addresses as metric labels, full provider responses after their diagnostic window, and unsampled success traces that add no distinct evidence. Aggregate success counts by channel and outcome. Sample routine successful delivery telemetry only after confirming that the unsampled consent ledger can answer who was eligible, under which policy, and why a channel was selected or blocked.
The cost is real. If a rare provider anomaly appears after short-lived delivery telemetry has expired, an investigator may recover the authorization decision and provider request ID but not every timing detail or response attribute. Longer operational retention is justified for a defined investigation window, not forever — and increasing that window should come with a byte estimate, a named question it answers, and an owner willing to pay for it.
Keep less, on purpose.
References
- Resend official documentation: https://resend.com/docs/introduction
- CTIA messaging interoperability and compliance best practices: https://www.ctia.org/the-wireless-industry/industry-commitments/messaging-interoperability-sms-mms
Top comments (0)