BroadNet
HTTP API & webhooksAPI design

REST messaging API design: resources, methods, status codes

Updated 6 min readStandards-based
Direct answer

A REST messaging API models each send as a resource in a messages collection. Clients POST a message and, because delivery is asynchronous, the server returns 202 Accepted with a message identifier and a status URL, then reports the final outcome over a webhook. HTTP methods and status codes follow RFC 9110 semantics.

DiagramHow an A2P message reaches the phone
  1. Your applicationSends over the REST API or SMPP
  2. BroadNet platformValidates, routes and bills each message
  3. Operator SMSCDirect operator connection
  4. Subscriber handsetThe message is delivered
The delivery receipt (DLR) travels back along the same path to your webhook
§01

The messages resource model

A REST messaging API treats every message as a resource under a collection — conventionally /messages — addressable by a server-assigned identifier. The verb, not a payload field, expresses intent: POST /messages creates and enqueues a new outbound message; GET /messages/{id} reads its current state; GET /messages lists and filters. This is the REST layer that sits above the same operator connections SMPP reaches on the wire, so the resource a client POSTs maps, downstream, to a submit_sm on a bound SMPP session. Because the identifier is minted by the server and returned in the response (and usually echoed in a Location header), the client has a stable handle to poll or to correlate against later status callbacks.

Create a message in the collection (generic REST pattern)http
POST /v1/messages HTTP/1.1
Host: api.example
Content-Type: application/json
Authorization: Bearer <token>
Idempotency-Key: 8f14e45f-ea7b-4c0f-9b2a-1d2c3e4f5a6b

{
  "to": "+15551234567",
  "from": "BRANDNAME",
  "text": "Your one-time code is 401 928",
  "callback_url": "https://app.example/webhooks/dlr"
}
§02

HTTP methods and their guarantees

RFC 9110 defines two properties that govern how a messaging client and any intermediary may treat a request. Safe methods (GET, HEAD) have no side effects and are freely cacheable and retryable. Idempotent methods (GET, HEAD, PUT, DELETE) can be repeated with the same net effect. POST is neither safe nor idempotent, which is exactly the problem for message submission: a retried POST could double-send. The standard remedy is not to abuse PUT but to keep POST and make it idempotent with an Idempotency-Key request header, so a replayed submission returns the original result instead of creating a second message.

HTTP methods on a messages resource (RFC 9110)
MethodOn /messagesSafeIdempotent
POSTCreate + enqueue a new messageNoNo (make it idempotent via Idempotency-Key)
GETRead a message or list the collectionYesYes
HEADMetadata / existence checkYesYes
PUTReplace a mutable resource (e.g. a webhook config)NoYes
PATCHPartial update of a mutable resourceNoNo
DELETECancel a still-queued messageNoYes
§03

Status-code semantics for send

The status code the server returns encodes whether the message was accepted, completed or rejected — and message send is almost always asynchronous. RFC 9110 defines 202 Accepted precisely for this case: the request was accepted for processing but processing is not complete, and the response should describe where to check on it. A messaging API therefore returns 202 for a queued send, reserving 201 Created for a resource that is fully materialised and 200 OK for a truly synchronous operation. Client errors (malformed payload, invalid destination, unknown sender) are 4xx and must not be retried unchanged; server and upstream failures are 5xx and are safe to retry with backoff under the same Idempotency-Key.

Status-code semantics for message submission (RFC 9110)
CodeMeaningWhen a messaging API uses it
202 AcceptedAccepted, processing incompleteAsync send queued; final state comes via callback
201 CreatedResource createdSynchronous create where the resource is complete
200 OKRequest completedSynchronous status read or truly immediate operation
400 / 422Client error / unprocessableMalformed body or invalid field — do not retry unchanged
401 / 403Unauthenticated / forbiddenMissing or insufficient credentials
409 ConflictState conflictIdempotency-Key reused with a different payload
429 Too Many RequestsRate limitedThrottle; honour Retry-After and back off
5xxServer / upstream failureTransient — retry with backoff under the same key
§04

Synchronous vs asynchronous delivery

There are two distinct latencies in a messaging API and it is a design error to conflate them. The API latency is how long the HTTP call takes to accept the message; the delivery latency is how long the operator network takes to hand the message to the handset and return a receipt. Because the second is unbounded and out of the sender's control, a well-designed API acknowledges immediately (202) with an accepted or queued state, then delivers the terminal outcome asynchronously — a delivery-status callback (the HTTP analogue of an SMPP delivery receipt). Clients that need the final state either subscribe to the callback (push) or poll GET /messages/{id} (pull); the callback is preferred because it avoids polling load and reports state changes within seconds.

202 Accepted response with a status handlehttp
HTTP/1.1 202 Accepted
Location: /v1/messages/01HZY8Q3M6
Content-Type: application/json

