# Auto Appy WhatsApp API

Source-checked on 2026-09-22. This guide describes the checked-out implementation; deployment may be older. Backend revision before documentation: `7acfcc1`.

## Authentication and independent use

These APIs can be called from any server-side HTTP client; no workflow node, integration template, or Gemini call is required. Use the API host below, not the product website. The examples use fictional records and placeholders. No request shown here was sent to production during this implementation.

Create an application-specific API key through the signed-in app's **API keys** page. Supply `Authorization: Bearer <key>`, `Accept: application/json`, and `X-AutoAppy-Workspace: <central organisation UUID>`. Add `Content-Type: application/json` for JSON writes. The key is a personal credential: current application access, organisation membership and resource permissions still apply. Use a read/write key for writes; a read-only key does not become writable by selecting an organisation. Keys cannot create more keys or bypass interactive management restrictions. Set finite client timeouts, keep TLS verification enabled, and do not follow redirects with credentials.

Store the key in a server-side environment/secret store. The Postman collection has an empty `api_key` variable; set it locally as a secret. Do not export it after adding a key. The collection contains writes: send only the requests you intend to execute; do not run the whole collection against production.

## Errors and debugging

| Result | Meaning / next check |
| --- | --- |
| 200 / 201 | API read/create accepted; inspect business status in the response. |
| 202 | Work accepted asynchronously, not finished. Read its status/result. |
| 401 | Missing, invalid, expired or revoked credential; wrong auth mechanism may also cause this. |
| 403 | Wrong application key, read-only write, missing membership, role, record access, or interactive-only endpoint. |
| 404 | Wrong local ID or inaccessible record. Do not substitute an ID from another organisation. |
| 409 | Version conflict or incompatible repeated operation. Fetch current state before deciding how to proceed. |
| 413 / 422 | Payload bounds or validation failure. Inspect the direct API's `message` and `errors`. |
| 429 | Rate limit. Honor `Retry-After` when present. Do not automatically repeat a write. |
| 5xx / timeout | Dependency or server failure; a write may already have happened. Inspect state before retrying. |

Central API key checks allow up to 120 requests/minute/key by default; module validation and platform subrequests consume additional calls. Endpoint-specific limits and AI orchestration limits also apply. A successful request is not proof of WhatsApp delivery or of completed AI processing.

To isolate a bug:

1. Call the documented read endpoint directly with the same app key, central organisation and resource IDs. Record method, path, timestamp, status and a redacted response.
2. Compare with this guide and the endpoint's linked controller/request validator. If the documented contract disagrees with deployed code, record both revisions and correct the contract or implementation deliberately.
3. If direct API succeeds but the workflow fails, compare the mapped input, saved connection tag, selected action name, path/query/body mapping, and source API host. Never paste a key into an issue or application log.
4. If the API accepts work but the result is wrong, inspect the owning app's job/message/turn history and downstream provider status. The integration must preserve these distinctions.

The [route reference](ROUTES.md), [OpenAPI specification](openapi.json) and [Postman collection](postman_collection.json) describe native application operations independently of the workflow catalog. [Coverage](coverage.json) accounts for included operations and exclusions. [Lookup recipes](lookup-guide.md) provide field descriptions, pagination, complete requests, responses and error handling. Third-party adapter endpoints are internal-only and absent from all public downloads. Sensitive control-plane endpoints are excluded; the exact internal audit remains in the repository. Browser-only product routes are explicitly marked and do not imply API-key access; SSO and CSRF requirements remain enforced.

## Endpoint and ID contract

API root: `https://whatsappapi.autoappy.com`. Website: `https://whatsapp.autoappy.com`. Key application: `whatsapp`. Paths do **not** have an automatic `/api` prefix.

Call `GET /api/auth/me` with the central organisation header. Read `data.current_workspace.id` and `data.local_workspace_id`; confirm the current central ID equals the requested one. Local workspace, account, phone, conversation and contact IDs in paths are local UUIDs, not Meta business/phone IDs. If there is no mapped workspace, connect the organisation in the signed-in WhatsApp app first. Account and phone discovery are subject to the existing connection-management roles; team members may have narrower conversation access.

```sh
curl --fail-with-body 'https://whatsappapi.autoappy.com/me' \
  -H "Authorization: Bearer $WHATSAPP_API_KEY" \
  -H "X-AutoAppy-Workspace: $AUTOAPPY_ORGANISATION_ID" -H 'Accept: application/json'
```

## Reading accounts, phones, templates and conversations

- `GET /workspaces/{workspace}/whatsapp-accounts` returns `data[]` with `id`, `business_name`, `status` and account metadata.
- `GET /workspaces/{workspace}/whatsapp-accounts/{whatsappAccount}/phone-numbers` returns `data[]` with local `id`, `phone_number`, `display_name`, `phone_number_id` (Meta ID), and connection status.
- `GET /phone-numbers/{phoneNumber}/templates` returns the account's local template catalog. Inspect each template's approval state and language; do not treat a template name alone as approval.
- `GET /phone-numbers/{phoneNumber}/conversations` accepts the existing conversation filters (including search and pagination). The Laravel resource paginator returns `data`, `links`, and `meta`.
- `GET /conversations/{conversation}/messages` reads message history. Message IDs and delivery state belong to this conversation.

