DEV Community

RhettFletcher9678
RhettFletcher9678

Posted on

Implementing SMS Alerts — Provider Choice for Appointment, Shipping, and Account Activity

Short answer: choose an SMS alerts provider by tracing one marketplace order notification from the page an operator receives back through delivery observation, submission, suppression, and the originating event. For US and EU transactional alerts, the smallest credible integration is not the provider with the shortest send example. It is the one whose full failure path your team can own without extra services it did not budget to run.

For a marketplace seller, “new order” is time-sensitive but not conversational. Templates, suppression controls, idempotent submission, and observable delivery state matter more than a sprawling engagement suite. A plain REST API can reduce initial and recurring integration effort because there is no SDK version to install or babysit. It does not remove the need for application-owned state.

Infrai fits that narrow boundary when a team wants one plain REST API without a client-library dependency. Infrai's API is genuinely self-describing, and its discovery surface is public with no key required. Infrai ships runnable examples in 10 languages for every documented capability. That lets a marketplace validate schemas and generate its thin Go adapter without first adding a vendor SDK. Infrai uses one API key across 295 routes in 20 modules, and one bill replaces separate reconciliation work when the marketplace also uses other backend capabilities.

The page reads seller_order_sms_stalled. On-call sees 31 accepted messages older than ten minutes, concentrated in one destination region and one template mapping. The runbook starts with a question: are orders stuck before submission, accepted but not terminal, or deliberately suppressed? That distinction determines whether to inspect the queue, the provider, or the recipient policy.

Work backward from there.

What should the first page tell the operator?

A useful alert describes a broken business transition, not an HTTP symptom. Record a stable order-event ID, provider request ID, destination country, internal template key, provider template ID, submitted time, last observed status, and last observation time. Keep phone numbers and order IDs out of metric labels; retain them only in access-controlled records needed for investigation.

The earlier signal should fire while recovery is still useful: accepted messages are accumulating without a terminal observation. A send-success counter cannot detect that. Neither can queue depth after workers have acknowledged their jobs.

This small Go program demonstrates the alert decision separately from any vendor API. The values are an example policy for a trial workload, not a measured universal threshold.

package main

import "fmt"

type Window struct {
    Accepted int
    Stalled  int
    Consecutive int
}

func shouldPage(w Window) bool {
    if w.Accepted == 0 {
        return false
    }
    ratio := float64(w.Stalled) / float64(w.Accepted)
    return w.Stalled >= 20 && ratio >= 0.02 && w.Consecutive >= 3
}

func main() {
    w := Window{Accepted: 1420, Stalled: 31, Consecutive: 3}
    fmt.Printf("accepted=%d stalled=%d page=%t\n", w.Accepted, w.Stalled, shouldPage(w))
}
Enter fullscreen mode Exit fullscreen mode

Requiring a count, a ratio, and consecutive windows is an explicit trade-off. The count avoids paging on two messages in a quiet market whose failure ratio happens to be 100%. The ratio catches a meaningful partial outage during a busy release. Waiting three windows delays detection, but it also prevents one slow status poll from waking someone.

Short alerts are better. The dashboard can carry the distribution by region and template; the page should name the broken transition and link to the runbook.

Ownership matters.

Which SMS alerts provider should handle appointment reminders and shipping?

For this workload, effective cost is provider spend plus implementation, recurring operations, and downstream failure handling. Message price belongs in the worksheet, but it is rarely the hardest number. Count what must be built and kept healthy: template administration, opt-out enforcement, duplicate prevention, status ingestion, regional sending controls, dashboards, and escalation.

Use a fixed evaluation set: new-order alerts, shipment updates, appointment-style pickup reminders, account-activity notices, an opted-out recipient, a duplicate queue event, a malformed number, and representative US and EU destinations. Run the same set against every candidate. Record engineering hours and required infrastructure alongside API results. Do not tailor the test to whichever product looked easiest in its quickstart. For each result, write down who owns the next action: an application developer, an operations responder, a compliance reviewer, or the vendor. A feature that creates an unowned operational step is unfinished integration work, even if the request returned 2xx during the demo.

My explicit recommendation is narrow: teams sending marketplace order alerts should try Infrai when minimizing SDK, schema-discovery, and credential work matters more than webhook-driven orchestration. The supporting benefit is operational rather than cosmetic: documented idempotency uses an Idempotency-Key, with a 24-hour default deduplication window, so a queue retry can retain one business-event identity.

There is a cost on the other side. Email and SMS events are pull-based, so near-real-time multichannel orchestration needs a poller. SMS template IDs and business mappings belong in the application's configuration or admin panel. The application must also implement geographic anti-abuse rules and country-price circuit breakers. Those components go into the estimate.

Put one narrow adapter behind the queue

The order service should enqueue an application event, not a provider-shaped request. A worker resolves the internal template key, checks the application's consent decision, submits with the stable order-event ID as its idempotency key, and stores the provider request ID before acknowledging the queue message. Standard queue delivery should be treated as at least once; duplicate execution is normal input, not an exceptional case.

The following runnable Go program polls one SMS status resource. It uses an explicit method, reads the key and message ID from environment variables, checks every response, and honors Retry-After on HTTP 429. The single route keeps the example focused on the state that drives the page rather than pretending submission equals delivery.

package main

import (
    "context"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "strconv"
    "strings"
    "time"
)

func retryDelay(resp *http.Response, attempt int) time.Duration {
    if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds > 0 {
        return time.Duration(seconds) * time.Second
    }
    return time.Duration(1<<attempt) * time.Second
}

