Skip to content
Browse docs
Open in ChatGPTView as Markdown

#Errors

The API uses conventional HTTP status codes to signal success or failure, and returns a consistent JSON error object on every failed request. Read the status code first to decide what to do, then read the error body for detail.

#The error object

Every error response has the same shape: a top-level error object with a machine-readable type and a human-readable message.

{
  "error": {
    "type": "invalid_request",
    "message": "field 'to' must be an E.164 phone number"
  }
}
  • type — a stable string you can branch on in code (e.g. invalid_request). Match on this, not on the message text.
  • message — a description meant for logs and developers. It is not localized and may change; never parse it.

Branch your code on the HTTP status code and on error.type. Treat error.message as a human-readable hint only — its wording is not part of the contract.

#Status codes

StatusMeaningWhen it occurs
200 OKSuccessA GET succeeded, e.g. retrieving a message or contact.
201 CreatedSuccessA resource was created, e.g. a message was accepted for delivery or a contact was created.
400 Bad RequestClient errorThe request was malformed — invalid JSON, or a missing/unsupported Content-Type.
401 UnauthorizedAuth errorThe API key is missing, malformed, or revoked. See Authentication.
404 Not FoundClient errorThe resource ID does not exist or is not on your account (e.g. an unknown message ID).
422 Unprocessable EntityValidation errorThe request was well-formed but failed validation — e.g. a non-E.164 number or an empty body.
429 Too Many RequestsRate limitedYou exceeded your request rate. Back off and retry — see Rate limits.
500503Server errorSomething went wrong on our side. These are transient; retry with backoff.

A 2xx for POST /messages means the message was accepted for delivery, not that it has reached the handset. Final delivery is reported asynchronously via the message status field and delivery-receipt webhooks.

#Example responses

A validation failure on POST /messages returns 422 with a validation_error type:

{
  "error": {
    "type": "validation_error",
    "message": "field 'to' is required"
  }
}

A missing or revoked key returns 401:

{
  "error": {
    "type": "unauthorized",
    "message": "missing or invalid API key"
  }
}

Requesting an unknown ID from GET /messages/{id} returns 404:

{
  "error": {
    "type": "not_found",
    "message": "no message with id 'msg_does_not_exist'"
  }
}

#Handling errors

Check the status code, then read error.type for cases you handle explicitly. The pattern below treats 4xx as caller mistakes to fix, 429 and 5xx as retryable, and surfaces the error object on everything else.

async function sendMessage(payload) {
const res = await fetch("https://api.netexem.com/v1/messages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.NETEXEM_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(payload),
});

if (res.ok) return res.json();

const { error } = await res.json();

if (res.status === 401) {
  throw new Error(`Auth failed: ${error.message}`);
}
if (res.status === 422 || res.status === 400) {
  // Caller mistake — fix the request, do not retry as-is.
  throw new Error(`Invalid request (${error.type}): ${error.message}`);
}
if (res.status === 429 || res.status >= 500) {
  // Transient — safe to retry with backoff. See /docs/rate-limits.
  throw new Error(`Retryable error (${res.status}): ${error.message}`);
}

throw new Error(`Unexpected ${res.status}: ${error.message}`);
}

#Which errors to retry

  • Do not retry 400, 401, 404, or 422 — the request will fail again until you fix it.
  • Retry 429 and 5xx with exponential backoff. For 429, honor the Retry-After header. See Rate limits for the recommended loop.

Log the full error object (type and message) alongside the request ID from your own system. The type lets you aggregate failures; the message speeds up debugging.

#Next steps