WhatsApp API for developers
WAIA Connect is a REST API on top of Meta's official WhatsApp API (Cloud API and Coexistence). You send a POST /v1/messages with your key, we queue it and deliver it to Meta at a safe pace, and incoming messages reach a signed webhook. A Meta-verified technology provider, with public documentation, OpenAPI and examples in four languages.
What the API solves for you
- Outbound queue with retries: a send is acknowledged with
202and delivered to Meta at each number's pace. A rejection from Meta lands in the message status with a stable code. - Idempotency: the
Idempotency-Keyheader keeps a retry from sending the same message twice. - Signed webhooks with HMAC over timestamp and body (replay protection), plus an optional static header for no-code tools such as n8n.
- Templates: create them, sync them and validate them before sending them to Meta, with the reason and the fix when something would be rejected. Carousels too, 2 to 10 cards with a photo or video, and which card and which button each contact tapped (Carousel templates).
- Media: send image, document, audio and video by link, and download incoming media through a proxy.
- Read receipts and "typing…": mark a message you received as read (blue ticks) and show "typing…" while your system prepares the answer. It does not use quota.
- Webhooks by API (
/v1/webhooks): create, test and rotate them with your key; and an onboarding link can carry the number's webhook, born signed with the secret you already have. - How each onboarding ended:
onboarding.completedandonboarding.failed(with the reason and Meta's code) reach your webhook, or you ask withGET /v1/onboarding-links?externalId=; and offboarding goes by API too (DELETE /v1/connections/{id}, with yourexternalId): its webhook switches off by itself and you getconnection.disconnected. - Coexistence history: if you choose it when connecting, you receive up to 6 months of chats by webhook (
history.batch) or request it through the API (GET /v1/connections/{id}/history). It does not use quota. - Edited and deleted messages:
message.editedcarries the final version with the original's id (on Cloud API-only numbers Meta reports the edit without the text:textAvailable: false) andmessage.revoked, the deletion. - Meta errors translated into a catalog of stable codes, with what to do in each case.
- Sending status before a campaign:
GET /v1/connections/{id}/sending-statussays whether a number can start conversations and why not — Meta's verdict and original codes (141006,141010,131042), business verification, payment (no card data) and the daily tier, shared by the whole portfolio; what Meta doesn't let us read comes asunknown. Every change reaches your webhook asconnection.sending_status_changed(Sending status). - Email notices and the Notices inbox: every notice about your numbers (disconnected, template approved, failing webhook…) stays in
GET /v1/noticeswith the same text as the email; you choose whether it is also emailed, for the whole account (PATCH /v1/account/settings) or number by number.
What it looks like
A send is an HTTP request with your API key. The number comes from the connection id (conn_…) that GET /v1/connections returns.
Copy-ready examples in cURL, Node.js, PHP and Python: Send a message. The whole contract: OpenAPI.
Everything the API does, endpoint by endpoint
| Endpoint | What it does |
|---|---|
POST /v1/messages | Send a message: text, template, image, document, audio or video. Acknowledged with 202 and sent through the queue. Limited per number (Coexistence 20/s, Cloud API 80/s) and per account. |
GET /v1/messages/{id} | A send's status (queued, sent, delivered, read, failed) and the reason if it failed. Never the content. |
POST /v1/messages/read | Mark a message you received as read (blue ticks) and, optionally, show "typing…". Uses no quota. |
GET /v1/connections | Your connected numbers, with their conn_… id, status and mode (Cloud API or Coexistence). |
PATCH /v1/connections/{id} | Give a number your own label, external id, notes, a monthly send limit and whether its notices are emailed to you. |
DELETE /v1/connections/{id} | Disconnect a number (by conn_… or your external id), like «Disconnect» in the dashboard: frees the slot in your plan and deletes nothing in Meta. |
GET /v1/connections/{id}/history | Read, page by page, the Coexistence history you chose to receive when connecting. Reading deletes nothing. |
POST /v1/connections/{id}/history/{batchId}/ack | Confirm you received a history batch: WAIA Connect deletes it right away. |
GET /v1/account/settings | See whether the notices about your numbers and signups are emailed to you (emailNotices). |
PATCH /v1/account/settings | Turn those email notices on or off for the whole account. Billing, security and support always arrive. |
GET /v1/notices | Notices: every notice about your numbers, emailed or not, with the same text, in your language. Filter by number and by unread. |
POST /v1/notices/{id}/read | Mark a notice as read (in the dashboard too). |
POST /v1/notices/read-all | Mark every notice as read at once (in the dashboard too). |
GET /v1/usage | What you used in the current period, your numbers and the cycle's estimated extra. |
GET /v1/connections/{id}/sending-status | Can this number start conversations? canSend, Meta's codes per entity, verification, payment (no card data) and daily tier; what Meta doesn't let us read, unknown. |
POST /v1/connections/{id}/sending-status/refresh | Re-read the sending status from Meta (read-only), once per number every 10 min. |
GET /v1/connections/{id}/usage | One number's figures: totals, daily series and health. |
GET /v1/usage/by-connection | Every number's totals for a period, at a glance. |
GET /v1/accounts/oversubscription | Whether you are selling more numbers than your plan includes. |
GET /v1/branding | Your brand (name, logo, color) for the onboarding page your clients see. |
PUT /v1/branding | Update your brand. |
POST /v1/onboarding-links | Create an onboarding link so your client connects their number without giving you their Meta credentials. With webhook, the number is born with its webhook. |
GET /v1/onboarding-links | Your onboarding links and how each one ended (outcome). With ?externalId=, one customer's. |
GET /v1/onboarding-links/{id} | One onboarding link with its outcome: pending, completed (with the conn_… and the webhook) or failed, with the reason and Meta's code. |
PATCH /v1/onboarding-links/{id} | Change the intro page of a live link: intro: "minimal" (one button and one line) or full; null = the account's. |
GET /v1/onboarding-cases | Your customers' sign-up cases (A-<n>): what they wrote, Meta's code and the diagnosis; ?status=open|acked|all. |
POST /v1/onboarding-cases/{id}/ack | Mark a case as handled (idempotent). A webhook delivery does not count. |
DELETE /v1/onboarding-links/{id} | Revoke an onboarding link. |
POST /v1/webhooks | Create a webhook for the account or for one number (by conn_… or your externalId). The signing secret is shown once. |
GET /v1/webhooks | Your webhooks, all of them or one number's (filter by conn_… or externalId). Never the secret. |
GET /v1/webhooks/{id} | One webhook and its state: active, consecutive failures, last response. |
PATCH /v1/webhooks/{id} | Change the URL or the events, turn it on or off, or move it to another number. |
DELETE /v1/webhooks/{id} | Delete a webhook. |
POST /v1/webhooks/{id}/test | Send it a signed test event now and see what your server answered. |
POST /v1/webhooks/{id}/rotate-secret | Rotate the signing secret: the new one is shown once and the old one stops working at once. |
GET /v1/media/{mediaId} | Download the image, audio or document you were sent, without exposing the Meta token. |
GET /v1/templates | Your templates and their status at Meta. |
POST /v1/templates | Create a template at Meta, checked before it is sent: with an image, video, document or location on top, and buttons, or a carousel of 2 to 10 cards. |
POST /v1/templates/media | Upload the sample file Meta reviews for a template with an image, video or document. Returns the handle to create it. |
POST /v1/templates/validate | Validate a template without sending anything to Meta: every error at once, each with its field, in your language. |
POST /v1/templates/preview | A template's preview, as JSON or as a WhatsApp bubble in HTML to show your customer. |
GET /v1/templates/{id} | One template. |
PATCH /v1/templates/{id} | Edit a template where Meta allows it (approved, rejected or paused), within its edit limit. |
DELETE /v1/templates/{id} | Delete a template. |
POST /v1/templates/sync | Pull templates and their current status from Meta. |
POST /v1/templates/precheck | Check a draft before sending it: if Meta will reject it, the reason and how to fix it. |
POST /v1/templates/generate-examples | Suggest examples for a template's variables (Meta asks for them). |
GET /v1/templates/catalog | The template catalog by industry, written to get approved by Meta. |
POST /v1/templates/seed | Add catalog templates to a number. |
GET /v1/templates/bulk-targets | Which numbers a template can be applied to in one go. |
POST /v1/templates/bulk | Create the same template on several numbers at once. |
GET /v1/templates/drafts | Templates your clients proposed, to review. |
GET /v1/templates/drafts/count | How many proposals are waiting for review. |
POST /v1/templates/drafts/{id}/approve | Approve a proposal: it is checked and created at Meta. |
PUT /v1/templates/drafts/{id} | Edit a proposal before approving it (the original is kept). |
POST /v1/templates/drafts/{id}/reject | Reject a proposal. |
POST /v1/templates/drafts/bulk-approve | Approve several proposals together; if one fails, the rest go on. |
POST /v1/template-links | Create a link so your client can propose templates for their number. |
GET /v1/template-links | Your template-proposal links. |
DELETE /v1/template-links/{id} | Revoke a template-proposal link. |
Each one in detail, with its errors: OpenAPI.
What it does not do
- It is not a chat inbox for a team: that is WAIA, our turnkey product.
- It does not store files: to send a file of your own, you host it and send the link.
- It does not take you out of Meta's rules: the 24-hour window, approved templates and sending limits still apply. We explain them in Before you connect.
What it costs
A flat fee per connected number, with no message cap. You start with a 14-day trial; the smallest plan is Starter (US$ 12 per month).
- Starter — US$ 12 per month, 1 number
- Studio — US$ 25 per month, 5 numbers
- Agencia — US$ 45 per month, 12 numbers
- Escala — US$ 99 per month, 30 numbers, plus US$ 3 for each extra number
Meta bills its messages separately, to your card; we add no markup. Compared with a provider that does: WAIA Connect vs Twilio.
Frequently asked questions
Does it use Meta's official API?
Yes. WAIA Connect is a Meta-verified technology provider and runs the Cloud API and Coexistence. It does not use unofficial libraries that emulate WhatsApp Web.
How do I receive incoming messages?
You register an https URL and they arrive by POST, signed with HMAC. The signature verification guide has examples in four languages.
What happens if I send the same request twice?
With the same Idempotency-Key the message is sent once and the second response returns the same id.
Is there a monthly message limit?
Not in WAIA Connect: the plan is paid per connected number. Meta has its own limits for starting conversations with templates, which depend on your Meta account.
Is the documentation meant to be read with an AI?
Yes: every module is also in Markdown, there is an llms.txt at the site root, and the full contract is in OpenAPI.
Keep reading
Connect your first number today
Meta-verified technology provider. Coexistence in one click.
Get started