BroadNet
Reliability·/retries

Retries & backoff.

Retry the failures that can succeed later, such as server errors (5xx), and fix the ones that can't, such as validation and authentication errors (4xx). Back off exponentially with jitter, cap the number of attempts, and check status before resending anything that may already have been accepted.

5 min read·Updated September 2026Developer Hub

Overview

Retry the failures that can succeed later, such as server errors (5xx), and fix the ones that can't, such as validation and authentication errors (4xx). Back off exponentially with jitter, cap the number of attempts, and check status before resending anything that may already have been accepted.

What to retry

Match your retry policy to the error class. Errors come back as an HTTP status code with an ExceptionResponse body (status, error, message, errorCode, requestUri). Client errors mean the request itself is wrong, so retrying it unchanged will fail again. Server errors are usually transient.

StatusMeaningRetry?
400Validation error or missing required fieldNo, fix the request
401 / 403Missing, invalid or unauthorised Bearer tokenNo, fix the token
500 / 502 / 503 / 504Server-side or upstream errorYes, with backoff
Timeout / connection resetNo response receivedOnly after checking status

Exponential backoff with jitter

For 5xx responses, wait progressively longer between attempts and add random jitter so a fleet of clients doesn't retry in lockstep. Cap both the delay and the number of attempts, then surface the failure to your own alerting.

Node.js
async function sendWithRetry(body, maxAttempts = 5) {
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const res = await fetch("https://api.broadnet.me/sms/sendSms", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.BROADNET_TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
    });

    if (res.ok) return res.text(); // message ID

    // 4xx: the request is wrong. Retrying won't help.
    if (res.status < 500) throw new Error(await res.text());

    // 5xx: back off exponentially, with jitter.
    const delay = Math.min(30000, 2 ** attempt * 500) + Math.random() * 250;
    await new Promise((r) => setTimeout(r, delay));
  }
  throw new Error("Exhausted retries");
}

Timeouts: check before you resend

The dangerous case is a request that succeeded on the server but whose response never reached you. The API does not take an idempotency key, so an automatic retry after a timeout can send the message twice. Record every attempt, keep the message ID from each 200 response, and check delivery status before you resend. The Idempotency guide shows the pattern.

Handling delivery webhooks

Delivery status is pushed to the webhook URL configured on your account. Reply with a 2xx quickly and do the heavy work asynchronously, and make your handler safe to run twice for the same status update.

Interpreting delivery failures

A message can be accepted by the API yet fail later at the operator, reported as a FAILED or REJECTED status. SMPP integrations receive a numeric error code in the deliver_sm status. Use the SMPP error-code lookup to translate those codes into a cause and a recommended action before deciding whether a resend is worthwhile.

Related documentation

Build on the wire

Test access within one business day.