## Send a message

`POST /sendAMessage` requires a write key and existing workspace/record permissions. It may create/find a conversation, create a message and call Meta. It is **not** an idempotent endpoint: do not retry automatically after a timeout.

| Field | Contract |
| --- | --- |
| `workspace_id` | Required local workspace UUID. |
| `workspace_whatsapp_phone_number_id` | Local connected phone UUID; always supply it for deterministic selection. |
| `phone_no` | Required recipient number including country code, max 30 characters. |
| `name` | Optional contact name, max 255 characters. |
| `type` | Required: text, image, video, audio, document, sticker, location, contacts, reaction, template, or interactive. |
| `payload` | Required object for most types; an array of contact cards for `contacts`. |
| `reply_to_message_id` | Optional local message UUID from the same conversation. |

Text request:

```json
{"workspace_id":"22222222-2222-4222-8222-222222222222","workspace_whatsapp_phone_number_id":"33333333-3333-4333-8333-333333333333","phone_no":"+15555550123","type":"text","payload":{"body":"Your request has been received."}}
```

Template request (the template and parameters must match an approved template in your account):

```json
{"workspace_id":"22222222-2222-4222-8222-222222222222","workspace_whatsapp_phone_number_id":"33333333-3333-4333-8333-333333333333","phone_no":"+15555550123","type":"template","payload":{"name":"approved_template_name","language":{"code":"en_US"},"components":[{"type":"body","parameters":[{"type":"text","text":"Example"}]}]}}
```

The controller returns HTTP 201 with `message` and `data` containing the message resource. Inspect message state and later delivery/read receipts separately; do not infer device delivery from the controller's success text. Provider message validation and messaging-window rules still apply. See the local `Meta/PayloadBuilders` classes for the exact payload forwarding for each media type.

## Other API families

Contacts support workspace/account/phone-scoped CRUD, tags and imports. Conversations support assignment, archive/close/reopen and read markers. Templates support local listing, creation, media upload and Meta sync. Runtime and memory endpoints operate on existing conversations. AI connection management, workspace membership and Meta setup retain stronger role requirements. Public Meta webhook verification/receive and bot-turn callbacks are not ordinary API-key CRUD routes. The route reference gives exact methods and validators for these families.


## Message payload examples and contacts

Each example below is the `payload` inside the documented `/sendAMessage` envelope. Include its `type`, explicit local phone UUID, local workspace UUID and recipient. Optional `reply_to_message_id` is a **local** message UUID belonging to the recipient conversation, not a Meta message ID. Meta validates type-specific size, format and template constraints. Freeform/media/location/contact/interactive messages require an open messaging window; use approved templates otherwise.

| Type | Example payload |
| --- | --- |
| text | `{"body":"See https://example.com","preview_url":true}` |
| image or video | `{"link":"https://example.com/media","caption":"Example"}` |
| audio or sticker | `{"link":"https://example.com/media"}` |
| document | `{"link":"https://example.com/invoice.pdf","caption":"Invoice","filename":"invoice.pdf"}` |
| location | `{"latitude":19.076,"longitude":72.8777,"name":"Office","address":"Example address"}` |
| contacts | `[{"name":{"formatted_name":"Example","first_name":"Example"},"phones":[{"phone":"+15555550123","type":"CELL"}],"emails":[{"email":"example@example.com","type":"WORK"}]}]` |
| interactive | `{"type":"button","body":{"text":"Continue?"},"action":{"buttons":[{"type":"reply","reply":{"id":"yes","title":"Yes"}}]}}` |

List example: `{"type":"list","body":{"text":"Choose a service"},"action":{"button":"Services","sections":[{"title":"Services","rows":[{"id":"service-a","title":"Service A"}]}]}}`. The API forwards type-specific payloads via `Services/Meta/PayloadBuilders`; no email is sent by a contact card. Approved templates may combine media headers and parameterised bodies through their registered components. Media must be provider-accessible; upload/download lifecycle remains a separate API capability.

Contacts use `/workspaces/{workspace}/whatsapp-accounts/{whatsappAccount}/phone-numbers/{phoneNumber}/contacts`, where every path ID is local. GET accepts search, page and per_page (1–100). POST requires name and phone_number; email, country_code, notes and metadata are optional. PATCH `/{contact}` updates supplied fields. Direct API nullable fields can be explicitly cleared with null; blank workflow text inputs are omitted. Nested workspace/account/phone ownership and current team access are enforced. API errors preserve their direct validation messages; the adapter returns a sanitized error for workflow history. See OpenAPI/Postman for independent calls.
