# Idempotency & safe retries.

- Canonical URL: https://www.broadnet.me/developers/idempotency
- Language: en
- Last updated: 2026-10-21
- Publisher: BroadNet (https://www.broadnet.me)
- Markdown version of the page above, generated from the rendered page.

Reliability· /idempotency

A network timeout doesn't tell you whether your message was sent. The BroadNet REST API does not take an idempotency key, so duplicate protection lives in your integration: record each send before you make it, keep the ID the API returns, and check the status before you send again.

4 min read · Updated September 2026 [Developer Hub](https://www.broadnet.me/developers)

On this page

## Overview

A network timeout doesn't tell you whether your message was sent. The BroadNet REST API does not take an idempotency key, so duplicate protection lives in your integration: record each send before you make it, keep the ID the API returns, and check the status before you send again.

## What the API gives you

Every accepted send returns an identifier. POST /sms/sendSms returns the message ID as the response body, POST /sms/sendbulk returns a bulkSessionId, and POST /whatsapp/message/sendbulk returns a batchId. Once you hold that identifier, the message was accepted: never send it again.

Store the identifier against your own operation ID (an order number, a login attempt, a campaign) so that a retry from anywhere in your stack can see that the work is already done.

Node.js

```
// 1. Record the attempt first, keyed by your own operation ID.
await db.sends.insert({ opId: "order-8842-confirm", status: "pending" });

// 2. Send.
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({
    senderId: "BroadNet",
    destinationNumber: "447700900123",
    message: "Your order 8842 is confirmed",
    messageType: "TEXT",
  }),
});

// 3. Keep the message ID. With it, the send is done.
if (res.ok) {
  const messageId = await res.text();
  await db.sends.update({ opId: "order-8842-confirm" }, { status: "accepted", messageId });
}
```

## When a request times out

The risky case is a request that reached BroadNet but whose response never reached you. Before you send again, look for evidence that the first attempt was accepted: a delivery status for that number and text on your webhook, or, for bulk jobs, a new entry in GET /sms/list.

Derive operation IDs from your own records

An ID like order-8842-confirm ties the send to a business event, so a queue redelivery or a restarted worker finds the existing record instead of sending a second message.

## What each outcome means

Decide what to do from the outcome of the call, not from a fixed retry count.

| Outcome | What to do |
|---|---|
| 200 with a message ID | Accepted. Store the ID and never resend. |
| 400 or 401 | Rejected, nothing was sent. Fix the request or the token, then send. |
| 5xx | Not accepted. Retry with exponential backoff. |
| Timeout or connection reset | Unknown. Check delivery status before you resend. |

## Best practices

- Write the attempt to your database before you call the API, and the returned ID after.
- Treat a stored message ID or batch ID as proof of acceptance.
- Retry 5xx responses with capped exponential backoff; see Retries & backoff.
- After a timeout, check delivery status before sending the same message again.

## Related documentation

[Reliability Retries & backoff /developers/retries](https://www.broadnet.me/developers/retries) [Reference API reference /developers/api](https://www.broadnet.me/developers/api) [Reference Error format /developers/error-codes](https://www.broadnet.me/developers/error-codes) [Guide Authentication /developers/authentication](https://www.broadnet.me/developers/authentication)

Build on the wire

## Test access within one business day.

[Request test access](https://www.broadnet.me/signup) [Back to docs](https://www.broadnet.me/docs)
