<!-- GENERATED — не редактировать. source-hash: dda8c78fcc840785 -->
# QNNECT Public API

HTTP API for sending WhatsApp messages and templates through your QNNECT
channel, and for reading the channel's template catalog.

## Authentication

Every request is authenticated with two values you can copy from the
QNNECT cabinet (**Integrations → API**):

* `channel_uuid` — the channel identifier, part of the URL path;
* `api_token` — the channel's secret token, passed as the `token` query
  parameter.

Both values are per-channel. Keep the token secret: anyone who has it can
send messages at your expense. The token can be regenerated in the cabinet
at any time (the old one stops working immediately).

## Delivery model

Sending endpoints are asynchronous: a successful call returns `202` with a
`message_uuid` — the message is queued and delivered in the background.
Delivery progress is reported through webhook integrations (see Webhooks).

## Errors

Errors are JSON objects `{"error": "<code>"}` with an optional
human-readable `message`. HTTP status codes: `400` invalid input,
`401` missing or invalid token, `403` sending suspended (company blocked
or out of funds), `404` unknown channel or template.


## Endpoints

### POST /api/v1/{channel_uuid}/message/

**Send a text or media message**

Sends a free-form message to a WhatsApp user. Free-form messages are
deliverable only inside the 24-hour customer-service window opened by
the user's last inbound message; outside the window use a template.

`type` selects the payload kind. For `text` the `text` field is
required. For media types (`image`, `video`, `audio`, `document`,
`sticker`) the `media.url` field is required; `text` becomes the
caption where WhatsApp supports it (`image`, `video`, `document`).

Supported media formats and size limits:

| type | MIME | max size |
|---|---|---|
| image | image/jpeg, image/png | 5 MB |
| video | video/mp4, video/3gpp | 16 MB |
| audio | audio/aac, audio/mp4, audio/mpeg, audio/amr, audio/ogg | 16 MB |
| document | pdf, doc(x), xls(x), ppt(x), txt | 100 MB |
| sticker | image/webp | 500 KB |

The `media.url` must be a public HTTPS URL.


Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | string (text, image, video, audio, document, sticker) | yes | Message kind. `text` requires `text`; media kinds require `media.url`. |
| `phone` | string | yes | Recipient phone in international format, digits only (e.g. 77778866697). |
| `text` | string | no | Message text for `type=text`; caption for image/video/document. |
| `media` | object | no | Media descriptor; required for media types. |

Example request:

```bash
curl -X POST 'https://qnnect.kz/api/v1/{channel_uuid}/message/?token=YOUR_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
  "type": "text",
  "phone": "77778866697",
  "text": "Hello! Your order is ready."
}'
```

Responses:

* `202` — Message accepted and queued for delivery.
* `400` — Invalid input. `error` holds a machine-readable code.
* `401` — Token is missing (`missing_token`) or wrong (`invalid_token`).
* `403` — Sending is suspended — the company is blocked or out of funds (`company_inactive`).
* `404` — No channel with this `channel_uuid` (`channel_not_found`).

### POST /api/v1/{channel_uuid}/template/

**Send a template message**

Sends an approved WhatsApp template. Templates work outside the
24-hour window and are the way to start a conversation.

`values` fills the numbered body placeholders `{{1}}, {{2}}, …` in
order; the count must match the template exactly (see
`GET /api/v1/{channel_uuid}/templates/` → `variables.body`).
`header_values` and `button_values` fill header/button placeholders
when the template has them.

Media templates (IMAGE/VIDEO/DOCUMENT header): pass a public
`media_url` (with optional `media_mime_type`) or a pre-registered
`media_id`. Carousel templates: `cards` replaces card images by index;
`null` keeps the sample image from the template.


Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `phone` | string | yes | Recipient phone in international format, digits only. |
| `template_name` | string | yes | Template name exactly as in the catalog (`listTemplates`). |
| `values` | array | no | Body placeholder values `{{1}}, {{2}}, …` in order; count must match `variables.body`. |
| `header_values` | array | no | Header placeholder values, when the template header has variables. |
| `button_values` | array | no | Button placeholder values (dynamic URL suffixes etc.). |
| `media_url` | string | no | Public media URL for templates with a media header. |
| `media_id` | string | no | Pre-registered media id — alternative to `media_url`. |
| `media_mime_type` | string | no | MIME type of `media_url` (e.g. image/jpeg). |
| `cards` | array | no | Carousel only — per-card image overrides; `null` keeps the sample image. |

