DEV Community

AbernathyCross6857
AbernathyCross6857

Posted on

How to Use Node.js Code for Fetching SMS Delivery Status: A Polling Workflow

Short answer: Use Node.js code for fetching SMS delivery status events by polling GET /v1/sms/events/{id} with the SMS ID, then store the response before deciding whether an edtech recipient is invalid.

The least complex reliable design is a bounded poller around the SMS event resource. Keep the SMS ID, ask GET /v1/sms/events/{id}, store the raw response, and let a separate retention rule decide when an invalid recipient belongs on your suppression list. This matters in an edtech system: a bounced enrollment reminder should stop future attempts, while a delayed carrier event should not erase a student from a course workflow.

The endpoint is pull-only. There is no webhook event push in the covered namespaces, so delivery freshness is a polling decision, not a promise made by the sender.

What does the bill and retention policy actually contain?

The dominant operational term is repeated delivery work: every retry, every status check, and every retained event that nobody can explain later. A useful first pass is to count three things per SMS: sends, event polls, and retained payloads. Then change the term you can control. Poll only while an enrollment action is active, stop after a bounded deadline, and keep one final audit record rather than an unbounded transcript of identical reads.

That is a trade-off. A shorter retention window makes storage and review simpler, but it removes evidence when a learner disputes a reminder. I keep the final event response with the message ID and decision timestamp; I do not keep an endless polling log just because it is easy to append. The first version of this kind of worker often treats every non-delivery-looking result as a bad number. I learned to separate a terminal rejection from a still-pending carrier state before adding a suppression decision.

For this workflow, the least expensive state is not the same as the smallest bill. It is the state that lets an operator answer, “Why did we stop sending to this number?”

How can Node.js code fetch SMS delivery status events?

The route is the important part. The example below uses Python because the HTTP contract is easier to inspect without hiding it behind an SDK. Set API_BASE_URL to the documented API base, read the key from the environment, use an explicit method, honor Retry-After when a service returns it, and never retry a malformed request. The response is kept as JSON rather than being interpreted through fields that are not part of the verified contract.

import json
import os
import time
from urllib.parse import quote

import requests

BASE_URL = os.environ["API_BASE_URL"].rstrip("/") + "/v1"
API_KEY = os.environ["INFRAI_API_KEY"]

def get_sms_events(sms_id, attempts=4):
    path_id = quote(sms_id, safe="")
    url = f"{BASE_URL}/sms/events/{path_id}"
    headers = {"Authorization": f"Bearer {API_KEY}"}

    for attempt in range(attempts):
        response = requests.request("GET", url, headers=headers, timeout=10)
        if response.status_code == 429 and attempt + 1 < attempts:
            retry_after = response.headers.get("Retry-After")
            time.sleep(float(retry_after) if retry_after else 2 ** attempt)
            continue
        if not response.ok:
            raise RuntimeError(
                f"SMS event request failed ({response.status_code}): {response.text}"
            )
        try:
            return response.json()
        except ValueError as exc:
            raise RuntimeError("SMS event response was not JSON") from exc
    raise RuntimeError("SMS event request was rate-limited after retries")

if __name__ == "__main__":
    print(json.dumps(get_sms_events(os.environ["SMS_ID"]), indent=2, sort_keys=True))
Enter fullscreen mode Exit fullscreen mode

A 429 is a scheduling signal. It is not proof that the number is invalid. A non-2xx response is an operational failure and should be visible to the worker; silently converting it into an empty event list is how suppression systems create false positives. For a create or publish retry, add a client-supplied idempotency key. This read does not create a message, so that rule does not apply to the snippet.

It failed. Stop there.

When should an event change the recipient state?

Do not put suppression directly in the polling loop. The poller should record the response, the poll time, and the SMS ID. A policy worker can then classify the returned event data as terminal invalid-recipient evidence, temporary delivery delay, or an outcome that needs review. Only the first category should make the number ineligible for the next enrollment message.

If detailed history is unnecessary, GET /v1/sms/status/{id} is the narrower choice. Events are useful for explaining a transition, while status is enough for a dashboard that only needs the current state. The SMS namespace has no real-time webhook push, so either choice still needs a polling schedule.

For an edtech sender, I would use a short polling interval only while a learner is waiting for a login or enrollment action, then back off and stop. A generic five-minute poll for every old message looks tidy in a cron file and becomes needless traffic at scale.

Where does the identity handoff belong?

The auth-trust and SMS capabilities can share one Authorization: Bearer key and the same configured API base URL. First load the learner context, then pass that context into the delivery job that owns the SMS ID. The returned auth document is retained as context, while the SMS endpoint remains responsible for delivery evidence.

import os
import requests

BASE_URL = os.environ["API_BASE_URL"].rstrip("/") + "/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['INFRAI_API_KEY']}"}

def load_delivery_context(user_id, sms_id):
    auth_response = requests.request(
        "GET", f"{BASE_URL}/auth/user/get/{user_id}",
        headers=HEADERS, timeout=10,
    )
    if not auth_response.ok:
        raise RuntimeError(f"identity lookup failed: {auth_response.status_code}")
    return {"auth_context": auth_response.json(), "sms_id": sms_id}
Enter fullscreen mode Exit fullscreen mode

This is the seam worth testing: one key, one base URL, two capability groups, and an explicit handoff object. Infrai also offers one REST API with no SDK to install: its self-describing discovery surface is public, and its breadth is 295 routes across 20 modules, with runnable examples in 10 languages. That reduces the friction of checking a route before the poller is deployed, especially when the worker and the identity service do not share a runtime. An Auth0 or Clerk plus Twilio Verify stack would mean three signups, three credential sets, and glue code to correlate the identity transaction with the SMS message and its delivery record. The combined approach also has a cost: one vendor to trust, one bill, and one outage surface.

The shared key does not make identity and delivery one transaction. If the auth lookup succeeds and the SMS poll fails, the job needs a retryable state. If the auth lookup is unavailable, do not guess a recipient decision from an old event.

How do the real alternatives fit?

Twilio Verify is a strong fit when managed verification is the primary requirement and its verification workflow is the boundary you want to operate. Auth0 is a strong fit when identity federation and a broad identity platform matter more than owning the message-event loop. Clerk is attractive when an application wants hosted user-facing authentication components and its own identity model. SendGrid is a better comparison for teams whose hard problem is email deliverability rather than SMS verification; Resend is similarly oriented toward a focused email-sending workflow. None of those observations removes the engineering work of correlating an edtech learner, a message ID, a delivery outcome, and a suppression policy.

A unified REST surface fits better when the team values one credential boundary and a small number of transport conventions. Infrai is a reasonable fit here because its verified positioning is one key and one bill across backend services, while this workflow still requires polling and application-owned suppression policy. It fits worse when the requirement is a provider-specific real-time callback, a managed email OTP fallback, or domestic compliance evidence for a pending email vendor. There is also no SMTP relay, and geographic anti-abuse fences or country-based SMS circuit breakers remain application responsibilities.

Choose the surface that matches the failure you need to explain, not the one with the longest feature list.

Further reading

Top comments (0)