> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gateway.connexease.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Codes

> All error codes returned by the Connexease Gateway, with descriptions and recommended actions.

Every error response from the Connexease Gateway follows the same JSON structure with a machine-readable `code` field. This lets you handle specific failure cases programmatically — pausing campaigns on `BILL_001`, retrying after the right delay on `RATE_001`, or skipping invalid recipients on `GW_001`. This page lists all error codes grouped by category, with example payloads and recommended actions for each.

## Response Structure

All error responses use the same JSON format:

```json theme={null}
{
  "isSuccess": false,
  "errors": {
    "code": "AUTH_001",
    "group": "UNAUTHORIZED",
    "description": "API Key not found, invalid, or expired.",
    "params": {}
  }
}
```

<ResponseField name="isSuccess" type="boolean">
  Always `false` for errors.
</ResponseField>

<ResponseField name="errors.code" type="string">
  Unique error identifier. Use this for programmatic error handling.
</ResponseField>

<ResponseField name="errors.group" type="string">
  Error category (e.g. `UNAUTHORIZED`, `PAYMENT_REQUIRED`).
</ResponseField>

<ResponseField name="errors.description" type="string">
  Human-readable explanation of the error.
</ResponseField>

<ResponseField name="errors.params" type="object">
  Optional additional context. Present only on some errors (e.g. `RATE_001`).
</ResponseField>

***

## HTTP Status Codes

| Code  | Meaning                         |
| ----- | ------------------------------- |
| `400` | Malformed or incomplete request |
| `401` | Authentication failure          |
| `402` | Insufficient account balance    |
| `403` | Permission denied               |
| `429` | Rate limit exceeded             |
| `500` | Server-side error               |
| `502` | Upstream Meta API failure       |

***

## Authentication Errors (AUTH\_\*)