Example request:

```bash
curl -X POST 'https://qnnect.kz/api/v1/{channel_uuid}/template/?token=YOUR_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
  "phone": "77778866697",
  "template_name": "order_confirm",
  "values": [
    "Ivan",
    "12345"
  ]
}'
```

Responses:

* `202` — Template accepted and queued for delivery.
* `400` — Invalid input. `error` holds a machine-readable code.
* `401` — Token is missing (`missing_token`) or wrong (`invalid_token`).
* `403` — Sending is suspended — the company is blocked or out of funds (`company_inactive`).
* `404` — Channel or template not found.

### GET /api/v1/{channel_uuid}/templates/

**List channel templates**

Returns the channel's template catalog with statuses and variable
counts. Use it to discover what can be sent and how many `values`
each template expects before calling the send endpoint.

All statuses are returned by default, including `PENDING` and
`REJECTED` — only `APPROVED` templates can actually be sent.


Query parameters:

* `status` — Filter by exact status (e.g. APPROVED, PENDING, REJECTED).
* `category` — Filter by category.
* `type` — Filter by template type (TEXT, IMAGE, VIDEO, DOCUMENT, CAROUSEL, …).

Example request:

```bash
curl 'https://qnnect.kz/api/v1/{channel_uuid}/templates/?token=YOUR_API_TOKEN'
```

Responses:

* `200` — Template catalog of the channel.
* `401` — Token is missing (`missing_token`) or wrong (`invalid_token`).
* `404` — No channel with this `channel_uuid` (`channel_not_found`).

## Webhooks

### incoming_message

**Incoming message from a WhatsApp user**

Sent to your configured webhook URL when a WhatsApp user writes to the
channel. Webhook URLs are configured in the cabinet
(**Integrations → Webhooks**) and can be enabled per event type.

Delivery: `POST` with JSON body, `Content-Type: application/json;
charset=utf-8`. Respond with any `2xx` within 12 seconds; failed
deliveries are retried in the background.


Payload:

| Field | Type | Description |
|---|---|---|
| `event` | string (incoming_message, outgoing_message, message_status_update) | Event type; matches the webhook name. |
| `phone` | string | WhatsApp user's phone number. |
| `channel_id` | string | Channel identifier (Gupshup app id) the event belongs to. |
| `text` | string | Message text (empty for non-text content). |
| `message_uuid` | string | Message identifier; matches `QueuedResponse.message_uuid` for API sends. |
| `status` | string | Current message status (queued, sent, delivered, read, failed, …). |
| `sender` | string (CLIENT, COMPANY, AI) | Who authored the message. |
| `timestamp` | string | Event time (ISO 8601). |

### outgoing_message

**Outgoing message sent from the channel**

Sent when a message leaves the channel — from the QNNECT UI, the API
or an integration. Same delivery contract as `incoming_message`.


Payload:

| Field | Type | Description |
|---|---|---|
| `event` | string (incoming_message, outgoing_message, message_status_update) | Event type; matches the webhook name. |
| `phone` | string | WhatsApp user's phone number. |
| `channel_id` | string | Channel identifier (Gupshup app id) the event belongs to. |
| `text` | string | Message text (empty for non-text content). |
| `message_uuid` | string | Message identifier; matches `QueuedResponse.message_uuid` for API sends. |
| `status` | string | Current message status (queued, sent, delivered, read, failed, …). |
| `sender` | string (CLIENT, COMPANY, AI) | Who authored the message. |
| `timestamp` | string | Event time (ISO 8601). |

### message_status_update

**Delivery status of an earlier message changed**

Sent when WhatsApp reports a status change for a previously sent
message (`sent` → `delivered` → `read`, or `failed`). Correlate by
`message_uuid`. Same delivery contract as `incoming_message`.


Payload:

| Field | Type | Description |
|---|---|---|
| `event` | string (incoming_message, outgoing_message, message_status_update) | Event type; matches the webhook name. |
| `phone` | string | WhatsApp user's phone number. |
| `channel_id` | string | Channel identifier (Gupshup app id) the event belongs to. |
| `text` | string | Message text (empty for non-text content). |
| `message_uuid` | string | Message identifier; matches `QueuedResponse.message_uuid` for API sends. |
| `status` | string | Current message status (queued, sent, delivered, read, failed, …). |
| `sender` | string (CLIENT, COMPANY, AI) | Who authored the message. |
| `timestamp` | string | Event time (ISO 8601). |
