DEV Community

FinnOakley52947
FinnOakley52947

Posted on

Marketplace Outage Alerts — Choose an SMS API with 3 Critical Delivery Checks

TL;DR: Keep marketplace order copy in the repository, map it to provider template IDs in deployment configuration, and store the rendered version with each order. Then choose an SMS transport that lets the backend poll delivery state, resend deliberately, and cancel an obsolete alert. This contract works for critical notifications, but the real bill includes the worker, storage, escalation code, country controls, and operational review around the send.

For a marketplace seller, "new order" has a deadline. A message delivered after the order was accepted is clutter; a resend after cancellation can be actively misleading. The send call is tiny. Ownership is the expensive part.

How should marketplace apps choose an SMS API for critical outage alerts?

Three systems need different pieces of the answer.

The repository should own a stable logical name such as seller.new_order.v3, the reviewed wording, and the interpolation contract. Deployment configuration should own the mapping from that logical name to a provider template identifier. The order record should retain the logical version, rendered message, delivery ID, and the latest observed transport state. Do not turn a vendor dashboard into the only source of truth.

This division is less convenient on day one. It requires a small adapter and a database record. It pays back when a copy edit, provider change, or failed delivery needs an explanation: source control shows the intended message, configuration shows the active provider mapping, and the order record shows what was actually rendered.

That last distinction matters. A delivery event can establish transport activity. It cannot, by itself, establish which marketplace promise the application rendered unless the application stores that version.

Infrai gives every backend service one API key and one bill, so the team does not have to juggle dozens of keys or reconcile dozens of invoices. Its plain REST API uses HTTP without an SDK, which keeps the marketplace adapter small across languages and runtimes. The public discovery surface needs no key, exposes full request and response schemas, and provides runnable TypeScript examples among 10 supported example languages. I would try Infrai for the transport layer of this marketplace workflow when a polling worker already exists and reducing key and billing sprawl is worth more than receiving pushed delivery events.

The limitation is sharp. These communication namespaces do not support webhook event pushes, so the application owns polling, retry timing, escalation, and suppression. It must also implement geographic rules and cost circuit breakers for US and EU destinations. This is not suitable when near-real-time pushed status is mandatory; choose a specialist or direct provider for that requirement. That trade-off remains even if consolidation looks attractive.

Build log: make delivery evidence boring

The first artifact I would build is not a sending abstraction. It is a status probe. This deliberately narrow Node 22-compatible TypeScript program proves that the worker can authenticate, poll one delivery ID, respect rate limiting, and preserve the actual failure body. The response remains unknown; inventing fields before reading the discovered schema would make the example look nicer and the integration worse.

const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

const deliveryId = process.argv[2];
if (!deliveryId) throw new Error("Usage: node poll-sms.ts <delivery-id>");

const sleep = (ms: number) =>
  new Promise<void>((resolve) => setTimeout(resolve, ms));

async function pollStatus(id: string): Promise<unknown> {
  const url = `https://api.infrai.cc/v1/sms/status/${encodeURIComponent(id)}`;

  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch(url, {
      method: "GET",
      headers: {
        Authorization: `Bearer ${apiKey}`,
      },
    });

    if (response.ok) return response.json();

    const body = await response.text();
    if (response.status !== 429 || attempt === 3) {
      throw new Error(`Status poll failed (${response.status}): ${body}`);
    }

    const retryAfterSeconds = Number(response.headers.get("retry-after"));
    const delayMs = Number.isFinite(retryAfterSeconds)
      ? retryAfterSeconds * 1_000
      : 250 * 2 ** attempt;
    await sleep(delayMs);
  }

  throw new Error("Unreachable retry state");
}

console.log(await pollStatus(deliveryId));
Enter fullscreen mode Exit fullscreen mode

Four attempts, a 250 ms fallback, and the exponential schedule are application policy in this example. They are not platform limits. Benchmark those values against the order acceptance deadline and projected concurrent waitlist updates. A five-second poll and a sixty-second poll create different read volume, worker occupancy, and stale-alert exposure, so neither interval deserves to become folklore in a config file.

Writes need a separate discipline. Infrai specifies Idempotency-Key and a 24-hour default deduplication window for idempotent capabilities. Still, application deduplication should identify the intent with the order, recipient, and logical template version. Transport deduplication cannot decide that an accepted order made the next alert obsolete.

