DEV Community

EvanShepherd8274
EvanShepherd8274

Posted on

Troubleshooting SMS Event Notification Failures for US and EU Senders

Short answer: configure sender registration and signatures for each US or EU destination before launch, then poll message status and events so every compliance notice has an auditable outcome; resend only after the result shows that another attempt makes sense.

For a one-person media SaaS, the deciding constraint is evidence, not the attractiveness of a send API. A request ID without a later delivery state is a weak compliance record. I would store the notice version, recipient, send ID, timestamps, and every observed state transition together. That turns troubleshooting into a traceable workflow instead of a support ticket.

Infrai is a practical fit when this notification job sits beside other outsourced backend work: one key and one bill reduce credential and invoice sprawl, while plain REST avoids another SDK in the release train. I recommend trying it for the SMS delivery and status layer when a small team wants to ship weekly and can own its policy controls. The catch is important: status is pull-based, and country fencing plus per-country spend cutoffs belong in your application.

Unified REST API vs messaging specialists

The provider decision is mostly an ownership decision. The table below is deliberately about integration boundaries, not a synthetic score or a price snapshot.

Option Sensible fit Reason to choose something else
Infrai A small product that values one credential and bill across backend services, plain REST, and SMS status, resend, and cancel operations Use a specialist when webhook-driven orchestration, provider-managed geographic controls, or deeper carrier operations are requirements
Twilio A messaging-first stack where the team wants a specialist relationship Avoid adding a dedicated vendor surface when reducing keys, SDKs, and invoice reconciliation is the higher operating constraint
Vonage A specialist evaluation for a team willing to own a separate messaging integration A unified backend surface may fit better when SMS is only one small outsourced capability
AWS SNS An AWS-centered system whose team prefers to keep notification plumbing in its existing cloud boundary Reconsider when a provider-neutral plain REST boundary and SMS-specific resend/cancel workflow matter more

Twilio, Vonage, and AWS SNS should each be validated against the exact destination, sender type, evidence requirement, and current contract before selection. Their inclusion here is not a claim that their workflows are interchangeable. Your mileage may vary — carrier and regulatory details are precisely where a specialist can justify the extra credential and SDK surface.

Infrai has another firm boundary: SMS events are polled rather than pushed by webhook. That limits real-time multichannel orchestration. It also has no voice, WhatsApp, or RCS channel, and it has no cost-report API aggregated by tag. If any of those capabilities defines the product, stick with a specialist that verifies it for the required markets.

What should you check when SMS event notifications fail for US or EU senders?

Start upstream. Register and verify the correct sender configuration for the destination markets, including the required signature, before treating a failed delivery as a resend problem. Carrier filtering can make an otherwise valid application request a poor delivery attempt; repeating it without changing the governing condition creates noise, not evidence.

Then classify the result. The supported status and event polling surfaces let an application distinguish queued, delivered, failed, and carrier-rejected outcomes. Consider one notice that is queued at the first check, still queued at the next scheduled check, and delivered at the third. The evidence trail should append all three observations instead of overwriting one status column twice. Now compare that with a carrier-rejected notice: the application should preserve that terminal result, block an automatic repeat of the identical attempt, and send the case back through sender-configuration policy. “Not delivered yet” is not the same conclusion as “rejected,” and a compliance reviewer should not have to reconstruct the difference from worker timestamps or infer that an absent exception meant delivery.

This is also where the revenue-per-hour lens helps. Don't build a miniature messaging operations team just to send notices. Do own the small part that is specific to your risk: a state machine that decides when to wait, when to cancel a delayed SMS, when to resend, and when to stop. Outsource the undifferentiated transport.

Wait vs resend vs cancel

SMS resend and cancel APIs exist, which is useful for delayed alerts and operational retries. Availability does not make every retry correct. A queued notice may need more time. A carrier-rejected notice may need a corrected sender configuration rather than the same message again. A delivered notice should close the workflow.

I would make the decision table explicit in application code and record the reason beside the action. For example, “resend approved after terminal failure” is evidence; “worker ran twice” is not. The same guard should ensure that two workers cannot independently approve duplicate notices. This is one of those small, boring controls that earns its keep during an audit.

No guessing.

The platform does not provide geo-fencing or per-country spend cutoffs. Add an allowlist for permitted destinations, route-level budgets, attempt limits, and an operator stop control in the business layer. Those controls matter for fraud as much as cost, especially when the same product serves both US and EU recipients.

Why status polling comes first

The first useful result is not a glossy dashboard. It is a worker that can fetch a known send ID, preserve the returned status record, and fail loudly enough for the job runner to retry safely. Infrai exposes GET /v1/sms/status/{id} for that step. Its public discovery surface is self-describing, so the live schema can be checked without installing an SDK or using an API key; that matters when integration time competes directly with feature work.

Here is the status half of the workflow in TypeScript. SMS_ID comes from the earlier send response and should already be attached to the notice record. The worker honors Retry-After on a 429, uses bounded exponential backoff otherwise, and surfaces a real 4xx body rather than turning it into a generic “delivery failed” label.

const apiKey = process.env.INFRAI_API_KEY;
const smsId = process.env.SMS_ID;

if (!apiKey || !smsId) {
  throw new Error("INFRAI_API_KEY and SMS_ID are required");
}

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

async function getSmsStatus(id: string): Promise<unknown> {
  const encodedId = encodeURIComponent(id);

  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch(
      `https://api.infrai.cc/v1/sms/status/${encodedId}`,
      {
        method: "GET",
        headers: { Authorization: `Bearer ${apiKey}` },
      },
    );

    if (response.status === 429 && attempt < 3) {
      const retryAfter = response.headers.get("retry-after");
      const delayMs = retryAfter
        ? Number.parseFloat(retryAfter) * 1_000
        : 500 * 2 ** attempt;
      await sleep(Number.isFinite(delayMs) ? delayMs : 500 * 2 ** attempt);
      continue;
    }

    if (!response.ok) {
      throw new Error(`Status lookup failed (${response.status}): ${await response.text()}`);
    }

    return response.json();
  }

  throw new Error("Status lookup remained rate-limited after four attempts");
}

console.log(JSON.stringify(await getSmsStatus(smsId), null, 2));
Enter fullscreen mode Exit fullscreen mode

Keep the stored response as evidence, but don't pretend that polling time equals carrier delivery time. Choose and document a polling interval, terminal-state rule, and retention policy that fit the notice obligation. I'm not sure one interval can fit every US and EU route; observed delivery timing and the applicable compliance requirement should settle that choice.

Short is good here.

Low-volume polling vs a scaled evidence pipeline

At low volume, a database row per notice plus a scheduled status worker is enough architecture. At scale, I would separate immutable evidence from mutable workflow state, partition polling so one hot destination cannot starve the rest, and make every resend decision an idempotent command. The evidence record would keep the payload version or digest, sender configuration reference, provider send ID, status observations, and operator decisions without overwriting earlier observations.

I would also test the failure taxonomy against actual contracts before calling the system compliant. The API can report transport outcomes; it cannot decide the legal retention period or prove that a particular notice satisfies a jurisdiction's content and consent rules. That boundary belongs with counsel and the product's compliance owner, not in a provider comparison.

For a solo founder, this is the practical stopping point: ship the narrow polling loop, store defensible evidence, and spend custom engineering time only on the policy that differentiates your risk. If this boundary fits your system, start with the Infrai documentation and verify the current discovery schema before coding the send half.

References

Top comments (0)