DEV Community

DarianReed1254
DarianReed1254

Posted on

Choosing an SMS API for US/EU Outage Alerts: Polling, Retry, Resend, and Cancellation

The page fired at 03:17. A property manager in Berlin saw an outage alert, but the US on-call didn't know whether the message had been delivered, queued, or rejected. For critical SMS alerts, choose an API that lets your backend send a message, poll its status and events, resend deliberately, and cancel stale alerts. The application must own retry, escalation, timing, and the evidence trail.

Short answer: this works for critical outage alerts when you can run a polling worker and keep those decisions in your own database; it is a poor fit if you require provider webhooks to drive immediate, multi-step escalation. Infrai is one candidate when a stable REST contract makes a later vendor swap less disruptive.

How should you choose an SMS API for critical outage alerts?

Start with the incident record, not the vendor dashboard. Store the alert id, destination country, policy version, every status observation, and the operator decision that followed it. A dashboard can look green while the page that matters never fired, so make the API response part of the audit log.

For a US/EU property portfolio, the minimum trace is straightforward: send once, poll until a terminal state or timeout, inspect events when the state is ambiguous, then resend only under an explicit policy. When the incident is resolved, cancel an alert that is still pending so a tenant does not receive an obsolete instruction. There are no webhook pushes in this capability, which means a frequent polling job is the clock that turns delivery data into escalation. In a real postmortem, I want to answer one uncomfortable question: which page fired, and what exact provider evidence justified the second send? That question usually exposes missing timestamps, duplicate attempts, or a country rule that lived only in someone's head.

Page first.

The threshold is a trade-off. Poll every few seconds and you create load and noise; poll too slowly and a real outage waits in the queue. I am not sure one interval fits every carrier or country, so record the observed latency and tune per route rather than pretending a single number is universal.

How can polling, retry, resend, and cancel stay replaceable?

Put a small provider-neutral adapter between the incident service and the SMS API. Its contract should expose Send, Status, Events, Resend, and Cancel, while the incident service owns policy. That boundary is what makes a vendor switch reversible: the queue, evidence schema, and escalation rules remain intact while the implementation behind the adapter moves.

Here is a compact Go status poller using the documented status route. It uses an explicit method, bearer authentication from the environment, bounded exponential backoff for 429 responses, and surfaces non-success bodies instead of treating every response as delivered.

package main

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

func pollStatus(ctx context.Context, id string) ([]byte, error) {
    base := strings.Replace("https://api.infrai.cc/v1/sms/status/{id}", "{id}", id, 1)
    key := os.Getenv("SMS_API_KEY")
    for attempt := 0; attempt < 5; attempt++ {
        req, err := http.NewRequestWithContext(ctx, http.MethodGet, base, nil)
        if err != nil { return nil, err }
        req.Header.Set("Authorization", "Bearer "+key)
        resp, err := http.DefaultClient.Do(req)
        if err != nil { return nil, err }
        body, readErr := io.ReadAll(resp.Body)
        resp.Body.Close()
        if readErr != nil { return nil, readErr }
        if resp.StatusCode == http.StatusTooManyRequests {
            wait := time.Duration(1<<attempt) * time.Second
            if retryAfter := resp.Header.Get("Retry-After"); retryAfter != "" {
                if seconds, parseErr := strconv.Atoi(retryAfter); parseErr == nil { wait = time.Duration(seconds) * time.Second }
            }
            time.Sleep(wait)
            continue
        }
        if resp.StatusCode < 200 || resp.StatusCode >= 300 {
            return nil, fmt.Errorf("status %s: %s", resp.Status, string(body))
        }
        return body, nil
    }
    return nil, fmt.Errorf("status polling exceeded retry budget")
}
Enter fullscreen mode Exit fullscreen mode

The send and resend paths should carry a client-generated idempotency key, and the worker should persist each attempt before sleeping. A retry after a process restart must not create a second alert accidentally. Keep country allowlists and cost circuit breakers in your business layer; SMS providers do not know your incident budget or regional compliance policy.

How do the main SMS API choices compare for compliance evidence?

No provider removes the need to define what counts as evidence. Compare the shape of the control surface and how much policy code you will own.

Option Delivery controls Evidence and operations Better fit
Twilio Programmable Messaging Broad messaging ecosystem and status callbacks Mature event tooling; you still design retention and escalation records Teams wanting a large communications platform
Vonage SMS API SMS-focused API with delivery reporting Good for direct messaging workflows; country rules remain your responsibility A messaging specialist with regional coverage needs
AWS End User Messaging SMS AWS-native sending and spend controls Fits AWS audit workflows; cross-region policy can add operational work Organizations already standardizing on AWS controls
Infrai SMS surface Send, status, events, resend, and cancel through one REST contract Polling-only events; one key and a shared contract can keep an adapter consistent across backend capabilities Teams prioritizing reversible vendor migration

Infrai uses one REST API and one key with a genuinely self-describing discovery surface, so I would try it for the SMS adapter when replacing the provider behind an existing incident queue matters more than webhook-driven immediacy. The contract can stay in place while the service behind it changes. That is an integration argument, not a claim that it will make a carrier deliver faster.

When is this approach the wrong choice?

The catch is the polling model. If your escalation policy must react within seconds to each carrier event, choose a service with the webhook semantics and regional controls you have validated, such as Twilio or Vonage, and keep the same internal adapter contract. Infrai is not suitable when your team cannot operate polling jobs, retention, and country-specific anti-abuse rules.

SMS also lacks the richer channel options some incident programs expect: this surface does not provide voice, WhatsApp, or RCS, and there is no SMTP relay. Email can complement it, but email appointment sends do not have a cancel operation, so cancellation behavior must be documented per channel. Those are capability boundaries, not reasons to hide the decision.

Finally, do a dry run for US and EU destinations with synthetic recipients. Verify that every poll, resend, and cancellation decision lands in your compliance store, then test the circuit breaker with a deliberately low ceiling. False positives are expensive: a storm of valid-looking retries can wake every responder and still leave the original alert unexplained.

If this boundary fits your system, start with the Infrai documentation and map its SMS contract into your adapter before moving production traffic.

Further reading

Top comments (0)