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

# Use the WhatsApp connection API

> Set up a Workspace's WhatsApp connection through authenticated requests and receive verified message webhooks.

Use the authenticated API to set up one WhatsApp connection for a Workspace, then receive messages through the verified webhook. Received messages and notifications are stored for the connected Workspace. Text conversations enter Cogno's existing Attention workflow, where Cogno can create or update Tasks when needed. For the browser setup flow, see [Connect WhatsApp to Cogno](https://cogno.studio/docs/whatsapp-integration.md).

## Access requirements

Send a Cogno Bearer token in `Authorization`. Every operation requires active Workspace membership and `integrations.manage`, which the member, admin, and owner roles hold. The Workspace's MFA policy also applies. The Workspace comes from the URL; a setup flow belongs to the user who started it.

The deployment must configure WhatsApp credentials, separate state and token encryption keys, a stable registration PIN key, an explicit Graph API version, and a public HTTPS webhook callback URL. The API and integration worker need the same configuration. `DATABASE_LISTENER_URL` must use a direct or session-preserving PostgreSQL endpoint. Missing configuration returns `503` with `setup_not_configured`.

The browser setup page also needs `NEXT_PUBLIC_WHATSAPP_META_APP_ID`, `NEXT_PUBLIC_WHATSAPP_CONFIGURATION_ID`, and `NEXT_PUBLIC_WHATSAPP_GRAPH_API_VERSION` at frontend build time. Match the App ID and Graph version to Core's settings, and allow the HTTPS frontend domain in the Meta app's Facebook Login settings. These frontend values are public; never expose the App Secret or access tokens there.

## Staging test configuration

Set these four server-side environment variables only with `ENVIRONMENT=stg`:

* `WHATSAPP_TEST_WORKSPACE_ID`: the Cogno Workspace used for the test.
* `WHATSAPP_TEST_MESSAGING_ACCOUNT_ID`: the Meta test WhatsApp Business account ID.
* `WHATSAPP_TEST_PHONE_NUMBER_ID`: the test phone number ID, not its telephone number.
* `WHATSAPP_TEST_ACCESS_TOKEN`: the temporary Meta access token. Keep it secret; never use a `NEXT_PUBLIC_` variable.

For deployments managed by this repository, configure `whatsapp.test_connection` in the staging Terraform inputs. Save the token in Secret Manager and set `access_token_secret_name` to its secret ID. `access_token_secret_version` accepts an enabled numeric version or `latest`; staging uses `latest`. The Core API and integration worker receive the same Workspace and Meta asset IDs and the same secret reference. Terraform stores the reference, not the token value. When the temporary token expires, save a new enabled secret version and redeploy both workloads through the normal release flow. Each instance resolves `latest` at startup; existing instances keep their current token. An existing connection also needs a new connection start to save that token; changing the secret alone does not refresh it.

The existing connection start request uses this token only for the configured Workspace and returns setup progress without requiring Embedded Signup. Other Workspaces use normal Meta sign-in. No additional endpoint or browser token field is used. The usual app, scope, expiry and phone checks, encrypted credential storage and setup worker still apply. A temporary token must be refreshed in the server configuration before reconnecting after expiry.

Leave all four unset to use normal signup. Partial configuration, or test credentials configured outside staging, returns `503 setup_not_configured`. Existing backend and frontend signup configuration remains required.

## Endpoints

The base path is `/api/v1/whatsapp/workspaces/{workspace_id}/connection-flows`.

| Method | Path below the base | Result |
| - | - | - |
| `POST` | Base path | `201`: create a flow and return `progress` and `state`. |
| `POST` | `/authorization` | `200`: exchange the Meta authorization code, save the authorization, and queue setup. |
| `GET` | `/{flow_id}` | `200`: return the initiating user's current progress. |
| `POST` | `/{flow_id}/resume` | `200`: queue setup again using a valid saved authorization. |
| `POST` | `/{flow_id}/cancel` | `200`: cancel unfinished setup. An existing active connection is preserved. |

The authorization request accepts exactly these fields:

| Field | Meaning |
| - | - |
| `state` | The opaque value returned when starting the flow. |
| `code` | The authorization code returned by Meta. |
| `messaging_account_id` | The selected WhatsApp Business Account ID, as a positive decimal string. |
| `phone_number_id` | The selected phone number ID, as a positive decimal string. |

The API checks the token and the selected number's account membership before saving the authorization. The code is exchanged during the authorization request. Tokens are encrypted in storage and are never returned in progress responses or put in job arguments.

## Request sequence

**1. Start a flow**

Send `POST` to the base path. Retain the returned `state` and `progress.flow_id`. The authorization state expires ten minutes after starting; the setup flow expires after one day.

**2. Submit Meta authorization**

Complete Meta authorization and submit its code and selected account and phone IDs to `/authorization`, together with the retained `state`. A successful acceptance returns `configuring`; setup continues in the background.

**3. Read progress**

Request `GET /{flow_id}` until setup completes or requires action. Repeating the same accepted authorization returns its saved progress without exchanging the code again.

## Progress and recovery

Progress contains `flow_id`, `status`, `expires_at`, `connection_id`, `phone_number_id`, and `last_error_code`. Every setup response uses `Cache-Control: no-store`.

| Status | Meaning | Next action |
| - | - | - |
| `awaiting_authorization` | The flow is waiting for Meta authorization. | Submit the authorization result before state expiry. |
| `configuring` | Background setup is queued or running. | Read progress again. |
| `completed` | The authorization and connection are active. | Connection setup is complete. |
| `action_required` | Setup needs a person to resolve the reported reason. | Resolve it and resume, or start a new flow if fresh authorization is required. |
| `cancelled` | The initiating user cancelled setup. | Start a new flow to try again. |
| `expired` | The setup deadline passed. | Start a new flow. |
| `failed` | The flow ended with a recorded failure. | Review the reason and start a new flow. |

