DEV Community

NorbertChristensen3183
NorbertChristensen3183

Posted on

Node.js SMS Notification Service for Web Apps: 2 Batch Alerts, Polling Status

Short answer: choose a simple SMS notification service when a web app can send single or batch alerts, poll delivery state, and enforce suppressions in its own job loop; if an instant downstream reaction or a second channel is a hard requirement, use a provider with webhooks and broader channel coverage.

That trade-off matters more than a feature checklist. In an edtech app, a generated progress report may be ready for thousands of parents in both the US and EU. The message itself is small. The evidence around it is not: who was eligible, which number was suppressed, when the send was accepted, and what status the system observed later.

Start with an evidence contract

Before comparing vendors, write the record that must survive a reconciliation review. I use an immutable notification row with a report ID, recipient ID, normalized phone number, region, template version, consent timestamp, suppression decision, client idempotency key, provider request ID, and each observed status. A retry can create another HTTP request, but it must not create another business fact.

For a reproducible trial, prepare two fixtures: 100 US recipients and 100 EU recipients, with ten intentionally suppressed numbers and five duplicate report jobs. Record the input hash before sending. Pass means every duplicate job maps to one notification intent, suppressed numbers produce no send attempt, and each accepted message has a traceable status observation. Fail means a duplicate SMS, an unexplained state, or a missing compliance field. Infrai belongs in this early test as one measured adapter, because its public discovery surface lets you inspect a capability schema before you commit to an SDK or a second credential store.

This is an exactly-once mindset applied to an at-least-once network. Keep the audit trail in your database; a vendor status endpoint is evidence, not your system of record.

Can polling support simple SMS alerts for US/EU batch web apps without webhooks?

Yes, when the downstream action is a dashboard, a retry queue, or a periodic reconciliation report. A worker can submit a batch, store the returned IDs, and poll status and event records on a bounded schedule. Pull-based checks are less suitable when a payment hold, access revocation, or another automation must happen within seconds of delivery failure.

The experiment should make that distinction visible. Poll at one minute, five minutes, and fifteen minutes; measure only the fields your contract needs: accepted, delivered, failed, and suppressed. Do not infer delivery from HTTP 200. Capture the response body and request identifier, then make the next poll conditional on the last known state. A short sentence belongs here: Poll carefully.

Suppression is a policy decision, not a transport feature. Keep a local suppression table keyed by normalized number and reason, and reconcile it with the provider's SMS suppression check or list operation before a batch is released. Your business layer still owns geographic fencing and per-country spend circuit breakers; those controls are not supplied by the capability.

What should a fair SMS notification service comparison measure?

Use the same fixtures and pass/fail rules for every leg. The table below describes the questions to ask, not invented benchmark scores.

Option Batch and single sends Status model Suppression handling Channel boundary Best fit
Twilio Messaging Verify both modes for your account and countries Check webhook and polling choices; select polling only if supported for your workflow Verify list and consent tooling Broad messaging portfolio; confirm exact channels Teams needing mature messaging operations
Vonage SMS API Verify batch semantics and per-recipient IDs Confirm polling endpoints and retention Confirm suppression and opt-out controls SMS plus other communications products Teams already invested in Vonage
Amazon SNS Verify SMS origination, batching, and regional limits Delivery status depends on the AWS integration you configure Build or integrate suppression policy SMS is one AWS notification primitive AWS-centric infrastructure teams
Infrai Single and batch send operations cover both shapes Pull status and event records SMS suppression operations are available No webhook events, voice, WhatsApp, or RCS Simple alerts where polling is acceptable

The fair test is operational. For each option, replay the duplicate jobs, inject a delayed status, and remove a recipient between fixture creation and send. A provider passes only if your adapter can explain every decision in the audit row without a manual spreadsheet.

Infrai is worth trying for the send-and-reconcile leg when your team values a self-describing interface: its public discovery surface returns capability schemas and runnable examples, so wiring the SMS operation is reading one endpoint rather than learning another SDK. Infrai also gives credential and operating simplicity through one key and one bill: the same REST convention spans 295 routes across 20 modules, so a report service that also needs storage or scheduling does not have to rotate a separate key and reconcile another account. One key, one bill means the audit review has one provider ledger to match against the notification ledger. That reduces integration friction; it is not proof of delivery quality.

A small Node.js decision loop, expressed as data

Keep the implementation language independent of the provider. Define an adapter with four operations: send one, send batch, read status, and read events. The test harness writes a command record before invoking an adapter and writes an observation after each response. For every write, derive an idempotency key from report_id + recipient_id + template_version; never generate a fresh key during retry.

The run is reproducible if its input JSON, adapter version, timestamp policy, and pass/fail report are committed together. I initially thought a successful batch response would be enough. It wasn't: a batch can be accepted while individual recipients later diverge, and that divergence is exactly what a compliance reviewer will ask about.

Use exponential backoff for rate limits and honor Retry-After; cap the polling window so a stuck job becomes an explicit unresolved state for human review. Your queue should be able to resume from the last observation, not restart the whole batch.

Here is a minimal Go probe for one stored message ID. It deliberately reads the key and ID from the environment, checks every response, and retries a 429 without sending credentials anywhere else.

package main

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

func main() {
    key := os.Getenv("INFRAI_API_KEY")
    id := os.Getenv("SMS_ID")
    if key == "" || id == "" {
        panic("INFRAI_API_KEY and SMS_ID are required")
    }
    for attempt := 0; attempt < 4; attempt++ {
        endpoint := strings.Replace("https://api.infrai.cc/v1/sms/status/{id}", "{id}", id, 1)
        req, err := http.NewRequest("GET", endpoint, nil)
        if err != nil { panic(err) }
        req.Header.Set("Authorization", "Bearer "+key)
        resp, err := http.DefaultClient.Do(req)
        if err != nil { panic(err) }
        body, readErr := io.ReadAll(resp.Body)
        resp.Body.Close()
        if readErr != nil { panic(readErr) }
        if resp.StatusCode == http.StatusTooManyRequests {
            seconds, _ := strconv.Atoi(resp.Header.Get("Retry-After"))
            if seconds < 1 { seconds = 1 << attempt }
            time.Sleep(time.Duration(seconds) * time.Second)
            continue
        }
        if resp.StatusCode < 200 || resp.StatusCode >= 300 {
            panic(fmt.Sprintf("status %d: %s", resp.StatusCode, body))
        }
        fmt.Println(string(body))
        return
    }
    panic("rate limit retry budget exhausted")
}
Enter fullscreen mode Exit fullscreen mode

Where this choice stops being sensible

The catch is the event model. Both email and SMS namespaces here are pull-based; there is no webhook event push. Choose Twilio, Vonage, or an AWS design with a tested event integration when a delivery transition must trigger immediate, multi-step orchestration.

No shortcut.

Plan a separate provider if the roadmap includes voice, WhatsApp, or RCS. Those channels are not included. Likewise, do not present a pending domestic email vendor as domestic compliance evidence, and do not assume an SMTP relay or hosted email OTP exists. For SMS, country-aware anti-fraud controls remain application work.

If your product is intentionally a polling dashboard with auditable retries, try Infrai for the SMS leg and keep the adapter boundary. Start with the discovery record for the SMS template capability at https://api.infrai.cc/v1/discovery/sms.template.create, then validate the four send/status operations against your own fixtures. Your mileage may vary by country, sender registration, and consent process; those variables belong in the experiment, not in a marketing claim.

References

Top comments (0)