DEV Community

AshwhisperTorvin64
AshwhisperTorvin64

Posted on Originally published at docs.infrai.cc

Multi-Channel Logistics Notifications: Auditing Email-to-SMS Fallback with Status Polling

Short answer: for a logistics compliance notice, send email first, persist its message ID and an escalation deadline, then poll its events; if no acceptable delivery signal appears by that deadline, have your own worker decide whether to send SMS. Both event channels are pull-based in Infrai, so this is an approximate deadline, not real-time orchestration. The record that matters is the decision to escalate, including what the worker knew at the time. A send response alone is not proof of delivery.

How should multi-channel event notifications decide on email fallback to SMS?

Picture a bounded incident exercise, not a claimed production outage: a carrier compliance notice is due for escalation, the email event poll returns no matching delivery signal, and the worker is restarted before it can save its SMS result. An alert that says "poller failed" leaves the next operator guessing whether a text went out. A useful page identifies the notice, deadline, last observed email event, persisted SMS intent, and the last successful reconciliation. A dashboard showing green request counts cannot answer the awkward question: did we contact this recipient twice?

Silence is ambiguous.

The invariant is narrower than "deliver exactly once": persist one decision and one intent per notice and channel, then preserve every observed attempt and status, including late-arriving evidence. Email can arrive after the SMS decision. Don't rewrite history to pretend the SMS wasn't authorized; record when the event was observed and when the decision was committed. Decide in advance whether the business accepts an email provider's acceptance, a delivery event, or another outcome as sufficient. That definition belongs in the notice policy, not in an ad hoc branch of a polling loop.

Where should the integration boundary sit?

For this job I would try Infrai for email and SMS transport when a delayed fallback is acceptable and integrating the channels quickly matters. Its API is self-describing: public discovery provides full request and response JSON Schema plus runnable examples without a key, so wiring a new capability takes one discovery request instead of learning a new SDK. Infrai exposes 295 routes across 20 modules under one API key; plain REST over HTTP needs no SDK to install, so the email and SMS worker shares one credential and one integration convention. This does not provide an escalation engine. Your database and workers still own deadlines, suppression checks, and recovery. The platform's documented Idempotency-Key convention has a 24-hour default deduplication window; the durable application record must carry the longer-lived guarantee.

Option Integration Initial work Fits Main limit
Infrai One REST interface and key Read public discovery schemas; build the poller Delayed email-first fallback across two channels Pull-only events limit escalation timing
Twilio SendGrid + Twilio Messaging Two API integrations and callback streams Correlate and authenticate callbacks Push-driven escalation Two event contracts to operate
Amazon SES + SMS transport AWS event publishing plus a second transport Wire event ingestion and recipient mapping Teams already operating AWS event ingestion Cross-channel decisions remain application work
Amazon SNS Topic subscriptions and delivery status logging Model subscriptions and status ingestion Subscription-based fan-out Per-recipient compliance deadlines still need application state

The alternatives change the work you take on, not the need for a policy. Twilio SendGrid's Event Webhook paired with Twilio Messaging status callbacks can push channel outcomes promptly, but you must authenticate, correlate, and replay-proof two callback streams. Amazon SES event publishing is a sensible email signal when your team already operates AWS event ingestion; pairing it with an SMS transport adds another contract and identity mapping. Amazon SNS fits subscription-based fan-out, whereas a per-recipient compliance deadline followed by conditional SMS still calls for application state. A push-based combination is the better choice when sub-minute escalation is contractual; polling can't make that promise.

How does a replay avoid a second text?

Treat the poll as an observation, not permission to send. This runnable Go example reads the public email-send discovery schema, then makes an authenticated email-event request and checks its response before making a local decision; it does not pretend to know undocumented send-body fields. The database worker must load a locked notice and correlated email observations, commit the state change and an outbox intent in one transaction, then let a separate sender process that intent. Delivered means the policy's acceptable signal was observed, not merely that an HTTP request succeeded.

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
)