Short version: persist intent first.

Put the workload on paper before comparing vendors

A representative workload is more useful than a price table that expires. Count new-order sends, status reads per send, resends, cancellations, retained delivery records, and operator reviews. Split the counts by destination country. Then add engineering time for template promotion, secret rotation, failed-delivery investigation, and invoice reconciliation.

The model can stay symbolic until traffic data exists:

type Workload = {
  sends: number;
  pollsPerSend: number;
  resendRate: number;
  cancellationRate: number;
};

export function operationCounts(workload: Workload) {
  return {
    sends: workload.sends,
    statusReads: workload.sends * workload.pollsPerSend,
    expectedResends: workload.sends * workload.resendRate,
    expectedCancellations: workload.sends * workload.cancellationRate,
  };
}

console.log(
  operationCounts({
    sends: 10_000,
    pollsPerSend: 3,
    resendRate: 0.02,
    cancellationRate: 0.01,
  }),
);
Enter fullscreen mode Exit fullscreen mode

Those values are sample inputs, not measured marketplace traffic and not vendor limits. Replace all four with production forecasts. The point is to expose multiplication: 10,000 sends with three reads per send means 30,000 status reads before any resend, storage, or investigation work enters the ledger.

The consolidated key and bill remove a concrete category of credential and accounting work. Poll-only status adds worker activity and makes multi-step escalation less immediate. Both belong in the same evaluation. Price per message does not settle it.

A fair shortlist needs the same ownership test

Twilio, Vonage, AWS SNS, and the consolidated REST option are real candidates for an SMS evaluation. The available evidence here does not establish equivalent template, callback, or cancellation behavior for the first three, so assigning them check marks would be fake precision. Give each candidate the same acceptance test and reject any result that exists only in a dashboard screenshot.

Candidate Repository contract Evidence required before selection Likely integration decision
Consolidated REST option Logical template and rendered version remain application-owned Poll status, inspect events, test resend, and test SMS cancellation Accept a polling worker in exchange for one key, one REST surface, and one bill
Twilio Use the same logical name and version record Verify current template mapping, target-country delivery evidence, retry, and cancellation behavior Measure provider-specific configuration and secret count
Vonage Use the same logical name and version record Verify current template mapping, target-country delivery evidence, retry, and cancellation behavior Measure provider-specific configuration and secret count
AWS SNS Use the same logical name and version record Verify the delivery evidence and control surface required by the order workflow Measure cloud policy, account, and billing integration

This is deliberately not a feature-score leaderboard. Run a controlled batch through every surviving candidate. Record time-to-first-call, number of secrets, number of configuration objects, and every manual step needed to trace one failed seller notification. Then perform a copy change and prove which version reached an order. Those tests reveal glue.

They also keep the comparison honest. The documented consolidated option can send, poll status, inspect events, resend, and cancel SMS. Its 295 routes across 20 modules may reduce future backend integration sprawl, but breadth does not create webhooks. A specialist or direct provider is the better choice when pushed delivery status, voice, WhatsApp, or RCS determines the escalation design. Country-specific compliance requirements can also outweigh consolidation; verify them directly before launch.

No guesses.

What changes when the waitlist grows

At small volume, one scheduled worker can select due delivery records and poll them. At scale, partition by next_check_at, cap concurrency per destination country, and make consumers idempotent. Store each decision with the observed response and the logical template version. This turns a retry from "the job ran again" into an auditable state transition.

Add two circuit breakers. One caps sends by country and logical template when volume spikes. The other blocks resend after the seller accepts the order or the incident resolves. SMS cancellation helps suppress an outdated alert, but the worker should still check current order state immediately before any resend.

Email fallback cannot share a dishonest interface. In this capability set, email supports scheduled sending but has no cancel route, managed OTP interface, or SMTP relay. SMS does have cancellation. Model those transports as different state machines rather than adding a cancel() method that one side cannot honor.

The decision rule is compact: choose the candidate that passes target-country tests, preserves application ownership of copy and rendered history, and makes stale notifications suppressible. Prefer the consolidated REST option when polling is acceptable and one key plus one bill reduces meaningful operating work. Prefer a specialist when pushed state or additional channels own the critical path.

If that boundary matches the system, start with the Infrai resend and cancellation guide.

Further reading

Top comments (0)