{
  "id": "01HZY8Q3M6",
  "status": "accepted",
  "created_at": "2026-07-19T09:15:22Z",
  "status_url": "/v1/messages/01HZY8Q3M6"
}
§05

Content negotiation and versioning

Clients and servers agree on representation through the RFC 9110 content-negotiation headers: the request carries Content-Type for the body it sends and Accept for what it wants back, and the server echoes the chosen media type in the response Content-Type. Most messaging APIs standardise on application/json and use application/problem+json for errors. API versioning has two mainstream patterns. URL versioning (/v1/messages) is explicit, cache-friendly and trivial to route, at the cost of a coarse, whole-surface bump. Header or media-type versioning (Accept: application/vnd.example.v2+json) keeps URLs stable and lets a resource evolve independently, at the cost of being invisible in a browser and harder to test by hand. Whichever is chosen, additive changes (new optional fields) should never require a version bump; only breaking changes do.

API versioning approaches
ApproachExampleTrade-off
URL path/v1/messagesExplicit, cacheable, easy to route; coarse-grained bumps
Custom headerApi-Version: 2026-07-19Clean URLs, date-pinned; invisible to caches and browsers
Accept media typeAccept: application/vnd.example.v2+jsonPer-resource evolution; harder to test by hand
Query parameter/messages?version=2Simple; muddies caching and the resource identity
§06

REST vs SMPP: choosing the transport

The HTTP/REST layer and SMPP are two transports onto the same messaging network, and mature senders use both. REST is the right choice for application integration: it is stateless, firewall-friendly over ordinary HTTPS, easy to secure with bearer tokens, and simple to instrument. SMPP is the right choice for very high sustained throughput and the lowest per-message overhead, because a long-lived binary session avoids per-message connection cost and gives fine-grained window control. The table contrasts the two so an integrator can match the transport to the workload; for BroadNet's concrete REST endpoints see the developer API reference, and for the wire protocol see the SMPP overview.

REST (HTTP) vs SMPP as a messaging transport
PropertyREST / HTTPSMPP
ModelStateless request/response over HTTPSStateful long-lived binary session (bind)
FramingJSON over HTTP/1.1 or HTTP/2Binary PDUs (submit_sm, deliver_sm)
Delivery receiptStatus webhook / poll GETdeliver_sm receipt on the same session
ThroughputGood; per-request overheadHighest sustained; windowed, low overhead
AuthBearer token / API key / OAuth 2.0system_id + password at bind
Flow control429 + Retry-After, RateLimit headersWindow size + enquire_link throttling
Best forApp integration, webhooks, moderate volumeCarrier-grade high-volume A2P firehoses
FAQ

Frequently asked

Why does a REST messaging API return 202 instead of 200 or 201?

Because message delivery is asynchronous. RFC 9110 defines 202 Accepted as 'accepted for processing, processing not complete', which exactly describes a queued send. The API acknowledges immediately with a message id and a status handle, then reports the terminal delivery outcome later over a callback. 201 and 200 imply the operation is already complete.

How should a messages resource be modelled?

As a collection: POST /messages creates and enqueues a message, GET /messages/{id} reads its state, and GET /messages lists and filters. The server mints the identifier and returns it (often in a Location header) so the client has a stable handle to poll or to correlate against status callbacks.

POST is not idempotent — how do you avoid double-sends?

You keep POST (it correctly expresses 'create a new message') and make it idempotent with an Idempotency-Key request header. A retried submission carrying the same key returns the original result instead of creating a second message. Abusing PUT to force idempotency breaks the REST resource model.

What is the difference between API latency and delivery latency?

API latency is how long the HTTP call takes to accept the message; delivery latency is how long the operator network takes to reach the handset and return a receipt. The second is unbounded and out of the sender's control, which is why the API acks fast (202) and reports final delivery asynchronously.

Should I version a messaging API in the URL or in a header?

URL versioning (/v1/messages) is explicit, cache-friendly and easy to route but bumps the whole surface at once. Header or media-type versioning keeps URLs stable and lets resources evolve independently but is invisible to caches and harder to test by hand. Either way, additive changes should never force a version bump.

When should I use REST versus SMPP?

Use REST for application integration: stateless, firewall-friendly HTTPS, bearer-token auth and easy instrumentation. Use SMPP for the highest sustained throughput and lowest per-message overhead, where a long-lived windowed binary session pays off. Both reach the same messaging network; many senders run REST for control traffic and SMPP for high-volume firehoses.

References

Source specifications

The published standards this page is grounded in. Opens in a new tab.

BNMaintained by the BroadNet engineering team·Carrier-grade messaging since 2003
Put it on the wire

Build with a real SMPP bind and REST API.

Request test access with real test keys: send to your own verified number first, then move to production across 200+ countries once your sender identity and routes are approved.