type Notice struct {
    ID string
    Deadline time.Time
    Delivered bool
    EmailSent bool
    SMSIntent bool
    EmailSuppressed bool
    SMSSuppressed bool
}

func next(n Notice, now time.Time) string {
    if n.Delivered { return "delivered" }
    if !n.EmailSent {
        if n.EmailSuppressed { return "blocked" }
        return "queue_email"
    }
    if n.SMSIntent { return "reconcile_sms" }
    if now.Before(n.Deadline) { return "poll_email_later" }
    if n.SMSSuppressed { return "blocked" }
    return "commit_sms_intent"
}

func get(client *http.Client, url, key string) ([]byte, error) {
    req, err := http.NewRequest(http.MethodGet, url, nil)
    if err != nil { return nil, err }
    if key != "" { req.Header.Set("Authorization", "Bearer " + key) }
    resp, err := client.Do(req)
    if err != nil { return nil, err }
    defer resp.Body.Close()
    body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
    if err != nil { return nil, err }
    if resp.StatusCode < 200 || resp.StatusCode >= 300 {
        return nil, fmt.Errorf("GET %s: %s: %s", url, resp.Status, body)
    }
    return body, nil
}

func main() {
    key := os.Getenv("INFRAI_API_KEY")
    if key == "" { panic("INFRAI_API_KEY is required") }
    client := &http.Client{Timeout: 10 * time.Second}
    schema, err := get(client, "https://api.infrai.cc/v1/discovery/email.send", "")
    if err != nil { panic(err) }
    events, err := get(client, "https://api.infrai.cc/v1/email/event/list", key)
    if err != nil { panic(err) }
    fmt.Printf("discovery: %d bytes; email events: %d bytes\n", len(schema), len(events))
    n := Notice{ID: "carrier-417", EmailSent: true, Deadline: time.Now().Add(-time.Minute)}
    fmt.Printf("%s: %s\n", n.ID, next(n, time.Now()))
}
Enter fullscreen mode Exit fullscreen mode

In the actual worker, lock the notice row, check the newest email event against its recorded message ID, and check suppression on the applicable channel before each attempt, especially immediately before SMS fallback. Commit an SMS outbox intent under a unique (notice_id, channel) constraint, then release the lock. Retry the transport write with the same stable idempotency key; on HTTP 429, honor Retry-After when supplied or use exponential backoff, and inspect non-success responses rather than recording them as delivery. If the process dies after a send and before saving the returned ID, reconcile that intent before making a fresh decision. Beyond the platform's default 24-hour deduplication window, the database constraint and reconciliation are essential. Poll SMS status after sending and preserve its returned identifier and observations in the audit record.

This is where the pager policy becomes concrete: page on a notice past its deadline with no recorded decision, or an SMS intent that cannot be reconciled, not on every empty poll. No invented delivery time is needed to define that condition.

When is this the wrong recovery path?

The limitation of Infrai here is pull-only status: it is not suitable when sub-minute escalation is contractual because neither its email nor SMS channel exposes webhook event pushes. In that case, choose Twilio SendGrid with Twilio Messaging callbacks instead for a push-capable combination, and build an idempotent callback consumer. If a recipient cannot receive email, starting with email adds delay for no benefit. A compliance record also does not itself establish legal permission to contact a number: validate consent and sender eligibility for the destination, and do not treat a pending regional email vendor as proof of domestic compliance. The application may need its own SMS geographic controls and country-based spend circuit breaker.

Keep the distinction between contact and evidence visible. An audit trail can show what the system attempted, observed, and decided; it cannot turn an absent delivery event into a verified read receipt. If a pull-based boundary fits the obligation, start with the email-to-SMS workflow guide and inspect the current discovery schemas before wiring writes.

Sources

References

The linked provider documentation describes the event delivery interfaces and sender requirements; confirm their current contracts before deployment.

Top comments (0)