---
name: optus-send-and-reconcile-sms
description: Send a single SMS through the Optus SMS Suite REST API v2 and reconcile its real delivery outcome from the DLR callback rather than from the submit response.
api: openapi/optus-messages-api-openapi.yml
base_url: https://api.sms.optus.com.au/rest/sms/v2
operations:
  - postMessages
  - getMessages
generated: '2026-10-21'
method: generated
source: https://sms.optus.com.au/docs/en/integrations/api-documentation/rest-v2/
---

# Send and reconcile an SMS (Optus SMS Suite REST v2)

The provider's REST v2 spec declares no `operationId`s, so operations below are named by
method and path exactly as they appear in
`openapi/optus-messages-api-openapi.yml` (server https://api.sms.optus.com.au/rest/sms/v2).

## Before you start

- You need per-application Basic credentials ("application name" + password) issued at
  `https://sms.optus.com.au/gateway/api_config/restv2`. There is no self-service token
  endpoint, no OAuth, and no key-issuance API.
- A **DLR callback URL must already be configured** for the application in the platform's API
  Configuration screen. On REST v2 this is not optional: without it you never receive error
  codes or delivery outcomes at all. The URL is configured per application, never per request.
- If the application has any authorised IP addresses registered, your calling host must be one
  of them or every request fails authentication.
- Destination numbers must be E.164 with a leading `+` and no leading zero, e.g.
  `+61414123456`.

## Step 1 — Submit the message

`postMessages` — `POST /messages` with HTTP Basic auth and `Content-Type: application/json`.

Required body fields (from `components.schemas.SingleMessage`): `destination`, `content`.
Useful optional fields: `source`, `reference`, `mask`, `scheduled`, `class`, `sms_class`,
`integration_id`.

A success is **HTTP 202 Accepted**, not 201 and not 200. The response body is a
`StatusCallback` object carrying the `id` — a UUID.

## Step 2 — Store the UUID before doing anything else

The UUID is the only key that ties the submit to every later event. There is no request-id
header and no other correlation mechanism. If you drop it you cannot reconcile the message.

Set `reference` on submit if you need your own key echoed back — it is returned on retrieval
and on the DLR callback. **`reference` is not an idempotency key.** The API de-duplicates
nothing: if you retry a submit, a second SMS is sent and billed. Guard retries on your side.

## Step 3 — Do not treat 202 as delivered

202 means accepted for processing. Delivery outcome arrives asynchronously on the DLR callback
your application hosts. Handle these `status` values (published DLR set):

| status | meaning |
|---|---|
| `sent` | sent by the carrier transport |
| `received` | received by the handset |
| `rejected` | the carrier rejected the message |
| `expired` | carrier could not deliver in time (e.g. handset off) |

The spec enum in `components.schemas.StatusCallback` is wider than the documented table and
also contains `accepted`, `queued`, `submitted`, `blocked`, `deficient`, `frozen`, `failed`,
`dead`. Treat any unrecognised value as non-terminal and keep the message open rather than
failing hard.

Callback rules the provider states: use HTTPS, the TLS certificate must match the domain,
self-signed certificates will not verify, and any credentials embedded in the callback URL must
be URL-encoded.

## Step 4 — Poll only as a fallback

`getMessages` — `GET /messages?id={uuid}` returns a `MessageDetails` object (`id`, `source`, `destination`,
`direction`, `reference`, `content`, `status`). Use it to reconcile a message whose callback
you may have missed — not as the primary delivery signal. A `404` means no message matched
that id.

## Scheduling

Set `scheduled` to an RFC 3339 timestamp (`YYYY-MM-DDThh:mm:ss±hh:mm`). Two rules are enforced:
`400` if the value is not RFC 3339, `422` if it is in the past. The documented upper bound is
60 days ahead.

## Errors

Submit errors return `send_failed`, `invalid_json`, `missing_attrib`, or `invalid_attrib`
inside an `ApiErrorResponse` (`message`, `statusCode`, `error`). `401` means missing or wrong
credentials, or no permission for this API. Full catalogue across every interface:
`errors/optus-error-codes.yml`.

## What this API does not give you

No rate-limit headers and no published request ceiling — build your own throttle. No
pagination. No bulk send on v2: for multiple destinations use REST v1
`POST /messages/broadcast` (up to 1000) or the Campaign Manager.
