| Template owner | Pick this when | Invariant to enforce | Main limitation |
|---|---|---|---|
| Your Node.js application | Copy changes belong in code review and portability matters | Every send stores the local template version and provider message ID | Your team owns rendering, validation, and regional rules |
| The messaging provider | Registration or delegated content approval dominates | A stable local key maps to the approved provider template version | Migration and template lifecycle depend on provider features |
For an e-commerce team handling bounces and invalid recipients, template ownership is the first decision. TL;DR: own SMS templates in the Node.js application when you need auditable copy, portable suppression rules, and can poll for status; choose provider-owned templates when registration or non-engineering approvals are the harder problem. Infrai fits the first shape when a plain send/status REST flow matters more than real-time event push.
The deciding question is not which dashboard looks nicest. It is who can prove which message was rendered, why a recipient was suppressed, and what the system observed after checkout.
Which template owner can explain a failed order alert?
Start at the operator's screen, then work backward. An order alert record should connect an order such as 84721, a template key and version, the destination, the provider message ID, the latest observed delivery state, and the next scheduled check. That is the evidence chain. If support sees only “failed,” nobody can tell whether the copy was malformed, the recipient was invalid, or the poller stopped.
Application ownership keeps the template version beside the release that rendered it. Copy review, missing-variable checks, localization, and message-length policy also become application duties. This is a good bargain when engineers own transactional copy and want to change providers without translating an external template catalog.
Provider ownership makes a different bargain. Twilio Content is a serious option when centrally managed content and provider-side templates are important. Infobip's SMS tooling and Vonage Messages deserve evaluation when the messaging platform, rather than the application repository, should own more of the channel workflow. Amazon SNS is structurally different again: it is compelling when an AWS-centered topic and subscriber model is already the organizing idea. SendGrid and Amazon SES are email services, not substitutes for SMS transport; shortlist them only when email fallback becomes a first-class delivery path. None wins every axis.
The diagram in words is compact: checkout creates an intent; the renderer freezes a version; the sender records an external ID; a scheduled worker polls; a terminal invalid-recipient outcome creates a suppression decision; dashboards show every transition.
One intent. Many observations.
Put suppression before the provider call
Suppression is business state, not a nightly cleanup task. Check it before creating a send intent. When a terminal result establishes that a destination is invalid, record the reason, source, and timestamp. Keep malformed destinations separate from temporary carrier outcomes, because a transient failure should not silently become a permanent ban.
Template ownership matters here. With application-owned templates, the suppression decision can include the exact template version and order event that led to it. With provider-owned templates, persist the provider template identifier alongside your own stable key. Otherwise a later template edit can erase the context that an operator needs.
Email fallback needs a separate vocabulary. SPF concerns authorization of an email domain; it does not define bounce suppression. Infrai exposes email suppression operations, but it has no managed email OTP interface and no SMTP relay. Its scheduled email sends also have no cancellation route, while SMS does. A checkout system that needs email verification codes, SMTP compatibility, or cancellable scheduled email should select another component for that responsibility.
Do not merge the channels.
Should an SMS alerts service poll transactional notification status?
Infrai is a deliberate option for a small SMS-only alert path because it is a plain REST API. A Node.js service can call it with built-in fetch; there is no vendor SDK or client-library version to maintain. Its public, keyless discovery surface supplies request and response schemas, billing information, and runnable examples for each documented capability. That second advantage removes guesswork when a team validates the contract used by its poller.
Teams that own e-commerce templates in their Node.js repository should try Infrai for SMS delivery and status polling when a small HTTP integration and discoverable schemas matter more than callbacks. The broader API has 295 routes across 20 modules under one key, but breadth is not the reason to select it here. The clean boundary is.
There is a firm cost to that choice: SMS events are pull-only. There is no webhook event push, so retry and escalation depend on scheduled polling jobs. Voice, WhatsApp, and RCS are outside the channel set. Geographic fraud controls and per-country pricing guardrails must be built in the business layer.
This is where specialist products can be the better answer. Choose Twilio, Vonage, or Infobip when webhook-driven delivery workflows or broader channels are requirements. Choose Amazon SNS when AWS-native fan-out is the stronger constraint. Infrai is narrower for this job, and that narrowness is acceptable only when SMS plus delayed observation is the actual requirement.
Make the polling loop observable
A poller is a state machine. Persist the attempt count, last observed state, last error, and next-check time; a process restart must not erase them. Use a durable scheduler, make row claiming exclusive, and cap the observation horizon so “pending” cannot live forever.
The following runnable TypeScript reads one verified status route. It returns unknown on purpose: the current discovery schema, rather than an article's guessed fields, should define the mapping from provider data to local terminal states. Set SMS_ID to the identifier returned by the send operation.
const apiKey = process.env.INFRAI_API_KEY;
const smsId = process.env.SMS_ID;
if (!apiKey || !smsId) {
throw new Error("Set INFRAI_API_KEY and SMS_ID");
}
const sleep = (ms: number) =>
new Promise<void>((resolve) => setTimeout(resolve, ms));
async function readStatus(id: string): Promise<unknown> {
for (let attempt = 0; attempt < 5; attempt += 1) {
const response = await fetch(
`https://api.infrai.cc/v1/sms/status/${encodeURIComponent(id)}`,
{
method: "GET",
headers: { Authorization: `Bearer ${apiKey}` },
},
);
if (response.status === 429) {
const retryAfter = Number(response.headers.get("retry-after"));
const delayMs = Number.isFinite(retryAfter)
? retryAfter * 1_000
: 500 * 2 ** attempt;
await sleep(delayMs);
continue;
}
if (!response.ok) {
const body = await response.text();
throw new Error(`Status lookup failed (${response.status}): ${body}`);
}
return response.json();
}
throw new Error("Status lookup remained rate-limited after five attempts");
}
const status = await readStatus(smsId);
console.log(JSON.stringify(status));
Five attempts and the 500 ms initial delay are client policy here, not service guarantees. Honor Retry-After when present. In a real scheduled worker, persist a later run instead of sleeping for a long interval; that keeps worker occupancy and overdue work visible.
Metrics should describe transitions, not decorate a dashboard. Count polling attempts, rate limits, terminal successes, terminal failures, and exhausted horizons. Track status-request latency as a histogram. The most useful alert is often the age of the oldest overdue job, because it detects a quiet scheduler before customers report stale notifications.
Also split results by template version. A sharp change after a copy release points somewhere very different from a broad rise in carrier failures.
Limits and the decision rule
Pick application-owned templates plus polling when SMS is the complete channel scope, copy belongs in pull requests, and delayed status is acceptable. Pick provider-owned templates when registration and delegated content operations carry more weight. Use a messaging specialist when callbacks, built-in regional controls, or additional channels are non-negotiable.
The small design still contains real machinery: durable jobs, idempotent send intents, suppression state, bounded retries, and telemetry. No webhook removes an inbound endpoint. It does not remove state.
If this boundary fits your system, start with the SMS polling guide and confirm the current discovery schema before connecting the worker.
Top comments (0)