Errors
Errors and limits
Every error response has the same shape:
{
"error": {
"code": "RECIPIENT_OPTED_OUT",
"message": "The recipient +923001234567 opted out of marketing messages. Send it as transactional or remove the opt-out.",
"details": {"address": "+923001234567", "suppressed_at": "2026-10-20T09:14:00Z"},
"request_id": "01923f80-5c6a-7b10-9c21-0d3e4f5a6b7c",
"doc_url": "https://mycomm.cogneer.tech/docs/errors#recipient-opted-out"
}
}
codeis stable. Branch on it. New codes can appear, so keep a fallback on the HTTP status.messageis for people. It says what happened and what to do, and its wording can change. Never parse it.detailsholds the facts the code needs, with the same keys every time. The keys of each code are listed in the error reference.request_idis also in theMyComm-Request-Idresponse header. Quote it when you ask for help.doc_urlopens the code's entry in the error reference.
What each status means#
| Status | Meaning | What to do |
|---|---|---|
| 400 | The request cannot be read (MALFORMED_REQUEST) or a header is wrong (INVALID_HEADER, with details.header) |
Fix the request; do not retry unchanged |
| 401 | No usable key (UNAUTHENTICATED; details.reason is missing_key or malformed_key when the key is absent or has a typo) |
Send Authorization: Bearer <key> with a complete key |
| 403 | The key is fine but may not do this (ability, IP list, account state) | Use a key that may, or fix the account state |
| 404 | The resource does not exist, belongs to another account or is outside the key's scope | Check the id |
| 409 | A conflict with current state: a repeated reference_id, a reused Idempotency-Key, a name already taken |
Read the code; often the first request already worked |
| 413 | The body is too large (JSON over 1 MB, uploads over 21 MB; details.max_bytes) |
Send less per request |
| 415 | The Content-Type is missing or wrong (details.expected) |
Send Content-Type: application/json (multipart/form-data on the two upload endpoints) |
| 422 | The request is well-formed but cannot be accepted: invalid fields, an opt-out, a closed channel | Read the code and details; do not retry unchanged |
| 429 | Too many requests | Wait Retry-After seconds, then retry |
| 5xx | The server failed or is in maintenance | Retry with backoff and the same Idempotency-Key |
An empty POST body counts as {}: actions such as pause, unarchive, cancel and replay need no body.
Line errors#
| Code | When |
|---|---|
LINE_REQUIRED |
A send names no line, channel or address, and the key is not limited to exactly one line |
CHANNEL_LINE_MISMATCH |
The sender fields point at different lines, or the channel is not a sender of the line |
LINE_HAS_NO_SENDERS |
The line has no enabled sender |
LINE_ARCHIVED |
The line is archived; unarchive it or send from another |
LINE_IS_DIRECT |
The Direct line cannot be archived |
All are 422. Sending to one of your own numbers is allowed.
Invalid fields#
422 VALIDATION_FAILED lists every broken field in details.fields. A field lists every rule it broke, each with a code:
{"error": {"code": "VALIDATION_FAILED", "message": "The request has invalid fields. See details.fields for each field and the rule it broke.",
"details": {"fields": {
"to": [{"code": "REQUIRED_WITH", "message": "Send to (a phone number) or contact_id."}],
"body": [{"code": "REQUIRED_WITH", "message": "Send a body, media_ids, template_id or content: the message has nothing to say."}],
"expires_in": [{"code": "OUT_OF_RANGE", "message": "expires_in must be at least 60 seconds."}]}},
"request_id": "01923f80-5c6a-7b10-9c21-0d3e4f5a6b7d", "doc_url": "https://mycomm.cogneer.tech/docs/errors#validation-failed"}}
The field codes are REQUIRED, REQUIRED_WITH, REQUIRED_WHEN, EXCLUSIVE, INVALID_TYPE, INVALID_FORMAT, INVALID_ENUM, INVALID_NUMBER, OUT_OF_RANGE, TOO_LONG, TOO_SHORT, NOT_FOUND, NOT_UNIQUE and INVALID. Fields the API does not know are ignored, so a typo in an optional field is not reported: check names against the API reference.
Idempotency and retries#
Send an Idempotency-Key (1 to 255 printable ASCII characters; a UUID is best) on every POST you may retry. The first answer, success or 4xx, is kept for 24 hours and returned again with Idempotent-Replayed: true. A 5xx is not kept, so a retry runs again. The same key with a different body answers 409 IDEMPOTENCY_KEY_REUSED; while the first request still runs, 409 IDEMPOTENCY_IN_PROGRESS. A bad key answers 400 INVALID_HEADER, also on a dry_run.
Limits#
- Request rate: 600 requests a minute per account key (partner keys: 600 a minute per client account and 3,000 overall;
/partner/v1: 300). Every response carriesRateLimit-Limit,RateLimit-RemainingandRateLimit-Reset; a 429 addsRetry-After. - Wrong keys: 60 failed authentications a minute per IP address, then
429until the minute ends. A correct key is never counted. - Body size: JSON up to 1 MB, uploads up to 21 MB.
- Lists:
limit1 to 200 (default 50) with an opaquecursor; a cursor older than 24 hours or used with other filters answers400 INVALID_CURSOR. - IP allow-lists: a key with
allowed_ipsanswers403 IP_NOT_ALLOWEDfrom any other address.
Sending speed is not an API limit. MyComm holds messages to protect each SIM; see keeping your SIM safe.