<AccordionGroup>
  <Accordion title="AUTH_001 — Invalid API Key">
    **HTTP:** `401 Unauthorized`

    ```json theme={null}
    {
      "isSuccess": false,
      "errors": {
        "code": "AUTH_001",
        "group": "UNAUTHORIZED",
        "description": "API Key not found, invalid, or expired."
      }
    }
    ```

    **When:** The token was not found in Redis cache or the Core Service. The key may be invalid, revoked, or expired.

    **Resolution:** Verify the key is active in [Dashboard](https://gateway.connexease.com) → API Keys. Create a new key if needed.
  </Accordion>

  <Accordion title="AUTH_002 — Missing Authorization Header">
    **HTTP:** `401 Unauthorized`

    ```json theme={null}
    {
      "isSuccess": false,
      "errors": {
        "code": "AUTH_002",
        "group": "UNAUTHORIZED",
        "description": "Missing Authorization header."
      }
    }
    ```

    **When:** The `Authorization` header is absent entirely.

    **Resolution:** Add `Authorization: Bearer YOUR_API_KEY` to every request.
  </Accordion>

  <Accordion title="AUTH_003 — Wrong Token Format">
    **HTTP:** `401 Unauthorized`

    ```json theme={null}
    {
      "isSuccess": false,
      "errors": {
        "code": "AUTH_003",
        "group": "UNAUTHORIZED",
        "description": "Invalid token format. It must be 'Bearer <token>'."
      }
    }
    ```

    **When:** The header is present but the `Bearer ` prefix (with a space) is missing. E.g. sending `Authorization: YOUR_API_KEY` instead of `Authorization: Bearer YOUR_API_KEY`.

    **Resolution:** Ensure the header is exactly in `Bearer YOUR_API_KEY` format.
  </Accordion>

  <Accordion title="AUTH_004 — Auth Service Error">
    **HTTP:** `500 Internal Server Error`

    ```json theme={null}
    {
      "isSuccess": false,
      "errors": {
        "code": "AUTH_004",
        "group": "INTERNAL_ERROR",
        "description": "An error occurred in the authentication service."
      }
    }
    ```

    **When:** Both Redis and the Core Service are unreachable.

    **Resolution:** Retry after a few seconds. If the error persists, contact support.
  </Accordion>

  <Accordion title="AUTH_005 — Corrupted Auth Data">
    **HTTP:** `500 Internal Server Error`

    ```json theme={null}
    {
      "isSuccess": false,
      "errors": {
        "code": "AUTH_005",
        "group": "INTERNAL_ERROR",
        "description": "Authentication data is corrupted."
      }
    }
    ```

    **When:** The token was found but cannot be parsed (data integrity issue).

    **Resolution:** Contact support.
  </Accordion>
</AccordionGroup>

***

## Webhook Errors (WH\_\*)

<AccordionGroup>
  <Accordion title="WH_001 — Invalid Verification Token">
    **HTTP:** `403 Forbidden`

    ```json theme={null}
    {
      "isSuccess": false,
      "errors": {
        "code": "WH_001",
        "group": "FORBIDDEN",
        "description": "Invalid verification token."
      }
    }
    ```

    **When:** The `hub.verify_token` value doesn't match during the Meta webhook verification handshake.

    **Resolution:** Ensure the `WEBHOOK_VERIFY_TOKEN` config value matches exactly what you entered in Meta Business Manager.
  </Accordion>

  <Accordion title="WH_002 — Missing Signature Header">
    **HTTP:** `401 Unauthorized`

    ```json theme={null}
    {
      "isSuccess": false,
      "errors": {
        "code": "WH_002",
        "group": "UNAUTHORIZED",
        "description": "Missing signature header."
      }
    }
    ```

    **When:** A `POST /v1/wa/webhook` request is missing the `X-Hub-Signature-256` header.

    **Resolution:** This error only occurs on requests from Meta. Do not call this endpoint from your own code.
  </Accordion>

  <Accordion title="WH_003 — Invalid Webhook Signature">
    **HTTP:** `403 Forbidden`

    ```json theme={null}
    {
      "isSuccess": false,
      "errors": {
        "code": "WH_003",
        "group": "FORBIDDEN",
        "description": "Invalid signature."
      }
    }
    ```

    **When:** The signature header is present but the computed HMAC-SHA256 doesn't match.

    **Resolution:** Verify that `META_APP_SECRET` in your config exactly matches the App Secret in Meta Business Manager → App Settings.
  </Accordion>
</AccordionGroup>

***

## Rate Limit Error (RATE\_001)

<AccordionGroup>
  <Accordion title="RATE_001 — Rate Limit Exceeded">
    **HTTP:** `429 Too Many Requests`

    ```json theme={null}
    {
      "isSuccess": false,
      "errors": {
        "code": "RATE_001",
        "group": "TOO_MANY_REQUESTS",
        "description": "Rate limit exceeded. Please try again later.",
        "params": {
          "limit_type": "sec",
          "retry_after": "1s"
        }
      }
    }
    ```

    **`params` Fields:**

    | Field         | Values           | Description                      |
    | ------------- | ---------------- | -------------------------------- |
    | `limit_type`  | `"sec"`, `"min"` | Which window was exceeded        |
    | `retry_after` | `"1s"`, `"60s"`  | How long to wait before retrying |

    **Resolution:** Wait for the `retry_after` duration and retry, or use a queue system for high-volume operations.

    **Error handling example:**

    ```javascript theme={null}
    async function sendWithRetry(payload, maxRetries = 3) {
      for (let attempt = 1; attempt <= maxRetries; attempt++) {
        const res = await fetch('https://api.gateway.connexease.com/v1/wa/message', {
          method: 'POST',
          headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
          body: JSON.stringify(payload),
        });

        if (res.status !== 429) return res;

        const data = await res.json();
        const waitMs = parseInt(data.errors.params.retry_after) * 1000 * attempt;
        await new Promise(r => setTimeout(r, waitMs));
      }
      throw new Error('Max retries exceeded');
    }
    ```
  </Accordion>
</AccordionGroup>

***

## Billing Errors (BILL\_\*)

<AccordionGroup>
  <Accordion title="BILL_001 — Insufficient Balance">
    **HTTP:** `402 Payment Required`

    ```json theme={null}
    {
      "isSuccess": false,
      "errors": {
        "code": "BILL_001",
        "group": "PAYMENT_REQUIRED",
        "description": "Insufficient balance. Please top up your account to continue sending messages."
      }
    }
    ```

    **When:** Account balance is ≤ 0 at the time of the request, before forwarding to Meta.

    **Resolution:**

    1. Immediately pause any active campaigns.
    2. Go to [Dashboard](https://gateway.connexease.com) → Billing → Add Balance.
    3. Resume the campaign.
  </Accordion>

  <Accordion title="BILL_002 — No Billing Account">
    **HTTP:** `400 Bad Request`

    ```json theme={null}
    {
      "isSuccess": false,
      "errors": {
        "code": "BILL_002",
        "group": "BAD_REQUEST",
        "description": "API Key does not have an associated billing account. Please contact support."
      }
    }
    ```

    **When:** No billing account is associated with this API key.

    **Resolution:** Contact the Connexease support team.
  </Accordion>
</AccordionGroup>

***

## WhatsApp Credentials Error (WABA\_001)

<AccordionGroup>
  <Accordion title="WABA_001 — Missing WABA Credentials">
    **HTTP:** `401 Unauthorized`

    ```json theme={null}
    {
      "isSuccess": false,
      "errors": {
        "code": "WABA_001",
        "group": "UNAUTHORIZED",
        "description": "WABA credentials are missing or invalid. Please check your configuration."
      }
    }
    ```

    **When:** No WhatsApp Business API credentials (phone number ID + access token) were found for the app associated with your API key.

    **Resolution:** Verify in [Dashboard](https://gateway.connexease.com) → App Settings that your WhatsApp Business account is properly connected.
  </Accordion>
</AccordionGroup>

***

## Security Error (SEC\_001)

<AccordionGroup>
  <Accordion title="SEC_001 — Token Decryption Failed">
    **HTTP:** `500 Internal Server Error`

    ```json theme={null}
    {
      "isSuccess": false,
      "errors": {
        "code": "SEC_001",
        "group": "INTERNAL_ERROR",
        "description": "Access token decryption failed. Please contact support."
      }
    }
    ```

    **When:** The WhatsApp access token could not be decrypted (encryption key mismatch).

    **Resolution:** Contact support.
  </Accordion>
</AccordionGroup>

***

## Gateway / Upstream Error (GW\_001)

<AccordionGroup>
  <Accordion title="GW_001 — Meta API Error">
    **HTTP:** `502 Bad Gateway`

    ```json theme={null}
    {
      "isSuccess": false,
      "errors": {
        "code": "GW_001",
        "group": "UPSTREAM_ERROR",
        "description": "The phone number you are trying to reach is not registered on WhatsApp."
      }
    }
    ```

    **When:** All auth and billing checks passed, but the Meta API returned an error. The `description` field contains Meta's original error message.

    **Common Meta errors surfaced via GW\_001:**

    | Situation                    | Meta Error Message                      | Resolution                            |
    | ---------------------------- | --------------------------------------- | ------------------------------------- |
    | Unregistered recipient       | "not registered on WhatsApp"            | Verify the number is a WhatsApp user  |
    | 24-hour window expired       | "outside the 24-hour window"            | Use a template message                |
    | Wrong template name/language | "template with the name does not exist" | Check template name and language code |
    | Inaccessible media URL       | "media URL is not accessible"           | Verify the URL is publicly accessible |

    **Resolution:** Log the `description` field and skip the failing recipient.
  </Accordion>
</AccordionGroup>

***

## Centralized Error Handling Pattern

```javascript theme={null}
async function sendMessage(payload) {
  const res = await fetch('https://api.gateway.connexease.com/v1/wa/message', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(payload),
  });

  const data = await res.json();

  if (!data.isSuccess) {
    const { code, description, params } = data.errors;

    switch (code) {
      case 'BILL_001':
        await notifyTeam('Balance depleted — campaign paused');
        throw new Error('INSUFFICIENT_BALANCE');

      case 'RATE_001':
        const waitMs = parseInt(params.retry_after) * 1000;
        await sleep(waitMs);
        return sendMessage(payload); // retry

      case 'AUTH_001':
      case 'AUTH_002':
      case 'AUTH_003':
        throw new Error(`Auth error: ${description}`);

      case 'GW_001':
        // Meta error — log and skip this recipient
        logger.warn({ code, description, to: payload.to }, 'Meta API error');
        return null;

      default:
        logger.error({ code, description }, 'Unexpected error');
        throw new Error(`${code}: ${description}`);
    }
  }

  return data;
}
```

```python theme={null}
import requests, time, logging

def send_message(payload, api_key):
    res = requests.post(
        "https://api.gateway.connexease.com/v1/wa/message",
        headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
        json=payload,
    )
    data = res.json()

    if not data.get("isSuccess"):
        err = data["errors"]
        code = err["code"]

        if code == "BILL_001":
            raise Exception("INSUFFICIENT_BALANCE")
        elif code == "RATE_001":
            wait = int(err["params"]["retry_after"].replace("s", ""))
            time.sleep(wait)
            return send_message(payload, api_key)  # retry
        elif code == "GW_001":
            logging.warning(f"Meta error for {payload.get('to')}: {err['description']}")
            return None
        else:
            raise Exception(f"{code}: {err['description']}")

    return data
```