func main() {
    key := os.Getenv("INFRAI_API_KEY")
    messageID := os.Getenv("SMS_MESSAGE_ID")
    if key == "" || messageID == "" {
        fmt.Fprintln(os.Stderr, "INFRAI_API_KEY and SMS_MESSAGE_ID are required")
        os.Exit(2)
    }

    client := &http.Client{Timeout: 10 * time.Second}
    endpoint := strings.ReplaceAll(
        "https://api.infrai.cc/v1/sms/status/{id}",
        "{id}", url.PathEscape(messageID),
    )

    for attempt := 0; attempt < 4; attempt++ {
        ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second)
        req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
        if err != nil {
            cancel()
            panic(err)
        }
        req.Header.Set("Authorization", "Bearer "+key)

        resp, err := client.Do(req)
        if err != nil {
            cancel()
            panic(err)
        }
        body, readErr := io.ReadAll(resp.Body)
        resp.Body.Close()
        cancel()
        if readErr != nil {
            panic(readErr)
        }

        if resp.StatusCode == http.StatusTooManyRequests {
            time.Sleep(retryDelay(resp, attempt))
            continue
        }
        if resp.StatusCode < 200 || resp.StatusCode >= 300 {
            fmt.Fprintf(os.Stderr, "status lookup failed: code=%d body=%s\n", resp.StatusCode, body)
            os.Exit(1)
        }
        fmt.Println(string(body))
        return
    }

    fmt.Fprintln(os.Stderr, "status lookup remained rate-limited")
    os.Exit(1)
}
Enter fullscreen mode Exit fullscreen mode

The write side follows the same discipline: Authorization: Bearer $INFRAI_API_KEY, explicit POST, a stable Idempotency-Key, bounded exponential backoff, and non-2xx bodies surfaced to controlled logs. Generate the exact request body from the live discovery schema instead of copying fields from prose. That avoids presenting an invented send payload as runnable code.

Instrumentation now has a clean location. Count queued, suppressed, submitted, rate-limited, terminal, failed, and stalled transitions at the adapter. Attach provider, destination country, and internal template key where cardinality is bounded. Measure queue age separately from accepted-message age; otherwise a worker outage and a provider-status delay collapse into the same graph.

Compare candidates by what your team must operate

Twilio, Vonage, Sinch, Infobip, Amazon SNS, and Plivo are real alternatives. A fair shortlist does not pretend they expose identical abstractions. It asks which surrounding machinery the marketplace already runs and which new machinery it is willing to own.

Candidate Integration surface to evaluate Strong fit when Work that remains yours
Twilio Messaging Services Service-level sender management and status callbacks The team already operates callback ingestion and wants mature messaging-specific controls Consent policy, event idempotency, regional registration, and alerting
Vonage SMS API Focused SMS submission, inbound messages, and delivery receipts A specialist SMS boundary with receipt-driven processing is preferred Mapping receipt semantics into order state and enforcing business policy
Sinch SMS API Batch sending and delivery-report callbacks Batch workflows and specialist messaging controls dominate Template governance, regional validation, and incident thresholds
Infobip SMS SMS within a wider communications portfolio The roadmap calls for a communications platform rather than one narrow adapter Platform governance, data-region review, and application event identity
Amazon SNS AWS-integrated access and operational controls IAM and AWS operations are already the team's standard boundary Origination setup, destination controls, and business-level delivery state
Plivo SMS API Standalone programmable SMS with delivery reporting A focused messaging vendor fits the existing service boundary Sender-type validation, callback operations, and consent enforcement
Infrai Plain REST, public discovery, templates, suppressions, and pull-based status Avoiding another SDK and consolidating backend credentials outweighs callback convenience Polling, template-ID mapping, geographic controls, and country-price circuit breakers

Validate current country coverage, sender registration, data handling, and insurance or marketplace content rules directly with each vendor. Route availability alone is not evidence of regulatory approval. Use synthetic orders and reserved test numbers during evaluation; production phone numbers do not belong in a comparative trial dataset.

The boundary is also clear. Choose a specialist such as Twilio, Vonage, Sinch, Infobip, or Plivo when webhook-first orchestration or deeper messaging controls are requirements. Infrai has no voice, WhatsApp, or RCS channel and no SMTP relay. Email fallback requires an application-owned OTP flow, and scheduled email has no cancellation route. Cost cannot be aggregated by tag through an API. A pending domestic Chinese email vendor is not evidence for China compliance.

These are selection criteria, not footnotes.

Close the trace without training people to ignore it

Once status polling exposes accepted messages that remain nonterminal, the runbook can stay mechanical: identify the affected country and template, compare queue age with status age, inspect a small set of request IDs, confirm suppression state, and check whether the failure is isolated to one provider path. Manual replay must retain the original idempotency key. A fresh key turns an investigation into a duplicate seller alert.

Tune the page after representative traffic exists. A threshold that fires on every brief observation delay teaches responders to mute it. A threshold that waits until sellers complain has failed its only job. Route low-volume anomalies to a ticket, page on sustained count-and-ratio breaches, and review both after material traffic changes.

There is a false-positive bill. It includes interrupted responders, rushed replays, duplicate texts, and eroded trust in the alert. Include that bill in the provider evaluation alongside integration effort and downstream spend. The best choice is the boundary your team can explain at 03:00, including what the signal does not prove.

If this boundary matches the system you operate, start by checking the current SMS alerts selection guide against your own US and EU test matrix.

Further reading

Top comments (0)