The worker retries explicitly transient setup failures, up to six attempts. An exhausted transient failure moves the flow to `action_required`. A saved valid authorization can be resumed after correcting the problem. An interrupted code exchange, invalid authorization, permission failure, or account mismatch requires fresh authorization.

## Errors

Expected failures use `application/problem+json` with `type`, `title`, `status`, and a stable `code`. Invalid requests return a generic error without echoing the submitted code or state.

| HTTP status | Typical code | Meaning |
| - | - | - |
| `400` | `invalid_state`, `authorization_required` | The authorization state is unusable or a new authorization is needed. |
| `401` | `authentication_required` | A Cogno credential is required. |
| `402` | `integration_limit_reached` | The Workspace's integration limit is reached. |
| `403` | `workspace_access_denied`, `mfa_required` | Workspace access, permission, or MFA is required. |
| `404` | `flow_not_found` | The flow is not available to this actor in this Workspace. |
| `409` | `authorization_conflict`, `connection_transition_conflict` | The request conflicts with the flow's saved state. |
| `422` | `invalid_request` | The request does not match the accepted shape. |
| `503` | `setup_not_configured` | Deployment configuration is incomplete or invalid. |

## Receive webhooks

Set the Meta app's callback URL and `WHATSAPP_WEBHOOK_CALLBACK_URL` to `https://[your-api-host]/api/v1/whatsapp/webhook`. Configure `WHATSAPP_WEBHOOK_VERIFY_TOKEN` with a dedicated verification string, then enter the same string in Meta's webhook settings. POST verification uses `WHATSAPP_META_APP_SECRET` and `WHATSAPP_META_APP_ID` from the deployment configuration; webhook requests do not use Cogno Bearer tokens.

Subscribe to `messages` for incoming messages and delivery/read statuses. For group metadata notifications, also subscribe to `group_lifecycle_update`, `group_participants_update`, `group_settings_update`, and `group_status_update`. See [Meta's webhook setup](https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/create-webhook-endpoint/) and [group webhook reference](https://developers.facebook.com/documentation/business-messaging/whatsapp/groups/webhooks/).

| Method | Path | Behavior |
| - | - | - |
| `GET` | `/api/v1/whatsapp/webhook` | Validate `hub.mode=subscribe` and `hub.verify_token`, then return `hub.challenge` as plain text. |
| `POST` | `/api/v1/whatsapp/webhook` | Verify `X-Hub-Signature-256` over the original body, validate the batch, and commit accepted records before returning `200`. |

The receiver accepts at most 3 MiB per request. It matches the app, WhatsApp Business Account, and current primary phone number to an active, unexpired Workspace authorization. Unknown or disconnected targets and unrelated subscription fields are acknowledged without saving. A connection still being configured returns `503` so Meta can retry after setup completes.

Accepted records are committed together. Repeated deliveries are deduplicated. Message text, sender identity, reply references, and available media metadata are stored; attachments are not downloaded. Delivery statuses and group changes are saved as notifications rather than conversation messages. These records are Workspace knowledge and are not sent to an external index.

Senders are identified by Meta's business-scoped user ID (BSUID). One-to-one conversations use the other person's BSUID; group conversations use `group_id`, so different participants stay in the same conversation. Contact names are matched by `contacts[].user_id`. Sender phone numbers (`from` and `wa_id`) are optional metadata and never determine identity. The receiving business's `metadata.phone_number_id` remains required to resolve the connection. Identity-change system messages use the BSUID in `system.user_id` when `from_user_id` is absent; their previous IDs are retained in the message details, without merging conversation history. See [Meta's BSUID reference](https://developers.facebook.com/documentation/business-messaging/whatsapp/business-scoped-user-ids/).

Background message processing is enabled by default (`WHATSAPP_SEMANTIC_REDUCTION_ENABLED=true`). Set it to `false` in the API to retain messages without scheduling further processing. Each newly accepted normal message and its processing job are committed together. The worker converts the saved message into a structured conversation event and a readable representation without calling Meta or an LLM. Reprocessing the same message does not create a second event. The shared semantic-reduction job retries failures up to five times after the initial attempt. Delivery/read statuses, group notifications, and system messages do not queue this processing. Previously saved messages are not automatically reprocessed, including when Meta redelivers them.

Text-bearing events reach the existing Attention Gate, which decides whether to act now, later, or ignore the conversation, and which active Projects it concerns. WhatsApp uses the Workspace's connected account; it has no Project source filters. Conversation identity includes the connection and receiving business phone, so matching BSUIDs or group IDs on another connection do not mix. The Gate and the resulting run can read bounded retained history, including a quoted message when available. External sender names do not establish Workspace member identity. Tasks filed from this workflow carry the source family `whatsapp_thread` and the source event ID. Task execution uses the existing Environment and agent setup; receiving a message does not guarantee a new Task.

| HTTP status | Meaning |
| - | - |
| `200` | Stored, already stored, or intentionally ignored. |
| `400` | Invalid challenge, JSON, or supported event payload. |
| `403` | Invalid verification token or signature. |
| `413` | Request exceeds 3 MiB. |
| `503` | Webhook configuration is missing, setup is pending, or the Workspace's event allowance is exhausted. Retry after resolving the cause. |
| `500` | Storage failed and the batch was rolled back. Meta can retry. |

## Related docs

* [Integrations](https://cogno.studio/docs/integrations.md)
* [Billing](https://cogno.studio/docs/billing.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.