HTTP SMS API (JSON) Possible errors and how to handle them
This document analyzes the possible errors in the JSON over HTTP/HTTPS exchange between your application and Ozeki SMS Gateway. It is split into two parts: the errors that originate on the client side (your application and its network connection), and the errors reported by the SMS Gateway. For each error it explains how to handle it, so that sending and receiving SMS messages stays reliable and safe.
The API has three failure domains:
server side errors:
- the SMS Gateway rejects requests
- the SMS Gateway has queue problems
- the mobile network is down
- callback delivery fails
client side errors:
- bad requests
- lost state
- wrong assumptions
- callback endpoint bugs
network errors:
between the two:
- timeouts
- resets
- TLS failures
- lost responses
- lost callbacks
An important asymmetry:
-for send / sendquery / receive / delete the client initiates
the request,
- for delivery reports and incoming SMS the gateway initiates and POSTs callbacks to the client's URL.
- Each direction needs its own error handling.
Possible Client errors and how to handle them
Client-side errors are the mistakes your application can make when constructing requests, handling responses, and operating its callback endpoint. Most of them are preventable with validation, persistence, and idempotent processing.
API key handling errors
- Never put the API key in client-side code, repositories, or logs. Use a secrets manager.
- Use one API user + key per application, so keys can be revoked independently.
- Rotate keys periodically; the gateway immediately supersedes the old key if a new key is generated.
- Coordinate rotation with the application: retire the old key only after the new one is verified.
Common implementation errors
| Error | How to handle |
| Wrong Content-Type | Always send application/json; charset=utf-8 . |
| Missing action or misspelled actions | Validate the action name client-side before sending; unknown actions are rejected as HTTP 4xx. |
| Invalid recipient format | Validate E.164 phone numbers client-side before sending. |
| Message length/encoding | GSM 7-bit fits about 160 characters, UCS-2 (emoji, non-Latin) about 70; longer messages become multipart. Encode as UTF-8 and be aware of the character count limits. |
| limit outside 1–1000 | Results in HTTP 4xx. Validate the range client-side before the request. |
| statuscode parsed as string | statuscode is a JSON number, not a string. Parse it as an integer, otherwise comparisons such as 0 fail. |
Response handling errors (most common source of bugs)
- Partial acceptance: a batch send can return a mix of statuscode 0 and 1 . You MUST check every array element, not just the HTTP response.
- Ordering assumption: the response array order matches the request order (per spec), but always match by recipient / clientref /position defensively and verify the count matches.
- Missing messageid: if a response element lacks messageid , or the HTTP response is lost after acceptance, you cannot correlate. This is why you must persist clientref and use it for dedupe and reconciliation.
- Timestamp parsing: timestamps are ISO 8601 with UTC offset. Parse them as UTC, never as local time.
- Duplicate delivery of callbacks: the same messageid may arrive multiple times; processing must be idempotent, keyed by messageid .
- Ignoring errormessage : it carries the real cause (e.g. "No subscriber found by this MSISDN"). Log it in structured form.
Callback endpoint (receiver) errors
- Not verifying the gateway: callbacks carry the same Bearer API key. Verify it, otherwise anyone can POST fake incoming/report callbacks.
- Acking before processing: return HTTP 2xx with incomingresp / accepted (11) only after you have durably stored the message; otherwise a crash loses data the gateway will delete.
- Not acking: if you return non-2xx or rejected (12), the gateway keeps the message and may retry. Use this deliberately, not accidentally.
- Synchronous slow processing: respond fast and process asynchronously, to avoid holding the gateway connection.
- Crash between receive and delete (polling mode): receive does not delete the downloaded messages. If you crash after download but before delete , the same messages are returned again, so you must dedupe by messageid when processing.
Client network errors
| Error | When | How to handle |
| DNS failure | Cannot resolve the gateway host | Retry with backoff; alert if persistent. |
| TCP connect timeout / connection refused | Gateway down, port blocked (9508/9509) | Retry with backoff; check firewall/proxy. |
| TLS handshake failure | Expired, self-signed or invalid certificate on HTTPS port 9508 | Fix the certificate. Never disable verification (except in controlled development). |
| Connection reset / half-open connection | Load balancer, gateway restart, keep-alive expiry | Treat as retryable; ensure idempotency. |
| Response lost after processing | Client timeout set too low, proxy issue | The request may have been accepted. Do NOT blindly re-send; reconcile via sendquery or rely on clientref dedupe. |
| HTTP used on port 9509 | API key and message content sent in cleartext | Production MUST use HTTPS (9508); use HTTP only in isolated dev networks. |
HTTP client configuration
- Set explicit connect/read/write timeouts (e.g. connect 10s, response 30s).
- Use connection pooling but tolerate resets; retry only safe, idempotent operations.
- Do not retry send at the transport level without dedupe: a retry after a lost response can double-send. Prefer retrying with the same clientref ; if the gateway dedupes on clientref you are safe, otherwise reconcile with sendquery and match messageid s.
Client-side reliability procedures
- Idempotency by clientref - attach a unique client reference (≤64 chars) per message; use it to dedupe callbacks and to reconcile.
- Persist before acting - store the message and reference before the HTTP call; store the messageid immediately after. On crash, recover from the local store, not from memory.
- Bounded retry with backoff - retry transport errors and statuscodes 1/7 with exponential backoff + jitter (e.g. 1s, 2s, 4s, 8s, max 5 tries), then move to a dead-letter queue with alerting.
- Reconciliation job - a scheduled task that runs sendquery for all messages stuck in acceptedfordelivery / submitted beyond an SLA deadline (e.g. 2h) and alerts on deliveryfailed / submitfailed .
- Polling fallback for incoming - even with registerurl , run a periodic receive on the inbox to catch lost callbacks; dedupe by messageid .
- Delete discipline - only delete sent messages after final state (4/8), and only delete inbox messages after durable processing. Verify deleteresp shows deleted (13) and alert on notfound (14) if you expected the message to exist.
- Rate limiting client-side - throttle send volume and polling frequency (e.g. receive no more than every few seconds, limit 100–1000) to avoid hammering the gateway.
- Backup/DR - the gateway outbox/inbox is a queue, not a durable store for you; your application's store is the system of record. Back it up and keep logs of raw request/response payloads (sanitized).
- Testing - contract tests against the documented JSON; error-injection tests (401, 5xx, timeouts, malformed JSON, callback duplicates, crash between receive/delete); verify behavior with a test gateway and a test SIM/number.
Client-side safety procedures
- HTTPS only in production (port 9508). Never transmit the Bearer key over HTTP.
- Secrets management - API key in a secrets manager or server-side environment config; rotated on schedule or on compromise; per-application keys; never in logs, exceptions, or client bundles.
- Sanitize logs - do not log full message bodies, phone numbers (PII), or the API key; log hashed identifiers and status codes. SMS content is personal data (GDPR): minimize retention and control access.
- Validate all inputs - recipient format, message length/type, limit bounds; treat gateway responses as untrusted (validate action , statuscode types, UUID format).
- Containment - if the key is suspected compromised: revoke the key, rotate it, inspect sendquery /logs for abuse, and alert.
Possible SMS Gateway errors and how to handle them
Server-side errors are reported by Ozeki SMS Gateway itself: HTTP-level rejections, per-message status codes in JSON bodies, and problems delivering callbacks to your URL.
HTTP-level rejections
| Error | When | How to handle |
| HTTP 401 | Missing, invalid, revoked or expired API key ( Authorization: Bearer ... ) | Check that the key is correct and not rotated out; alert immediately (security event); never retry blindly - verify the key before retrying. |
| HTTP 4xx (validation) | Malformed JSON, unknown action , missing recipient / message , invalid folder , limit outside 1–1000, invalid UUIDs in messageids | Treat as a bug: validate the payload client-side first, log the exact body, and fix request generation. Do not retry. |
| HTTP 5xx | Gateway overload, internal queue/database failure | Retry with exponential backoff + jitter; if persistent, raise an alert. |
| Timeout / no response | Gateway busy or connection hangs | Retry after backoff; use clientref so a retry cannot duplicate a message that was actually accepted. |
Status codes reported in JSON bodies (not HTTP status)
The gateway can accept the request but report per-message failures. These codes arrive in the JSON response, so you must inspect the body of every response element:
| code | statusmessage | Meaning | Handling |
| 0 | acceptedfordelivery | In outbox queue | Normal; track via sendquery or callbacks. |
| 1 | notacceptedfordelivery | Rejected, check message | Fix payload; retry if transient. |
| 2 / 3 | submitted / partiallysubmitted | Sent to network | Normal. |
| 4 / 5 | delivered / partiallydelivered | Delivered to handset | Normal (final for success). |
| 7 | submitfailed | Submission failed | Apply retry policy (bounded retry with backoff); inspect errormessage . |
| 8 | deliveryfailed | Network could not deliver | Do NOT retry automatically with the same parameters; log errormessage (e.g. "No subscriber found") and notify operations. |
| 13 | deleted | Delete succeeded | Normal. |
| 14 | notfound | messageid unknown to gateway | Message already deleted, or never existed; reconcile client records. |
| 11 / 12 | accepted / rejected | Incoming callback ack result | Return accepted (11) for messages you will process; rejected (12) only if you cannot process - the gateway will not delete it. |
Callback (reporturl / incomingurl) delivery problems
- The gateway may retry callbacks with an undocumented policy - assume at-least-once delivery and make your endpoint idempotent (dedupe by messageid ).
- If the gateway cannot reach your URL, delivery reports and incoming SMS can be delayed or lost; use sendquery / receive polling as a reconciliation fallback.
- Unknown actions/fields: the spec says receivers MUST ignore unrecognized fields - do that to stay forward compatible.
Gateway behaviors to verify with the gateway operator
- Callback retry count/backoff, and whether a 4xx vs 5xx response changes retry behavior.
- Whether rate limiting / throttling exists (not documented - assume there is some).
- Retention of messages in outbox/inbox and queue capacity limits.
- Behavior on gateway restart: are queued ( statuscode 0 ) messages lost or resubmitted? If lost, you MUST reconcile with sendquery .
Monitoring and alerting on gateway errors
- Metrics: HTTP 401 count, 5xx rate, retry count, dead-letter queue depth, callback lag (last callback timestamp per message), stuck-message count; alert on anomalies.
- Run a periodic receive poll as a fallback and monitor gaps in expected callbacks (callback lost between gateway and app, e.g. ISP issues or firewall).