# Telnyx Messaging: WhatsApp — Full Documentation
> Complete page content for WhatsApp (Messaging section) of the Telnyx developer docs (https://developers.telnyx.com).
> This file: https://developers.telnyx.com/docs/development/llms/messaging-whatsapp-llms-full-txt · Root index: https://developers.telnyx.com/llms.txt
## Getting Started
### Quickstart
> Source: https://developers.telnyx.com/docs/messaging/whatsapp/quickstart.md
# Send Your First WhatsApp Message
Get started with WhatsApp Business messaging via Telnyx in minutes. This guide covers account setup, template creation, and sending your first message with delivery confirmation.
## Prerequisites
Before sending WhatsApp messages, ensure you have:
- **Telnyx Account** — [Sign up](https://telnyx.com/sign-up) and verify your account
- **Meta Business Manager Account** — Required for WhatsApp Business Platform access
- **WhatsApp Business Account (WABA)** — Connected via Telnyx's Embedded Signup
- **Verified phone number** — Added to your WABA and verified with Meta
WhatsApp Business Platform requires business verification through Meta's process. Personal WhatsApp accounts cannot send template messages via the API.
1. Create a [Telnyx account](https://telnyx.com/sign-up) and verify your email
2. Navigate to the [Portal](https://portal.telnyx.com) and complete account verification
3. Add billing information to enable message sending
4. Generate an API key from **Developer Center → API Keys → Create API Key**
Store your API key securely — you'll need it for all API requests.
Complete the WhatsApp Embedded Signup to connect your Meta Business Manager:
1. Navigate to **Messaging → WhatsApp** in the Telnyx Portal
2. Click **Connect WhatsApp Business Account**
3. Sign in with your Facebook Business Manager credentials
4. Select the Business Manager account to connect
5. Follow Meta's prompts to create or select a WhatsApp Business Account
6. Verify your business phone number when prompted
The standard Cloud API signup flow cannot register a number that is active in the personal WhatsApp or WhatsApp Business app. To keep using an existing WhatsApp Business app number, follow the [WhatsApp Coexistence guide](/docs/messaging/whatsapp/coexistence) instead of removing it from the app.
Embedded Signup accepts either a Telnyx number with an active messaging profile or a bring-your-own-number (BYON) that is not on Telnyx. No manual import by Telnyx is required. A BYON number must already be added to a WhatsApp Business Account in the associated Meta Business Manager to appear in the Embedded Signup window; verify it by SMS or voice when prompted, the same as a Telnyx number.
If you're registering a landline number, choose **phone call verification** instead of SMS when prompted. SMS delivery to landline numbers can be unreliable — call verification is more consistent.
Some platforms — including Meta — may not deliver SMS verification codes to **virtual numbers** (VoIP and DID ranges). Meta rates SMS OTP delivery to VoIP numbers as *Not Recommended*, while voice OTP is *Standard* (see [Meta's phone number requirements](https://developers.facebook.com/documentation/business-messaging/whatsapp/business-phone-numbers/phone-numbers#business-phone-number-types)). If the SMS verification code doesn't arrive, retry with **phone call verification** — this is the common fix for virtual numbers.
WhatsApp requires pre-approved templates for outbound marketing and notifications:
Set a display name for the phone number before submitting templates. Templates submitted from numbers without an approved display name are rejected by Meta.
When a template contains parameters (e.g., `\{\{1\}\}`), provide sample values in the `example` field. Meta reviewers use these to evaluate the template. Without sample values, templates are typically rejected.
Complete the business profile (website, description, industry category) before submitting templates. Incomplete profiles increase rejection rates, especially for new WhatsApp Business Accounts.
Create a message template using the API:
```bash cURL
curl -X POST "https://api.telnyx.com/v2/whatsapp/message_templates" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "welcome_message",
"category": "MARKETING",
"language": "en_US",
"waba_id": "YOUR_WABA_ID",
"components": [
{
"type": "BODY",
"text": "Hello {{1}}! Welcome to our WhatsApp updates.",
"example": {
"body_text": [["John"]]
}
}
]
}'
```
```javascript JavaScript
import Telnyx from 'telnyx';
const client = new Telnyx({apiKey: 'YOUR_API_KEY'});
const template = await client.whatsapp.templates.create({
name: 'welcome_message',
category: 'MARKETING',
language: 'en_US',
waba_id: 'YOUR_WABA_ID',
components: [
{
type: 'BODY',
text: 'Hello {{1}}! Welcome to our WhatsApp updates.',
example: {
body_text: [['John']]
}
}
]
});
```
```python Python
from telnyx import Telnyx
client = Telnyx(api_key='YOUR_API_KEY')
template = client.whatsapp.templates.create(
name='welcome_message',
category='MARKETING',
language='en_US',
waba_id='YOUR_WABA_ID',
components=[
{
'type': 'BODY',
'text': 'Hello {{1}}! Welcome to our WhatsApp updates.',
'example': {
'body_text': [['John']]
}
}
]
)
```
```ruby Ruby
require 'telnyx'
telnyx.whatsapp.templates.create(
name: 'welcome_message',
category: 'MARKETING',
language: 'en_US',
waba_id: 'YOUR_WABA_ID',
components: [
{
type: 'BODY',
text: 'Hello {{1}}! Welcome to our WhatsApp updates.',
example: {
body_text: [['John']]
}
}
]
)
```
Meta typically reviews templates within 24-48 hours. You'll receive an email when approved.
Start with simple templates for faster approval. Avoid promotional language or special characters in your first template.
Once your template is approved, send your first message:
```bash cURL
curl -X POST "https://api.telnyx.com/v2/messages/whatsapp" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "template",
"template": {
"name": "welcome_message",
"language": {
"policy": "deterministic",
"code": "en_US"
},
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"text": "John"
}
]
}
]
}
}
}'
```
```python Python
from telnyx import Telnyx
client = Telnyx(api_key='YOUR_API_KEY')
response = client.messages.send_whatsapp(
from_="+15551234567",
to="+15557654321",
whatsapp_message={
"type": "template",
"template": {
"name": "welcome_message",
"language": {
"policy": "deterministic",
"code": "en_US"
},
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"text": "John"
}
]
}
]
}
}
)
print(f"Message ID: {response.data.id}")
print(f"Status: {response.data.to[0].status}")
```
```javascript Node.js
import Telnyx from 'telnyx';
const client = new Telnyx({apiKey: 'YOUR_API_KEY'});
const message = await client.messages.sendWhatsapp({
from: '+15551234567',
to: '+15557654321',
whatsapp_message: {
type: 'template',
template: {
name: 'welcome_message',
language: {
policy: 'deterministic',
code: 'en_US'
},
components: [
{
type: 'body',
parameters: [
{
type: 'text',
text: 'John'
}
]
}
]
}
}
});
console.log(`Message ID: ${message.data.id}`);
console.log(`Status: ${message.data.to[0].status}`);
```
```ruby Ruby
require 'telnyx'
response = telnyx.messages.send_whatsapp(
from: '+15551234567',
to: '+15557654321',
whatsapp_message: {
type: 'template',
template: {
name: 'welcome_message',
language: {
policy: 'deterministic',
code: 'en_US'
},
components: [
{
type: 'body',
parameters: [
{
type: 'text',
text: 'John'
}
]
}
]
}
}
)
puts "Message ID: #{response.data.id}"
puts "Status: #{response.data.to[0].status}"
```
```go Go
package main
import (
"context"
"fmt"
"github.com/team-telnyx/telnyx-go/v2"
)
func main() {
client := telnyx.NewAPIClient(telnyx.NewConfiguration())
client.GetConfig().AddDefaultHeader("Authorization", "Bearer YOUR_API_KEY")
parameters := []telnyx.TemplateParameter{
{
Type: telnyx.PtrString("text"),
Text: telnyx.PtrString("John"),
},
}
components := []telnyx.TemplateComponent{
{
Type: telnyx.PtrString("body"),
Parameters: parameters,
},
}
template := telnyx.WhatsAppTemplate{
Name: telnyx.PtrString("welcome_message"),
Language: &telnyx.TemplateLanguage{
Policy: telnyx.PtrString("deterministic"),
Code: telnyx.PtrString("en_US"),
},
Components: components,
}
whatsappMessage := telnyx.WhatsAppMessageContent{
Type: telnyx.PtrString("template"),
Template: &template,
}
params := telnyx.MessageSendWhatsappParams{
From: "+15551234567",
To: "+15557654321",
WhatsappMessage: whatsappMessage,
}
response, err := client.Messages.SendWhatsapp(context.TODO(), params)
if err != nil {
panic(err)
}
fmt.Printf("Message ID: %s\n", response.Data.Id)
fmt.Printf("Status: %s\n", response.Data.To[0].Status)
}
```
```java Java
import com.telnyx.sdk.ApiClient;
import com.telnyx.sdk.ApiException;
import com.telnyx.sdk.Configuration;
import com.telnyx.sdk.auth.HttpBearerAuth;
import com.telnyx.sdk.api.MessagesApi;
import com.telnyx.sdk.model.*;
import java.util.Arrays;
public class WhatsAppQuickstart {
public static void main(String[] args) {
ApiClient defaultClient = Configuration.getDefaultApiClient();
HttpBearerAuth bearerAuth = (HttpBearerAuth) defaultClient.getAuthentication("bearerAuth");
bearerAuth.setBearerToken("YOUR_API_KEY");
MessagesApi apiInstance = new MessagesApi(defaultClient);
TemplateParameter parameter = new TemplateParameter()
.type("text")
.text("John");
TemplateComponent component = new TemplateComponent()
.type("body")
.parameters(Arrays.asList(parameter));
TemplateLanguage language = new TemplateLanguage()
.policy("deterministic")
.code("en_US");
WhatsAppTemplate template = new WhatsAppTemplate()
.name("welcome_message")
.language(language)
.components(Arrays.asList(component));
WhatsAppMessageContent whatsappMessage = new WhatsAppMessageContent()
.type("template")
.template(template);
MessageSendWhatsappRequest request = new MessageSendWhatsappRequest()
.from("+15551234567")
.to("+15557654321")
.whatsappMessage(whatsappMessage);
try {
var result = apiInstance.sendWhatsapp(request);
System.out.println("Message ID: " + result.getData().getId());
System.out.println("Status: " + result.getData().getTo().get(0).getStatus());
} catch (ApiException e) {
System.err.println("Exception: " + e.getResponseBody());
}
}
}
```
The messaging profile is automatically resolved from your WhatsApp-enabled phone number specified in the `from` field.
Configure webhooks to receive real-time delivery status updates:
1. In the Telnyx Portal, navigate to **Messaging → Messaging Profiles**
2. Select your messaging profile
3. Set **Webhook URL** to your endpoint (e.g., `https://yourapp.com/webhooks/telnyx`)
4. Enable **Failover URL** for redundancy (optional)
5. Save the configuration
Your webhook endpoint will receive delivery reports and inbound message notifications.
```json
{
"data": {
"record_type": "event",
"id": "b301ed3f-1490-491f-995f-6e64e69674d4",
"event_type": "message.sent",
"occurred_at": "2026-01-01T12:00:01.000+00:00",
"payload": {
"record_type": "message",
"direction": "outbound",
"id": "40385f64-5717-4562-b3fc-2c963f66afa6",
"type": "WHATSAPP",
"organization_id": "7a7f45eb-6670-45fd-a3e3-5b9e4a4f2103",
"messaging_profile_id": "4001799e-1457-4c7f-a96a-735ed44c7de7",
"from": {
"phone_number": "+15551234567",
"carrier": "Telnyx",
"line_type": "Wireless"
},
"to": [
{
"phone_number": "+15557654321",
"status": "sent",
"carrier": "T-MOBILE USA, INC.",
"line_type": "Wireless"
}
],
"webhook_url": "https://yourapp.com/webhooks/telnyx",
"webhook_failover_url": "",
"tags": [],
"cost": {
"amount": null,
"currency": null
},
"cost_breakdown": null,
"received_at": "2026-01-01T12:00:00.000+00:00",
"sent_at": "2026-01-01T12:00:01.000+00:00",
"completed_at": null,
"valid_until": "2026-01-01T13:00:00.000+00:00",
"errors": [],
"wait_seconds": null,
"body": {
"type": "template",
"template": {
"name": "welcome_message",
"language": {
"policy": "deterministic",
"code": "en_US"
},
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"text": "John"
}
]
}
]
}
},
"template_id": "019a5c3e-7b1d-7c2e-9f4a-3d8b6e1f2a90",
"template_name": "welcome_message"
}
}
}
```
Implement webhook handling to track message status and respond to inbound messages:
```python Python
from flask import Flask, request, jsonify
import hmac
import hashlib
app = Flask(__name__)
@app.route('/webhooks/telnyx', methods=['POST'])
def handle_webhook():
webhook_data = request.json
event_type = webhook_data['data']['event_type']
payload = webhook_data['data']['payload']
if event_type == 'message.sent':
print(f"WhatsApp message sent: {payload['id']}")
print(f"Status: {payload.get('to', [{}])[0].get('status', 'unknown')}")
elif event_type == 'message.received':
text = payload['body'].get('text', {}).get('body', '')
print(f"WhatsApp message received: {text}")
print(f"From: {payload['from']['phone_number']}")
# Auto-reply during 24-hour window
reply_to_customer(payload['from']['phone_number'], payload['to'])
return '', 200
def reply_to_customer(customer_number, business_number):
from telnyx import Telnyx
client = Telnyx(api_key="YOUR_API_KEY")
# Send free-form message (no template required in 24hr window)
client.messages.send_whatsapp(
from_=business_number,
to=customer_number,
whatsapp_message={
"type": "text",
"text": {
"body": "Thanks for your message! We'll get back to you soon.",
"preview_url": False
}
}
)
if __name__ == '__main__':
app.run(debug=True)
```
```javascript Node.js
const express = require('express');
import Telnyx from 'telnyx';
const client = new Telnyx({apiKey: 'YOUR_API_KEY'});
const app = express();
app.use(express.json());
app.post('/webhooks/telnyx', async (req, res) => {
const { data } = req.body;
const { event_type, payload } = data;
if (event_type === 'message.sent') {
console.log(`WhatsApp message sent: ${payload.id}`);
console.log(`Status: ${payload.to[0]?.status || 'unknown'}`);
} else if (event_type === 'message.received') {
console.log(`WhatsApp message received: ${payload.body.text?.body ?? ''}`);
console.log(`From: ${payload.from.phone_number}`);
// Auto-reply during 24-hour window
await replyToCustomer(payload.from.phone_number, payload.to);
}
res.status(200).send();
});
async function replyToCustomer(customerNumber, businessNumber) {
try {
await client.messages.sendWhatsapp({
from: businessNumber,
to: customerNumber,
whatsapp_message: {
type: 'text',
text: {
body: "Thanks for your message! We'll get back to you soon.",
preview_url: false
}
}
});
} catch (error) {
console.error('Error sending reply:', error);
}
}
app.listen(3000, () => {
console.log('Webhook server listening on port 3000');
});
```
## Verify Message Delivery
Check message delivery through multiple channels:
### Portal Dashboard
1. Navigate to **Messaging → Message Logs** in the Telnyx Portal
2. Filter by **Channel: WhatsApp** and **Date Range**
3. View delivery status, timestamps, and any error codes
4. Click individual messages for detailed delivery information
### Webhook Events
Monitor these key webhook events:
- `message.sent`: WhatsApp accepted the message.
- `message.finalized`: The message reached a final state. Check `to[].status`, for example `delivered` or `delivery_failed`, and `errors` for failure details.
- `message.read`: The recipient read the message, when the recipient has read receipts enabled.
### API Response
The initial API response includes:
```json
{
"data": {
"id": "msg_123abc",
"to": [
{
"phone_number": "+15557654321",
"status": "queued",
"carrier": "WhatsApp"
}
]
}
}
```
## Common Issues
**Symptoms:** API returns a `40008` error indicating the template was not found or not approved
**Solutions:**
- Verify template status in Portal under **Messaging → WhatsApp → Send Messages**
- Ensure template name matches exactly (case-sensitive)
- Wait for Meta's approval (typically 24-48 hours for first templates)
- Review Meta's template guidelines for approval requirements
**Symptoms:** API returns a `40008` error indicating the sender phone number is not registered with WhatsApp
**Solutions:**
- Complete phone number verification in the Telnyx Portal
- Ensure number is added to your WhatsApp Business Account
- Verify the number through Meta's verification process
- Check that the number hasn't been used with personal WhatsApp
**Symptoms:** Free-form (session) message fails with a `40008` error. WhatsApp requires a template to initiate conversations outside the 24-hour window
**Solutions:**
- Use approved message templates for outbound messaging
- Only send free-form messages within 24 hours of customer's last message
- Check conversation window status via webhook events
- Consider switching to template-based messaging for customer re-engagement
**Symptoms:** Message status webhook returns `undeliverable` with Meta API error details in the response
**Solutions:**
- Verify recipient has WhatsApp installed and active
- Ensure phone number format includes country code (+1...)
- Check that recipient hasn't blocked your business number
- Confirm recipient's WhatsApp account is not banned or restricted
## Next Steps
Send templates, text, media, and interactive messages
Set up your WhatsApp Business Account and verify your number
Handle inbound messages and delivery status callbacks
---
### Embedded Signup
> Source: https://developers.telnyx.com/docs/messaging/whatsapp/embedded-signup.md
# WhatsApp Embedded Signup
WhatsApp Embedded Signup connects a Meta business portfolio to Telnyx. The business completes Meta's signup window, and Telnyx then provisions the WhatsApp Business Account (WABA) and its phone numbers for Cloud API messaging.
Tech providers that onboard their own customers use the [Tech Provider guide](/docs/messaging/whatsapp/embedded-signup/tech-provider) instead.
## Prerequisites
**Telnyx account:**
- A Telnyx account above the trial level. Trial accounts cannot use WhatsApp.
**Meta business portfolio:**
- A Facebook account with admin access to a business portfolio, or permission to create one during signup
- Business verification is not required to start. Meta applies a messaging limit of 250 to new business portfolios, and business verification is one of the ways to raise it.
- The business must comply with WhatsApp's Commerce Policy
**Phone number:**
- A Telnyx number or a number from another carrier. See [Phone numbers](#phone-numbers).
- Able to receive an SMS or a voice call for Meta's verification code
- Not registered to another WhatsApp account, unless it is migrated from another provider
- For standard Cloud API signup, not active in the WhatsApp or WhatsApp Business app. To keep using an existing WhatsApp Business app number, use [WhatsApp Coexistence](/docs/messaging/whatsapp/coexistence).
## Phone numbers
The business enters and verifies the number in Meta's signup window. The number does not need to be on Telnyx, and Telnyx does not import or port it.
| Number | Requirements |
| --- | --- |
| Telnyx number | SMS and voice keep working on the same number. To receive the SMS verification code, assign the number to a messaging profile with a webhook URL. To receive the voice code, assign it to a voice connection or application. |
| Number from another carrier | The number stays with its carrier. Telnyx only registers it with WhatsApp. |
| Number on another WhatsApp provider | Meta supports migrating the number through Embedded Signup once two-step verification is disabled on it. See Meta's [migration guide](https://developers.facebook.com/documentation/business-messaging/whatsapp/solution-providers/support/migrating-phone-numbers-among-solution-partners-via-embedded-signup). |
After signup, assign each number to a messaging profile to receive inbound WhatsApp messages. See [Signup webhook](#signup-webhook).
## Meta signup window
Start Embedded Signup from **Messaging Suite** → **WhatsApp Messaging** in the [Telnyx Portal](https://portal.telnyx.com) with **Connect WhatsApp Business**, **Add Account** on the **Business Accounts** tab, or **Add Number** on the **WhatsApp Numbers** tab. Meta's signup window then handles the business-side steps:
Log in with Facebook and grant the `whatsapp_business_management` and `whatsapp_business_messaging` permissions.
Select an existing business portfolio or create a new one.
Select an existing WhatsApp Business Account or create a new one. Meta creates the WABA, not Telnyx.
Enter the phone number and verify it with the code Meta sends by SMS or voice call.
Some platforms, including Meta, may not deliver SMS verification codes to **virtual numbers** (VoIP and DID ranges). Meta rates SMS OTP delivery to VoIP numbers as *Not Recommended*, while voice OTP is *Standard* (see [Meta's phone number requirements](https://developers.facebook.com/documentation/business-messaging/whatsapp/business-phone-numbers/phone-numbers#business-phone-number-types)). If the SMS code doesn't arrive, request verification by voice call instead.
Telnyx then provisions the account asynchronously and tracks progress in a signup session.
## Provisioning states
The signup session moves through these states in order. The `state` field shows the current step, or the step that failed.
```mermaid
flowchart LR
A[SUBSCRIBE_WEBHOOKS] --> B[ASSIGNED_USERS]
B --> C[SHARE_CREDIT_LINE]
C --> D[REGISTER_PHONE_NUMBER]
D --> E[ADD_WABA_TO_DB]
C -. coexistence .-> E
E --> F[ADD_PHONE_NUMBERS_TO_DB]
F --> G[COMPLETE]
```
| State | Description |
| --- | --- |
| `SUBSCRIBE_WEBHOOKS` | Subscribes the Telnyx app to the WABA's webhooks |
| `ASSIGNED_USERS` | Assigns the Telnyx system user to the WABA |
| `SHARE_CREDIT_LINE` | Shares the Telnyx credit line with the WABA |
| `REGISTER_PHONE_NUMBER` | Registers the phone numbers with Cloud API, including the two-step verification PIN. Skipped for coexistence. |
| `ADD_WABA_TO_DB` | Stores the WABA in the Telnyx account |
| `ADD_PHONE_NUMBERS_TO_DB` | Stores the WABA's phone numbers in the Telnyx account |
| `COMPLETE` | Provisioning finished |
The `status` field reports the session outcome:
| Status | Description |
| --- | --- |
| `INCOMPLETE` | The session is waiting for the Meta signup code |
| `PENDING` | Provisioning is in progress. Telnyx retries transient Meta errors automatically. |
| `SUCCESS` | Provisioning finished and the WABA and phone numbers are available |
| `FAILURE` | Provisioning stopped. `errors` contains the reason, and `state` shows the failed step. |
A `SUCCESS` session can still list `errors`. Telnyx records an error for each retried attempt, and a phone number that fails registration does not stop the session, unless the number is active in the WhatsApp Business app. Check each number's `status` after signup.
## Check signup status
Retrieve a signup session with `GET /v2/whatsapp/signup/{session_id}/status`:
```bash
curl -X GET "https://api.telnyx.com/v2/whatsapp/signup/019f4253-e09a-7622-bc6f-625378711d78/status" \
-H "Authorization: Bearer YOUR_API_KEY"
```
This example shows a session that failed at the credit line step:
```json
{
"data": {
"record_type": "whatsapp_session_id",
"session_id": "019f4253-e09a-7622-bc6f-625378711d78",
"user_id": "00000000-0000-4000-8000-000000000000",
"state": "SHARE_CREDIT_LINE",
"status": "FAILURE",
"created_at": "2026-10-01T09:58:25Z",
"updated_at": "2026-10-01T09:58:27Z",
"waba_id": "123456789012345",
"errors": [
"[ShareCreditLine] Bad status received: Invalid parameter Credit Allocation Not Allowed During Month-End Close: This WhatsApp Business Account cannot be moved onto a credit line while the monthly billing close is running. Please try again once the close has completed, usually within the first few days of the month."
]
}
}
```
List finished signup sessions with `GET /v2/whatsapp/signup`. Filter by WABA with `filter[waba_id]`, or by the tech provider's customer reference with `filter[customer_id]`:
```bash
curl -g -X GET "https://api.telnyx.com/v2/whatsapp/signup?filter[waba_id]=123456789012345" \
-H "Authorization: Bearer YOUR_API_KEY"
```
## Signup webhook
Telnyx sends a `whatsapp.waba_added` event when a signup session succeeds or fails. Set the destination with `PATCH /v2/whatsapp/user_data`:
```json
{
"webhook_url": "https://example.com/whatsapp/signup",
"webhook_failover_url": "https://example.com/whatsapp/signup-failover"
}
```
The event payload contains:
| Field | Description |
| --- | --- |
| `record_type` | Always `waba_account_added` |
| `waba_id` | Meta WABA ID |
| `phone_numbers` | Phone numbers found on the WABA. Omitted for standard sessions started with a `phone_number_id`, and for sessions that fail before Telnyx fetches the numbers. |
| `status` | `SUCCESS` or `FAILURE` |
| `event_id` | Signup session ID |
| `errors` | Errors recorded during provisioning |
When a phone number fails registration for a reason other than being active in the WhatsApp Business app, Telnyx sends a `FAILURE` event, continues provisioning, and sends another event for the same `event_id` when the session ends. Telnyx sends events only when `webhook_url` or `webhook_failover_url` is set. A session that fails before a WABA is identified sends no event.
Inbound messages for a provisioned number are delivered to the webhook of the messaging profile assigned to that number. Assign the profile by setting `profile_id` with `PATCH /v2/whatsapp/phone_numbers/{phone_number}/profile`. See [Receiving Webhooks](/docs/messaging/messages/receiving-webhooks).
## Verify the result
After the session reaches `SUCCESS`, list the WABAs with `GET /v2/whatsapp/business_accounts` and retrieve each number with `GET /v2/whatsapp/phone_numbers/{phone_number}`.
The WABA `status` is derived from Meta's `account_review_status` and `business_verification_status`:
| Status | Meaning |
| --- | --- |
| `ACTIVE` | Account review is `APPROVED` and business verification is `verified` |
| `PENDING` | Account review is `PENDING` |
| `REJECTED` | Account review is `REJECTED` |
| `INACTIVE` | Any other combination, such as an approved account with business verification not yet completed |
`INACTIVE` does not block sending. Meta applies the messaging limit for unverified businesses until business verification completes.
The phone number `status` is Meta's value. A number is ready for Cloud API messaging when `status` is `CONNECTED`. Coexistence numbers also need `coexistence_state` to reach `active`. See [WhatsApp Coexistence](/docs/messaging/whatsapp/coexistence).
## Remove a phone number
[`DELETE /v2/whatsapp/phone_numbers/{phone_number}`](/api-reference/whatsapp-phone-numbers/delete-a-whatsapp-phone-number) deregisters the number from Cloud API at Meta and removes it from Telnyx. The number stays in its WhatsApp Business Account at Meta. To register it in a different WABA, first delete it from the original WABA in WhatsApp Manager or migrate it through Embedded Signup.
Coexistence numbers cannot be removed through this endpoint. Disconnect them from the WhatsApp Business app instead. See [Disconnect coexistence](/docs/messaging/whatsapp/coexistence#disconnect-coexistence).
## Next steps
Send templates, text, media, and interactive messages
Send a first WhatsApp message end-to-end
Handle inbound messages and delivery status callbacks
## Troubleshooting
**State:** `SHARE_CREDIT_LINE`
Meta rejects the credit line in these cases:
- **`is already using a payment method`:** The WABA already has a payment method, for example from a previous provider or its own Meta billing, so Meta does not attach the Telnyx credit line. Select or create a different WABA in the signup window. To move a number from the old WABA, delete it there first.
- **`Credit Allocation Not Allowed During Month-End Close`:** Meta's monthly billing close is running. Retry once the close completes, usually within the first few days of the month.
- **`Balance from previous month`:** The WABA has spend from the previous billing cycle on its own Meta billing. Retry once that billing is settled.
**State:** `SUBSCRIBE_WEBHOOKS` or `ASSIGNED_USERS`
`errors` contains `Telnyx cannot access WhatsApp Business Account`. The WABA was not shared with Telnyx during signup. Grant the Telnyx system user named in the error the `MANAGE` task on the WABA, then retry. Tech providers can avoid this by passing `extras.setup.solutionID` in Embedded Signup. See the [Tech Provider guide](/docs/messaging/whatsapp/embedded-signup/tech-provider).
**State:** `REGISTER_PHONE_NUMBER`
`errors` reports that the phone number is active in the WhatsApp Business App. Standard Cloud API signup cannot register these numbers. Use [WhatsApp Coexistence](/docs/messaging/whatsapp/coexistence) instead.
WhatsApp Manager shows `Please contact Telnyx to resolve phone number connection issue` and the number does not appear in the Telnyx Portal. Meta completed its steps, but the signup result never reached Telnyx. This happens when Meta's signup window stays open after the last step instead of closing. Retry Embedded Signup and select the same WABA and phone number. If the number still does not appear, contact Telnyx support with the WABA ID and phone number.
Meta returns `This number is registered to an existing WhatsApp account. To use this number, disconnect it from the existing account.` Disconnect or delete the number from the existing account first. Meta notes it may take up to 3 minutes for the number to become available.
Meta verifies numbers by SMS or voice call only. SMS delivery to virtual numbers (VoIP and DID ranges) is not guaranteed, so request the code by voice call instead. Confirm the number accepts inbound calls.
Meta initially caps new business portfolios at two registered phone numbers. Meta raises the cap to 20 when the business is verified or reaches a messaging limit of 2,000.
Meta initially limits each business portfolio to 20 WABAs. Remove unused WABAs from the business portfolio or use an existing WABA.
**Symptom:** Embedded signup popup fails to load, shows a blank page, or gets stuck during the Facebook OAuth step.
**Causes:**
- Ad blockers (uBlock Origin, Adblock Plus, etc.) blocking Facebook/Meta domains
- Privacy extensions (Privacy Badger, Ghostery) blocking third-party scripts
- Browser security settings blocking cross-origin popups
**Solutions:**
1. Temporarily disable ad blockers and privacy extensions for the signup flow
2. Add `facebook.com` and `meta.com` domains to the extension's allowlist
3. Try using a different browser profile without extensions
4. Use Chrome Incognito mode (extensions are typically disabled by default)
5. Ensure popup blockers allow popups from the Telnyx Portal domain
**Note:** The WhatsApp embedded signup flow is powered by Meta/Facebook. The OAuth consent screen and business verification steps are hosted on Facebook's servers, which means browser extensions that block Facebook domains will prevent the signup from completing.
---
### Tech Provider Embedded Signup
> Source: https://developers.telnyx.com/docs/messaging/whatsapp/embedded-signup/tech-provider.md
WhatsApp Tech Provider Embedded Signup lets ISVs and SaaS platforms embed Meta's WhatsApp onboarding flow directly into their own portal. Your end-customers can create and register a WhatsApp Business Account (WABA), claim a phone number, and begin messaging without leaving your application.
Unlike the [standard embedded signup flow](/docs/messaging/whatsapp/embedded-signup), which is designed for direct Telnyx customers, the Tech Provider flow is built for partners who manage multiple end-customers and need programmatic control over WABA provisioning.
## Overview
As a Tech Provider, you build the onboarding experience into your own product. The end-customer clicks a button in your portal, completes Meta's embedded signup flow, and your backend receives the resulting WABA ID and phone number ID. You then register the WABA with Telnyx so that it can use Telnyx's messaging infrastructure.
Telnyx offers two integration paths: a **hosted signup** option where Telnyx manages the signup UI and backend processing, and a **custom integration** option where you embed Meta's Facebook SDK directly into your own portal. Both paths support WhatsApp Business App coexistence. See [Step 3](#step-3-choose-your-integration-path) for a comparison.
To connect a number that must remain active in the WhatsApp Business app, use coexistence onboarding: set `signup_type` to `coexistence` when creating a hosted signup, or use the coexistence configuration in the custom integration.
```mermaid
sequenceDiagram
participant EndCustomer as End Customer
participant PartnerPortal as Your Portal
participant Meta as Meta
participant Telnyx as Telnyx API
EndCustomer->>PartnerPortal: Clicks "Connect WhatsApp"
PartnerPortal->>Meta: Launches embedded signup (FB.login)
Meta->>EndCustomer: Shows signup & business verification
EndCustomer->>Meta: Completes signup
Meta-->>PartnerPortal: Returns WABA ID + Phone Number ID
PartnerPortal->>Telnyx: POST /whatsapp/business_accounts/tech_provider
Telnyx-->>PartnerPortal: Returns an asynchronous signup session
PartnerPortal->>Telnyx: Polls signup session status
Telnyx-->>PartnerPortal: Signup complete, number ready
PartnerPortal->>EndCustomer: WhatsApp connected ✅
```
### Tech Provider vs. Direct Customer
| | Tech Provider | Direct Customer |
|---|---|---|
| **Who uses this?** | ISVs, SaaS platforms, resellers | Individual businesses |
| **Onboarding** | Embedded in your portal | Via Telnyx portal or API |
| **WABA ownership** | Your end-customers own their WABAs | You own your WABA |
| **API calls** | You call Telnyx on behalf of customers | You call Telnyx directly |
| **Scale** | Many WABAs under one integration | One WABA per integration |
## Prerequisites
Before you begin, make sure you have:
- A [Meta Business Account](https://business.facebook.com/) with admin access
- A **Meta Tech Provider App** that has been approved by Meta with the following permissions:
- `whatsapp_business_messaging`
- `whatsapp_business_management`
- The Tech Provider onboarding process completed in [Meta App Dashboard](https://developers.facebook.com/apps/)
- A [Telnyx account](https://telnyx.com/sign-up) with an API key
All links to Meta documentation in this guide require that you are logged in to a Meta Business account. If you don't have one, create one at [business.facebook.com](https://business.facebook.com/) before proceeding.
## Step 1: Create and configure your Meta Tech Provider App
Go to [Meta App Dashboard](https://developers.facebook.com/apps/) and create a new app. Select **Business** as the app type.
In your app's dashboard, add the **WhatsApp** product. This will generate the permissions you need.
Under **App Review > Permissions and Features**, request the following permissions:
| Permission | Purpose |
|---|---|
| `whatsapp_business_messaging` | Send and receive WhatsApp messages on behalf of your customers |
| `whatsapp_business_management` | Manage WhatsApp Business Accounts and phone numbers |
Both permissions require **Advanced Access** for production use.
Follow Meta's [Tech Provider onboarding guide](https://developers.facebook.com/docs/development/create-an-app/business-onboarding) to complete the Tech Provider registration process. This includes business verification and agreeing to Meta's Tech Provider terms.
Submit your app for Meta's App Review. Meta will evaluate your use case and grant (or deny) the requested permissions. This process typically takes several business days.
While waiting for App Review approval, you can test in **Development Mode** with your own test users. Production use requires **Live Mode** and Advanced Access permissions.
## Step 2: Link your Meta App to Telnyx
Once your Meta App is configured, you need to link it to Telnyx so that WABAs created through your embedded signup flow are registered on Telnyx's infrastructure.
The app is linked to a single Telnyx account. For managed-account integrations, link it to the manager account. Its managed accounts can use the same app without separate links.
In the [Meta App Dashboard](https://developers.facebook.com/apps/), change your app mode from **Development** to **Live**. This is required for Telnyx to initiate the partner invitation.
Reach out to your Telnyx representative or contact [support](https://support.telnyx.com) and provide:
- Your **Meta App ID** (found in your app's dashboard)
- Your **business name** as registered with Meta
Within 1–2 business days, you'll receive an email from Meta containing a partner invitation link. Click the link and accept the invitation to link your app with Telnyx.
Linking your app creates a Meta Multi-Partner Solution containing your app and Telnyx. Meta approval and an accepted partner invitation do not complete the Telnyx setup by themselves. Telnyx must also register your Meta App ID against your Telnyx account (the manager account if you use managed accounts) and enable the app link.
Verify the link with the API key of the account the app is linked to. For managed-account integrations, use the manager account's API key:
```bash
curl -X GET https://api.telnyx.com/v2/whatsapp/foreign_apps \
-H "Authorization: Bearer YOUR_API_KEY"
```
Confirm that `app_id` matches your Meta app, `solution_id` is populated, and `enabled` is `true`. Pass the returned `solution_id` in `extras.setup.solutionID` on every custom signup. Solutions are also listed under **Partner solutions** in the Meta App Dashboard.
Managed accounts use the `solution_id` from the manager account's response. This endpoint only returns the calling account's own app link, so a managed account's API key returns `404` even when the manager account's link is configured correctly.
If verification with the API key above returns `404` or `enabled` is `false`, contact [Telnyx support](https://support.telnyx.com) with that Telnyx account's UUID and Meta App ID. A `404` can mean that the app link is missing or that the App ID is linked to an unrelated Telnyx account.
After accepting the invitation, you can switch your app back to **Development** mode for testing. The link persists regardless of the app mode.
You must switch your app to **Live mode** before Telnyx can send the partner invitation. The invitation will fail if your app is in Development mode. After accepting the invite, you can switch back to Development mode for testing.
## Step 3: Choose your integration path
After your Meta App is linked to Telnyx, you have two ways to onboard your end-customers' WABAs. Choose the path based on whether the number must remain active in the WhatsApp Business app.
| | Option A: Hosted signup (recommended) | Option B: Custom integration |
|---|---|---|
| **Who it's for** | Most Tech Providers who want the fastest signup path | ISVs who want full control over the UX |
| **What you build** | One API call to generate a shareable URL | Facebook SDK integration in your own frontend |
| **Signup UI** | Telnyx-hosted page at `acct.fyi` | Embedded directly in your portal |
| **End-customer experience** | Clicks a link → completes Meta signup → done | Clicks a button in your app → Meta popup → you handle the response |
| **Backend code exchange** | Telnyx handles it automatically | Optional for your own Meta Graph API use; the Telnyx registration request uses the session event identifiers |
| **WABA registration** | Telnyx handles it automatically | You call Telnyx's registration API |
| **WhatsApp Business App coexistence** | Supported with `signup_type` set to `coexistence` | Supported with the coexistence configuration and completion event |
With the hosted signup flow, Telnyx hosts the embedded signup page. You generate a time-limited onboarding URL via API and share it with your end-customer. The customer visits the URL, completes Meta's signup, and Telnyx handles the code exchange, webhook subscription, credit line sharing, and phone number registration. No Facebook SDK or custom JavaScript is required.
For a phone number that must remain registered in the WhatsApp Business app, create the hosted signup with `signup_type` set to `coexistence`. The hosted page then launches Meta's coexistence flow. A hosted signup created without it launches standard Cloud API onboarding, where an existing Business App number appears as already in use.
```mermaid
sequenceDiagram
participant TechProvider as Tech Provider
participant Telnyx as Telnyx API
participant EndCustomer as End Customer
participant HostedPage as Telnyx hosted page
participant Meta as Meta
TechProvider->>Telnyx: POST /v2/whatsapp/hosted_signups
Telnyx-->>TechProvider: Returns onboarding URL (acct.fyi?token=…)
TechProvider->>EndCustomer: Shares URL via email/SMS/embed
EndCustomer->>HostedPage: Visits URL
HostedPage->>Meta: Launches Meta embedded signup
Meta->>EndCustomer: Shows signup & business verification
EndCustomer->>Meta: Completes signup
Meta-->>HostedPage: Returns credentials
HostedPage->>Telnyx: Registers WABA, applies credit line, subscribes webhooks
Telnyx-->>TechProvider: WABA registered & ready ✅
```
### Step 3A.1: Generate an onboarding URL
Call [`POST /v2/whatsapp/hosted_signups`](/api-reference/whatsapp-signup/create-a-whatsapp-hosted-signup) to create a JWT-authenticated onboarding URL for your end-customer. The URL is valid for up to 3 days.
To onboard a client into a managed account, create the hosted signup with that managed account's API key and explicitly set `app_id` to the app linked to the manager account in Step 2. Omitting `app_id` makes Telnyx look for an app link on the managed account itself, and the request fails. The WABA and its phone numbers belong to the managed account.
```bash
curl -X POST https://api.telnyx.com/v2/whatsapp/hosted_signups \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"app_id": "123456789012345"
}'
```
For a number that remains active in the WhatsApp Business app, add `signup_type`:
```json
{
"app_id": "123456789012345",
"signup_type": "coexistence"
}
```
#### Request body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| `app_id` | string | Yes | Your Meta App ID (the same one linked to Telnyx in Step 2) |
| `expires_at` | string (RFC 3339) | No | When the URL expires. Must be at most 3 days in the future. Defaults to 24 hours. |
| `signup_type` | string | No | `standard` (default) or `coexistence`. Use `coexistence` for a number that remains active in the WhatsApp Business app. |
This endpoint requires authentication with a Telnyx API key. You can create one in the [API Keys](https://portal.telnyx.com/#/app/api-keys) page.
### Response
```json
{
"data": {
"id": "0192345a-7b8c-7d9e-0f12-3456789abcde",
"record_type": "whatsapp_jwt",
"app_id": "123456789012345",
"created_at": "2026-07-08T12:00:00Z",
"expires_at": "2026-07-09T12:00:00Z",
"jwt": "eyJhbGciOiJSUzI1NiIs...",
"url": "https://acct.fyi?token=eyJhbGciOiJSUzI1NiIs..."
}
}
```
| Field | Description |
|---|---|
| `url` | The hosted onboarding URL to share with your end-customer |
| `jwt` | The raw JWT token (also embedded in the URL) |
| `expires_at` | When the URL expires (max 3 days) |
| `id` | Unique identifier for this signup session |
### Step 3A.2: Share the URL with your end-customer
Send the `url` to your end-customer via email, SMS, or embed it as a link in your own portal. When the customer visits the URL:
1. Telnyx validates the JWT and renders a signup page branded with your app's Meta configuration
2. The customer clicks **Get Started** to launch Meta's embedded signup popup
3. The customer signs in to Facebook, creates or selects a WABA, and verifies their phone number
4. On completion, Telnyx automatically handles the backend: code exchange, webhook subscription, credit line sharing, and phone number registration
You can poll the signup status via `GET /v2/whatsapp/signup/{session_id}/status` to track progress and surface it in your own dashboard.
### Step 3A.3: Check signup status (optional)
```bash
curl -X GET "https://api.telnyx.com/v2/whatsapp/signup/019f4253-e09a-7622-bc6f-625378711d78/status" \
-H "Authorization: Bearer YOUR_API_KEY"
```
Returns the signup session record with `status`, `state`, `waba_id`, and any errors. Use this to build a progress indicator in your portal or trigger downstream workflows when onboarding completes.
### What happens after signup
When the end-customer completes the hosted signup:
1. **Credit line is applied:** The WABA is associated with the Telnyx account whose API key created the hosted signup
2. **WABA is registered:** The WhatsApp Business Account is linked to Telnyx's messaging infrastructure
3. **Webhooks are subscribed:** Telnyx subscribes to Meta webhook events on the WABA's behalf
4. **Number is ready for messaging:** The phone number can send and receive WhatsApp messages through Telnyx
If you prefer to embed Meta's signup flow directly into your own portal, follow the steps below. This gives you full control over the UX and requires integrating the Facebook JavaScript SDK. Exchange the authorization code only if your application also needs to call Meta's Graph API directly.
Use this path for WhatsApp Business App coexistence. After your Telnyx app link is enabled, there is no additional per-account coexistence flag. Meta selects the coexistence experience from the `featureType` and `sessionInfoVersion` values passed to `FB.login()`.
### Step 3B.1: Implement the frontend embedded signup
Meta's embedded signup flow is triggered from your web application using the Facebook JavaScript SDK. Your frontend launches the signup dialog, the end-customer completes it, and you receive the resulting credentials.
Add the Facebook SDK to your web application:
```html
```
Replace `YOUR_GRAPH_API_VERSION` with a Graph API version supported by your Meta app, including the leading `v`. Use the same version for the server-side code exchange below.
Register a browser message listener before launching signup. Meta sends `FINISH` for standard onboarding and `FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING` for coexistence. Both events include the WABA ID, while coexistence also requires the phone number ID.
```javascript
window.addEventListener('message', (event) => {
if (
event.origin !== 'https://www.facebook.com' &&
event.origin !== 'https://web.facebook.com'
) {
return;
}
let session;
try {
session = typeof event.data === 'string'
? JSON.parse(event.data)
: event.data;
} catch {
return;
}
if (session?.type !== 'WA_EMBEDDED_SIGNUP') {
return;
}
const { waba_id, phone_number_id } = session.data ?? {};
if (session.event === 'FINISH' && waba_id) {
saveSignupSession({
event: session.event,
waba_id,
});
}
if (
session.event === 'FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING' &&
waba_id &&
phone_number_id
) {
saveSignupSession({
event: session.event,
waba_id,
phone_number_id,
});
}
});
```
Store the event and identifiers on your backend and associate them with the end-customer who started the flow. Ignore messages from any other origin or event type.
Call `FB.login()` with the `config_id` of your WhatsApp Business Configuration. Add `featureType` and `sessionInfoVersion` only when launching coexistence onboarding.
```javascript
function launchWhatsAppSignup({ coexistence = false } = {}) {
const extras = {
setup: {
solutionID: 'YOUR_SOLUTION_ID',
},
};
if (coexistence) {
extras.featureType = 'whatsapp_business_app_onboarding';
extras.sessionInfoVersion = '3';
}
FB.login(
function (response) {
if (response.authResponse) {
const code = response.authResponse.code;
// Optional: exchange the code if your app calls Meta's Graph API.
sendCodeToBackend(code);
} else {
console.error('User cancelled login or did not fully authorize.');
}
},
{
config_id: 'YOUR_CONFIG_ID',
response_type: 'code',
override_default_response_type: true,
extras,
}
);
}
```
Call `launchWhatsAppSignup()` for standard Cloud API onboarding. Call `launchWhatsAppSignup({ coexistence: true })` when the end-customer is connecting a number that remains active in the WhatsApp Business app.
If your application does not need a Meta customer access token, omit the `sendCodeToBackend` call. Telnyx registration uses the WABA and phone identifiers from the session event.
`solutionID` is required. It is the ID of the Meta Multi-Partner Solution that
links your Meta app to Telnyx, created when your app is linked in
[Step 2](#step-2-link-your-meta-app-to-telnyx). See that step for how to look it up.
Without it, your end-customer's WABA is created outside that solution and Telnyx
is not granted access to it. Registration then fails, and the WABA has to be
[shared with Telnyx manually](#granting-telnyx-access-to-an-existing-waba) before
it can be provisioned.
When you pass it, the WABA is created inside the solution and access is granted
to Telnyx automatically as part of your end-customer's normal signup.
The `FB.login()` callback and the session logging event provide different values. The callback provides an authorization code for obtaining a customer access token. The Telnyx registration request in the next step does not accept that token and uses the identifiers from the session event instead.
If your application needs to call Meta's Graph API directly, exchange the code on your backend. Never expose your Meta App Secret in browser code.
```bash
curl -X GET \
"https://graph.facebook.com/YOUR_GRAPH_API_VERSION/oauth/access_token?\
client_id=YOUR_APP_ID\
&client_secret=YOUR_APP_SECRET\
&code=AUTHORIZATION_CODE"
```
The response contains access token information:
```json
{
"access_token": "EAAJc...",
"token_type": "bearer",
"expires_in": 5183814
}
```
Before calling Telnyx, confirm which completion event you received and retain the identifiers required for that onboarding mode:
| Field | Standard onboarding | Coexistence onboarding |
|---|---|---|
| `event` | `FINISH` | `FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING` |
| `waba_id` | Required | Required |
| `phone_number_id` | Not required by the Telnyx registration request | Required |
Refer to Meta's [Embedded Signup implementation guide](https://developers.facebook.com/documentation/business-messaging/whatsapp/embedded-signup/implementation) for the standard event contract and the [Business App onboarding guide](https://developers.facebook.com/documentation/business-messaging/whatsapp/embedded-signup/onboarding-business-app-users/) for the coexistence event contract. Telnyx handles the remaining WABA provisioning after the registration request in the next step.
### Step 3B.2: Register the WABA with Telnyx
Use the registration request that matches the completion event captured by your frontend. Both requests start an asynchronous provisioning workflow.
To register a client's WABA to a managed account, call `POST /v2/whatsapp/business_accounts/tech_provider` with that managed account's API key and the `app_id` linked to its manager account in Step 2. The WABA and its phone numbers belong to the managed account.
#### Standard Cloud API onboarding request
When the session event is `FINISH`, submit the WABA ID. Without `phone_number_id`, Telnyx retrieves the WABA's phone numbers and registers every one of them with Cloud API, including numbers that are already connected. With `phone_number_id`, Telnyx registers only that number. Pass `phone_number_id` when adding a number to a WABA that already has live numbers.
```bash
curl -X POST https://api.telnyx.com/v2/whatsapp/business_accounts/tech_provider \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"waba_id": "123456789012345",
"app_id": "123456789012345",
"customer_id": "acme-corp-42",
"event": "FINISH"
}'
```
#### Coexistence onboarding request
When the session event is `FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING`, submit both the WABA ID and phone number ID. Telnyx uses the event to select the coexistence provisioning path.
```bash
curl -X POST https://api.telnyx.com/v2/whatsapp/business_accounts/tech_provider \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"waba_id": "123456789012345",
"phone_number_id": "987654321098765",
"app_id": "123456789012345",
"customer_id": "acme-corp-42",
"event": "FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING"
}'
```
#### Request body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| `waba_id` | string | Yes | The WABA ID returned by Meta's embedded signup flow |
| `phone_number_id` | string | For coexistence | The phone number ID returned by Meta's signup session event. Optional for standard onboarding, where it limits registration to that number |
| `app_id` | string | Yes | Your Meta App ID. It must be enabled and linked to the Telnyx account used by this request or its manager account |
| `customer_id` | string | No | An optional identifier you assign to track this customer (e.g., your internal customer ID) |
| `event` | string | For coexistence | Use `FINISH` for standard onboarding or `FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING` for coexistence. Omitting the field selects standard onboarding |
This endpoint requires authentication with a Telnyx API key via the `Authorization: Bearer` header. Create an API key from your Telnyx account on the [API Keys](https://portal.telnyx.com/#/app/api-keys) page.
#### Response
A successful response confirms that Telnyx accepted the signup for asynchronous processing. It does not mean that provisioning or synchronization is complete.
```json
{
"data": {
"record_type": "whatsapp_session_id",
"session_id": "019f4253-e09a-7622-bc6f-625378711d78",
"user_id": "00000000-0000-4000-8000-000000000000",
"status": "PENDING",
"waba_id": "123456789012345",
"customer_id": "acme-corp-42"
}
}
```
Poll the returned session until it reaches a terminal state:
```bash
curl -X GET \
"https://api.telnyx.com/v2/whatsapp/signup/019f4253-e09a-7622-bc6f-625378711d78/status" \
-H "Authorization: Bearer YOUR_API_KEY"
```
#### What happens after registration
For standard Cloud API onboarding, Telnyx:
1. Subscribes the WABA to required webhook fields
2. Assigns the required users and shares the credit line
3. Registers the WABA's phone numbers with Cloud API
4. Stores the WABA and phone numbers in the Telnyx account
Standard provisioning is complete after the signup session reaches `state: COMPLETE` and `status: SUCCESS`.
For coexistence onboarding, Telnyx:
1. Subscribes the WABA to required webhook fields
2. Assigns the required users and shares the credit line
3. Verifies that Meta reports `is_on_biz_app: true` and `platform_type: CLOUD_API`
4. Skips Cloud API phone registration because the number is already registered in the Business App
5. Starts the required coexistence synchronization lifecycle
The signup session can reach `SUCCESS` before coexistence synchronization finishes. After signup succeeds, retrieve the phone with `GET /v2/whatsapp/phone_numbers/{phone_number}` and inspect `coexistence_state`. The number becomes eligible for Cloud API sends only after that state reaches `active`. See [WhatsApp Coexistence](/docs/messaging/whatsapp/coexistence) for synchronization deadlines and lifecycle behavior.
## Granting Telnyx access to an existing WABA
If a WABA was created before you started passing `solutionID`, Telnyx has no access to it
and registration fails. Passing the solution ID fixes every subsequent signup, but it does
not apply retroactively, so an affected WABA has to be shared with Telnyx once by hand.
This is done in Meta Business settings by the business portfolio that **owns** the WABA.
For a WABA created through your embedded signup, that is usually your end-customer, not you.
Go to [business.facebook.com/settings](https://business.facebook.com/settings) and select the business portfolio that owns the WABA.
In the left sidebar, select **Users**, then **Partners**.
Select **Telnyx** in the list. If Telnyx is not listed, choose **Add**, then add a partner using Telnyx's business portfolio ID **`1770378036525495`**.
Choose **Assign assets**, then **WhatsApp accounts**, and select the WABA.
Enable **Full control**, then select **Assign**.
Tell Telnyx the WABA has been shared and wait for confirmation that the access has landed before retrying registration. A share that appears complete in the dashboard does not always take effect immediately.
Assigning Telnyx's system user directly to the WABA does not work. Meta scopes system users
to the business that owns them, so the assignment is rejected. The partner share above is the
supported route.
## Coexistence behavior and limitations
| Question | Behavior |
|---|---|
| Can I use the Telnyx-hosted signup page? | Yes. Create the hosted signup with `signup_type` set to `coexistence`. |
| Does the customer keep using the Business App? | Yes. The number remains active in the WhatsApp Business app while Telnyx connects it to Cloud API. |
| Does the 24-hour customer service window change? | No. Standard Cloud API service-window and template rules still apply. Business App sends delivered as `message.echo` do not open or extend that window. |
| What happens if an agent and automation reply at the same time? | WhatsApp can deliver both messages. Telnyx reports the Business App send as `message.echo`, but does not lock the conversation or remove a simultaneous API send. Your application must coordinate agents and automation if duplicate replies are a concern. |
| Is the number ready immediately after registration? | No. Telnyx initiates the required contact and history synchronization within Meta's 24-hour deadline. Cloud API sends remain blocked until the coexistence state is `active`. |
| Does Telnyx expose synchronized history through customer webhooks? | No. Telnyx processes Meta's `history` and `smb_app_state_sync` protocol events internally. Customer-facing history storage and retrieval are not part of the current integration. |
For webhook payloads and processing guidance, see [WhatsApp Coexistence webhooks](/docs/messaging/whatsapp/coexistence/webhooks).
## Troubleshooting
- Verify that your app was in **Live mode** when you contacted Telnyx
- Check your spam and junk folders
- Ensure the email associated with your Meta Business account is correct
- Confirm with your Telnyx representative that they initiated the invitation
- The invitation typically arrives within 1–2 business days
- Review Meta's rejection reasons carefully in the App Review section of your dashboard
- Common reasons include: insufficient permissions justification, unclear use case description, or missing screencast
- Update your submission with clearer documentation and resubmit
- Ensure your app is functional and testable during the review period
- Confirm the Facebook SDK is loaded before calling `FB.login()`
- Check that your `config_id` is correct and matches a WhatsApp Business Configuration in your app
- Ensure your app has the `whatsapp_business_messaging` and `whatsapp_business_management` permissions
- Open your browser's developer console for error messages from the SDK
Meta Tech Provider approval is not enough to use Telnyx signup. Your Meta app must also have an accepted Telnyx partner invitation and an enabled Telnyx app link.
- Call `GET /v2/whatsapp/foreign_apps` with the API key of the account the app is linked to. For managed-account integrations, use the manager account's API key
- Confirm that the response contains your Meta App ID, a `solution_id`, and `enabled: true`
- Managed accounts use the `solution_id` from the manager account's response. A `404` with a managed account's API key is expected and does not indicate a missing manager account link
- If verification with the API key of the account the app is linked to returns `404`, contact [Telnyx support](https://support.telnyx.com) with that Telnyx account's UUID and Meta App ID
- If the returned App ID is linked to an unrelated Telnyx account or the link is disabled, ask support to correct or enable the link
Coexistence onboarding through `POST /v2/whatsapp/business_accounts/tech_provider` returns this error. Hosted signup returns `foreign app with this ID is not registered to the user` for the same problem.
The `app_id` is not linked to Telnyx, or the API key belongs to neither the Telnyx account the app is linked to (the manager account) nor one of its managed accounts.
Use an API key from the manager account or one of its managed accounts. To onboard a client into a managed account, use that managed account's API key with the same linked `app_id`.
The signup launched standard Cloud API onboarding instead of the WhatsApp Business App coexistence variant.
For hosted signup, create a new hosted signup with `signup_type` set to `coexistence` and share the new URL.
For the custom integration:
- Pass `featureType: 'whatsapp_business_app_onboarding'`
- Pass `sessionInfoVersion: '3'`
- Handle the `FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING` session event
- Send that event name, `waba_id`, `phone_number_id`, and your `app_id` to the Tech Provider registration endpoint
Do not remove the number from the WhatsApp Business app when the goal is coexistence.
- Verify you are using a valid Telnyx API key
- Ensure the `Authorization: Bearer YOUR_API_KEY` header is included in your request
- Check that your API key has not expired or been revoked
- Read the WABA ID and phone number ID from Meta's `WA_EMBEDDED_SIGNUP` browser message, not the `FB.login()` authorization callback
- Confirm that your listener accepts Meta's documented origins and safely parses string and object event data
- For coexistence, handle only `FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING`
- Verify that the user completed all steps in the Meta signup flow
Telnyx returns an error stating it cannot access the WhatsApp Business Account, and
the signup does not complete. Meta reports the WABA as missing permissions.
This means the WABA was created outside the Multi-Partner Solution that links your
app to Telnyx, so Telnyx was never granted access to it.
- If you integrate with the Facebook SDK directly, confirm you passed `solutionID` inside `extras.setup` when calling `FB.login()`
- Ask your Telnyx representative to confirm your solution is active and that your Meta app is linked
- For a WABA that was already created without the solution ID, follow [Granting Telnyx access to an existing WABA](#granting-telnyx-access-to-an-existing-waba). Passing the solution ID prevents this for subsequent signups but does not apply retroactively
- Ensure you're using the full URL returned by `POST /v2/whatsapp/hosted_signups`, including the `?token=` query parameter
- Check that the JWT has not expired. Tokens are valid for a maximum of 3 days from creation
- If the token has expired, generate a new one via the API
- Verify the phone number entered by the end-customer is correct and can receive SMS
- Check that the phone number is not already registered with another WhatsApp account
- Try requesting the verification code via phone call instead of SMS
- Ensure the end-customer's phone carrier is not blocking Meta's verification messages
Some platforms, including Meta, may not deliver SMS verification codes to virtual numbers
(VoIP and DID ranges), preferring voice-call verification instead. This applies to numbers in
countries where the local numbering plan classifies them as virtual (for example, Belgian
mobile numbers of this kind). Meta documents this in its
[phone number requirements](https://developers.facebook.com/documentation/business-messaging/whatsapp/business-phone-numbers/phone-numbers#business-phone-number-types):
SMS OTP delivery to VoIP numbers is *Not Recommended*; voice OTP is *Standard*. If the SMS code
does not arrive, retry with **phone call verification** before contacting support.
## Next steps
Send your first WhatsApp message with Telnyx
Direct customer embedded signup flow
Understand synchronization, lifecycle states, and send eligibility
Handle Business App message echoes and lifecycle events
Explore WhatsApp API endpoints
---
## Sending Messages
### Send Messages
> Source: https://developers.telnyx.com/docs/messaging/whatsapp/send-messages.md
Send WhatsApp messages using `POST /v2/messages/whatsapp`. All message types use the same endpoint — the `whatsapp_message` object determines the message type.
```
POST https://api.telnyx.com/v2/messages/whatsapp
```
### Request Structure
Every request requires these fields:
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `from` | string | Yes | Your WhatsApp-enabled phone number (E.164) |
| `to` | string | Yes | Recipient phone number (E.164) or [BSUID](#reply-to-a-bsuid) |
| `whatsapp_message` | object | Yes | Message content — structure varies by type |
| `webhook_url` | string | No | URL for delivery status callbacks |
The messaging profile is automatically resolved from the `from` number. You do not need to pass `messaging_profile_id`.
The `whatsapp_message` object must include a `type` field and the corresponding content object. Supported types: `text`, `template`, `image`, `video`, `document`, `audio`, `sticker`, `location`, `contacts`, `interactive`, `reaction`.
---
## Template Messages
Template messages are required to start conversations outside the 24-hour window. Templates must be pre-approved by Meta.
Error code `40008` indicates the template could not be used for sending. Possible causes include: the template is still pending review, was rejected by Meta, has been paused due to quality issues, or was disabled. Check template status in the Telnyx Portal or via `GET /v2/whatsapp/message_templates`.
Text, media, and interactive messages can only be sent within a 24-hour conversation window. The window opens when the recipient sends a message to the business number. Outside this window, use an approved template message to initiate the conversation.
For comprehensive template creation guidance, see the [Manage Templates guide](/docs/messaging/whatsapp/manage-templates).
```bash cURL
curl -X POST https://api.telnyx.com/v2/messages/whatsapp \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "template",
"template": {
"name": "order_confirmation",
"language": {
"policy": "deterministic",
"code": "en_US"
},
"components": [
{
"type": "body",
"parameters": [
{"type": "text", "text": "John"},
{"type": "text", "text": "ORD-12345"}
]
}
]
}
}
}'
```
```python Python
from telnyx import Telnyx
client = Telnyx(api_key="YOUR_API_KEY")
client.messages.send_whatsapp(
from_="+15551234567",
to="+15557654321",
whatsapp_message={
"type": "template",
"template": {
"name": "order_confirmation",
"language": {"policy": "deterministic", "code": "en_US"},
"components": [
{
"type": "body",
"parameters": [
{"type": "text", "text": "John"},
{"type": "text", "text": "ORD-12345"}
]
}
]
}
}
)
```
```javascript Node.js
import Telnyx from 'telnyx';
const client = new Telnyx({apiKey: 'YOUR_API_KEY'});
const message = await client.messages.sendWhatsapp({
from: '+15551234567',
to: '+15557654321',
whatsapp_message: {
type: 'template',
template: {
name: 'order_confirmation',
language: { policy: 'deterministic', code: 'en_US' },
components: [
{
type: 'body',
parameters: [
{ type: 'text', text: 'John' },
{ type: 'text', text: 'ORD-12345' }
]
}
]
}
}
});
```
### Sending by Template ID
Instead of specifying `name` and `language`, you can reference a template by its Telnyx UUID (`template_id`). The name and language are resolved automatically from the database.
```bash cURL
curl -X POST https://api.telnyx.com/v2/messages/whatsapp \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "template",
"template": {
"template_id": "019cd44b-3a1c-781b-956e-bd33e9fd2ac6",
"components": [
{
"type": "body",
"parameters": [
{"type": "text", "text": "John"},
{"type": "text", "text": "ORD-12345"}
]
}
]
}
}
}'
```
You can find a template's UUID by listing templates via `GET /v2/whatsapp/message_templates`. The `id` field in the response is the `template_id` you can use when sending.
### Template Components
Templates use `components` to pass dynamic content into header, body, and button slots:
| Component `type` | `sub_type` | Use |
|-------------------|-----------|-----|
| `header` | — | Media or text for template header |
| `body` | — | Variable substitution in body text |
| `button` | `quick_reply` | Quick reply button payload |
| `button` | `url` | Dynamic URL suffix for CTA buttons |
| `button` | `flow` | Flow token and initial data for a flow button |
### Media Header Template
To send a template with an image header:
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "template",
"template": {
"name": "promo_with_image",
"language": {"policy": "deterministic", "code": "en_US"},
"components": [
{
"type": "header",
"parameters": [
{"type": "image", "image": {"link": "https://example.com/promo.jpg"}}
]
},
{
"type": "body",
"parameters": [
{"type": "text", "text": "20%"}
]
}
]
}
}
}
```
Header parameters also support `document` and `video` types with the same `{link, caption, filename}` structure.
---
## Text Messages
Send plain text within the 24-hour conversation window. Body must be 1–4096 bytes.
```bash cURL
curl -X POST https://api.telnyx.com/v2/messages/whatsapp \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "text",
"text": {
"body": "Thanks for reaching out! How can we help?",
"preview_url": false
}
}
}'
```
```python Python
from telnyx import Telnyx
client = Telnyx(api_key="YOUR_API_KEY")
client.messages.send_whatsapp(
from_="+15551234567",
to="+15557654321",
whatsapp_message={
"type": "text",
"text": {
"body": "Thanks for reaching out! How can we help?",
"preview_url": False
}
}
)
```
```javascript Node.js
import Telnyx from 'telnyx';
const client = new Telnyx({apiKey: 'YOUR_API_KEY'});
const message = await client.messages.sendWhatsapp({
from: '+15551234567',
to: '+15557654321',
whatsapp_message: {
type: 'text',
text: {
body: 'Thanks for reaching out! How can we help?',
preview_url: false
}
}
});
```
Set `preview_url: true` to render link previews when the message body contains a URL.
---
## Media Messages
Send images, videos, documents, audio, and stickers. Each media object requires exactly one of `link` (URL) or `id` (Meta media ID). Captions are optional and limited to 1024 bytes.
Each media field (`image`, `video`, `document`, `audio`, `sticker`) must be a JSON object, not a bare URL string. Put the file URL in the `link` field:
- Correct: `"image": { "link": "https://example.com/receipt.png" }`
- Incorrect: `"image": "https://example.com/receipt.png"`
Use `link` for a publicly accessible URL that Telnyx fetches and forwards to WhatsApp. Use `id` only for a Meta-issued media ID returned by a prior upload; `id` is not a URL. A bare string, or a URL placed in `id`, is rejected.
### Image
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "image",
"image": {
"link": "https://example.com/receipt.png",
"caption": "Your receipt for order #12345"
}
}
}
```
### Document
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "document",
"document": {
"link": "https://example.com/invoice.pdf",
"filename": "invoice_12345.pdf",
"caption": "Invoice for March 2026"
}
}
}
```
### Video
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "video",
"video": {
"link": "https://example.com/tutorial.mp4",
"caption": "Setup walkthrough"
}
}
}
```
### Audio
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "audio",
"audio": {
"link": "https://example.com/voicenote.ogg"
}
}
}
```
### Sticker
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "sticker",
"sticker": {
"link": "https://example.com/sticker.webp"
}
}
}
```
Stickers do not support captions. Audio does not support captions. Only one media type per message.
---
## Downloading Inbound Media
Inbound WhatsApp messages deliver media (images, audio, video, documents, stickers) inside the message webhook. Each media object also carries `mime_type` and `sha256` for integrity checks.
In the normal case, Telnyx fetches the media ahead of time and the media object contains a ready-to-download Telnyx **`url`** — download the file directly from that URL:
```json
{
"type": "image",
"image": {
"url": "https://.../telnyx-hosted-media",
"mime_type": "image/jpeg",
"sha256": "..."
}
}
```
### Fallback: retrieving by media ID
If Telnyx could not pre-fetch or store the file, the media object instead contains a Meta media **`id`** and no `url`. A `url`, when present, is always Telnyx-hosted, never a Meta `lookaside.fbsbx.com` link. The object retains `mime_type` and `sha256` in either case for integrity checks. Retrieve the file by media `id` with:
```
GET https://api.telnyx.com/v2/whatsapp/media/{phone_number}/{id}
```
| Path parameter | Description |
|----------------|-------------|
| `phone_number` | The business WhatsApp number that received the message, in E.164 format (e.g. `+34881170453`). |
| `id` | The Meta media `id` from the inbound webhook's media object. |
Authenticate with the same `Authorization: Bearer YOUR_API_KEY` header as the rest of the v2 API. Telnyx resolves the media with Meta and streams the file back: the response body is the **raw media bytes**, and the `Content-Type` header is the media's MIME type (for example `image/jpeg` or `application/pdf`). There is no download URL or expiry to manage: each request fetches the file fresh.
```bash cURL
curl -X GET "https://api.telnyx.com/v2/whatsapp/media/+34881170453/1330663939258924" \
-H "Authorization: Bearer YOUR_API_KEY" \
--output received-image.jpg
```
This endpoint is for **inbound** WhatsApp media only. The MMS media endpoints (`/v2/media/...`) do not apply to WhatsApp.
Error responses:
| Status | Meaning |
|--------|---------|
| `404` | The phone number was not found, or the media ID could not be resolved with Meta (for example, expired or already deleted on Meta's side). |
| `503` | Telnyx could not fetch the media from Meta. |
---
## Location Messages
Share a location pin. Latitude and longitude are passed as **strings** (decimal format).
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "location",
"location": {
"latitude": "40.7128",
"longitude": "-74.0060",
"name": "Telnyx HQ",
"address": "311 W 43rd St, New York, NY"
}
}
}
```
Latitude must be between -90 and 90. Longitude must be between -180 and 180.
---
## Contact Messages
Share one or more contact cards (1–257 contacts per message).
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "contacts",
"contacts": [
{
"name": {
"formatted_name": "Jane Smith",
"first_name": "Jane",
"last_name": "Smith"
},
"phones": [
{"phone": "+15559876543", "type": "WORK"}
],
"emails": [
{"email": "jane@example.com", "type": "WORK"}
]
}
]
}
}
```
---
## Interactive Messages
Interactive messages let recipients tap buttons, select from lists, or open URLs. Supported `interactive.type` values:
| Type | Description |
|------|-------------|
| `button` | Up to 3 quick reply buttons |
| `cta_url` | Call-to-action URL button |
| `list` | Selectable list with sections and rows |
| `location_request_message` | Request the recipient's location |
| `carousel` | Scrollable cards with media and buttons |
| `flow` | Open a WhatsApp Flow, such as a form or booking screen |
### Quick Reply Buttons
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "interactive",
"interactive": {
"type": "button",
"body": {"text": "Would you like to schedule a callback?"},
"action": {
"buttons": [
{"type": "reply", "reply": {"id": "yes_callback", "title": "Yes, call me"}},
{"type": "reply", "reply": {"id": "no_thanks", "title": "No thanks"}}
]
}
}
}
}
```
When a recipient taps a button, you receive an inbound webhook with the button's `id` in the payload.
### CTA URL Button
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "interactive",
"interactive": {
"type": "cta_url",
"body": {"text": "Track your shipment"},
"action": {
"name": "cta_url",
"parameters": {
"display_text": "Track Order",
"url": "https://example.com/track/12345"
}
}
}
}
}
```
### List Messages
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "interactive",
"interactive": {
"type": "list",
"body": {"text": "Select a support topic:"},
"action": {
"button": "Choose topic",
"sections": [
{
"title": "Account",
"rows": [
{"id": "billing", "title": "Billing", "description": "Payment and invoice questions"},
{"id": "access", "title": "Account Access", "description": "Login and permissions"}
]
},
{
"title": "Technical",
"rows": [
{"id": "api", "title": "API Issues", "description": "Integration and endpoint help"},
{"id": "webhooks", "title": "Webhooks", "description": "Delivery and configuration"}
]
}
]
}
}
}
}
```
### Location Request
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "interactive",
"interactive": {
"type": "location_request_message",
"body": {"text": "Share your location so we can find the nearest store."},
"action": {"name": "send_location"}
}
}
}
```
### Flow Messages
Flows open a multi-screen form inside WhatsApp, such as a sign-up, booking, or intake form. Create the Flow in WhatsApp Manager first (see Meta's [WhatsApp Flows documentation](https://developers.facebook.com/docs/whatsapp/flows)), then send it with `interactive.type: "flow"`. Publish the Flow before sending it to customers, or send it with `mode: "draft"` to test.
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "interactive",
"interactive": {
"type": "flow",
"header": {"type": "text", "text": "Patient intake"},
"body": {"text": "Please fill in a few details before your appointment."},
"footer": {"text": "Takes about 2 minutes"},
"action": {
"name": "flow",
"parameters": {
"flow_message_version": "3",
"flow_id": "1234567890123456",
"flow_cta": "Start form",
"flow_token": "intake-order-12345",
"flow_action": "navigate",
"flow_action_payload": {
"screen": "WELCOME"
}
}
}
}
}
}
```
| Parameter | Required | Description |
|-----------|----------|-------------|
| `flow_message_version` | Yes | Must be `3`. |
| `flow_id` or `flow_name` | Yes | The Flow to open. Provide exactly one. |
| `flow_cta` | Yes | Text on the button that opens the Flow. 1–20 characters, no emoji. |
| `flow_token` | No | Business-generated identifier for the Flow session. Returned in the Flow response for correlation. |
| `flow_action` | No | `navigate` (default) opens the screen in `flow_action_payload`. `data_exchange` requests the first screen from the business's Flow endpoint. |
| `flow_action_payload` | No | `screen` is the first screen ID. `data` is optional input for that screen and must be a non-empty object. When omitted, the Flow opens on its first entry screen. Omit it when `flow_action` is `data_exchange`. |
| `mode` | No | `published` (default) or `draft` to test an unpublished Flow. |
Flow messages require `body.text` (1–1024 characters) and `action.name` set to `flow`. `action` accepts only `name` and `parameters`, and `parameters` accepts only the Flow fields above. Header text and footer text are limited to 60 characters. Like other interactive messages, flow messages can only be sent within the 24-hour conversation window.
To start a Flow outside the window, send an approved template that has a flow button. Pass the `flow_token` and optional `flow_action_data` in the button component:
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "template",
"template": {
"name": "patient_intake",
"language": {"policy": "deterministic", "code": "en_US"},
"components": [
{
"type": "button",
"sub_type": "flow",
"index": 0,
"parameters": [
{
"type": "action",
"action": {
"flow_token": "intake-order-12345",
"flow_action_data": {"patient_id": "p-1"}
}
}
]
}
]
}
}
}
```
#### Receiving Flow Responses
When the recipient completes the Flow, Telnyx sends a `message.received` webhook. The submitted data is in `body.interactive.nfm_reply.response_json` as a JSON-encoded string and includes the `flow_token` from the original message.
```json
{
"data": {
"event_type": "message.received",
"payload": {
"direction": "inbound",
"type": "WHATSAPP",
"body": {
"from": "+15557654321",
"id": "c7b87bc9-d220-4b2c-916a-83c11ef9821f",
"foreign_id": "wamid.HBgLMTU1NTc2NTQzMjEVAgASGBQzQTRBNjU5OUFFRTAzODEwMTQ0RgA=",
"type": "interactive",
"interactive": {
"type": "nfm_reply",
"nfm_reply": {
"name": "flow",
"body": "Sent",
"response_json": "{\"flow_token\":\"intake-order-12345\",\"full_name\":\"Jane Doe\",\"date_of_birth\":\"1990-01-01\"}"
}
}
}
}
}
}
```
Parse `response_json` to read the submitted fields. Field names match those defined in the Flow.
---
## Reactions
React to a received message with an emoji.
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "reaction",
"reaction": {
"message_id": "wamid.ABGGFlA5FpafAgo6tHcNmNjXhvRm",
"emoji": "👍"
}
}
}
```
---
## Reply Context
Reply to a specific message by including `context.message_id`:
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "text",
"context": {
"message_id": "wamid.ABGGFlA5FpafAgo6tHcNmNjXhvRm"
},
"text": {
"body": "Thanks for your order! It will ship tomorrow."
}
}
}
```
---
## Reply to a BSUID
When a WhatsApp user hides their phone number behind a username, inbound messages identify them by a business-scoped user ID (BSUID) in `body.from_user_id`, for example `US.13491208655302741918`. A BSUID is a two-letter country code, a period, and up to 128 alphanumeric characters. Some BSUIDs include `ENT.` after the country code.
A BSUID is scoped to a business portfolio. To reply, pass the BSUID as `to` and send from a phone number in the same portfolio:
```json
{
"from": "+15551234567",
"to": "US.13491208655302741918",
"whatsapp_message": {
"type": "text",
"text": {
"body": "Thanks for reaching out. How can we help?"
}
}
}
```
Text, media, and interactive replies follow the 24-hour customer service window. Template messages can be sent to a BSUID, except authentication templates, which require a phone number. Meta rejects authentication templates sent to a BSUID with error `131062`.
---
## Callback Tracking
Use `biz_opaque_callback_data` to attach tracking data that will be returned in delivery webhooks:
```json
{
"from": "+15551234567",
"to": "+15557654321",
"whatsapp_message": {
"type": "text",
"biz_opaque_callback_data": "order_12345_confirmation",
"text": {
"body": "Your order has been confirmed."
}
}
}
```
---
## API Response
All message types return the same response structure:
```json
{
"data": {
"record_type": "message",
"direction": "outbound",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"type": "WHATSAPP",
"from": {"phone_number": "+15551234567"},
"to": [{"phone_number": "+15557654321", "status": "queued"}],
"webhook_url": "https://example.com/webhooks"
}
}
```
---
## Validation Rules
| Constraint | Limit |
|-----------|-------|
| Text body | 1–4096 bytes |
| Media caption | Max 1024 bytes |
| Interactive header text | 1–60 characters |
| Contacts per message | 1–257 |
| Location latitude | -90 to 90 (string) |
| Location longitude | -180 to 180 (string) |
| Media per message | Exactly 1 |
| Media source | Exactly one of `link` or `id` |
---
## Error Handling
Common WhatsApp errors return error code `40008`. This is a catch-all code covering template issues (pending, rejected, paused, disabled) and delivery failures. For general API errors, see the [Error Codes Reference](/docs/development/api-fundamentals/api-errors).
Template, text, media, and interactive messages all use the same endpoint. The `type` field inside `whatsapp_message` determines which content object is required.
---
## Next Steps
Send your first WhatsApp message end-to-end
Set up your WhatsApp Business Account and verify your number
Handle inbound messages and delivery status callbacks
---
### Manage Templates
> Source: https://developers.telnyx.com/docs/messaging/whatsapp/manage-templates.md
# Manage WhatsApp Message Templates
WhatsApp requires pre-approved message templates for business-initiated conversations. Use the Telnyx Management API to create, review, and manage templates programmatically.
For template best practices and approval tips, see the [Approval Tips](/docs/messaging/whatsapp/manage-templates#approval-tips) guide.
## Template Lifecycle
```mermaid
graph LR
A[Create] --> B[PENDING]
B --> C[APPROVED]
B --> D[REJECTED]
C --> E[PAUSED]
E --> C
C --> F[DISABLED]
```
Templates must be approved by Meta before they can be used. Review typically takes 24-48 hours.
## List Templates
Retrieve all templates for your WhatsApp Business Account.
```bash cURL
curl -X GET "https://api.telnyx.com/v2/whatsapp/message_templates" \
-H "Authorization: Bearer YOUR_API_KEY"
```
```python Python
from telnyx import Telnyx
client = Telnyx(api_key="YOUR_API_KEY")
templates = client.whatsapp.templates.list()
for template in templates.data:
print(f"{template.name} ({template.category}) - {template.status}")
```
```javascript Node.js
import Telnyx from 'telnyx';
const client = new Telnyx({apiKey: 'YOUR_API_KEY'});
const { data } = await client.whatsapp.templates.list();
data.forEach(t => console.log(`${t.name} (${t.category}) - ${t.status}`));
```
### Filter by Status
```bash
# List only approved templates
curl -X GET "https://api.telnyx.com/v2/whatsapp/message_templates?filter[status]=APPROVED" \
-H "Authorization: Bearer YOUR_API_KEY"
# Search by name
curl -X GET "https://api.telnyx.com/v2/whatsapp/message_templates?filter[search]=welcome" \
-H "Authorization: Bearer YOUR_API_KEY"
```
### Response
```json
{
"data": [
{
"id": "019cd44b-3a1c-781b-956e-bd33e9fd2ac6",
"record_type": "whatsapp_message_template",
"name": "welcome_message",
"category": "MARKETING",
"language": "en_US",
"status": "APPROVED",
"components": [
{
"type": "BODY",
"text": "Hello {{1}}! Welcome to our WhatsApp updates.",
"example": {
"body_text": [["John"]]
}
}
],
"quality_rating": "HIGH",
"created_at": "2026-03-15T12:00:00.000Z",
"updated_at": "2026-03-15T14:30:00.000Z"
}
],
"meta": {
"page_number": 1,
"page_size": 20,
"total_pages": 1,
"total_results": 1
}
}
```
## Create a Template
Submit a new template for Meta review.
Set a display name for the phone number before submitting templates. Templates submitted from numbers without an approved display name are rejected by Meta.
Always include sample values in the `example` field when your template contains parameters (`\{\{1\}\}`, `\{\{2\}\}`). Templates without examples are typically rejected.
### Authentication Template
Authentication templates include an OTP code with a copy-code button.
```bash cURL
curl -X POST "https://api.telnyx.com/v2/whatsapp/message_templates" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "login_verification",
"category": "AUTHENTICATION",
"language": "en_US",
"components": [
{
"type": "BODY",
"text": "Your verification code is {{1}}. Do not share this code.",
"example": {
"body_text": [["123456"]]
}
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "OTP",
"otp_type": "COPY_CODE",
"text": "Copy Code"
}
]
}
]
}'
```
```python Python
from telnyx import Telnyx
client = Telnyx(api_key="YOUR_API_KEY")
template = client.whatsapp.templates.create(
name="login_verification",
category="AUTHENTICATION",
language="en_US",
waba_id="YOUR_WABA_ID",
components=[
{
"type": "BODY",
"text": "Your verification code is {{1}}. Do not share this code.",
"example": {"body_text": [["123456"]]}
},
{
"type": "BUTTONS",
"buttons": [{"type": "OTP", "otp_type": "COPY_CODE", "text": "Copy Code"}]
}
]
)
print(f"Template ID: {template.id}, Status: {template.status}")
```
```javascript Node.js
import Telnyx from 'telnyx';
const client = new Telnyx({apiKey: 'YOUR_API_KEY'});
const template = await client.whatsapp.templates.create({
name: 'login_verification',
category: 'AUTHENTICATION',
language: 'en_US',
waba_id: 'YOUR_WABA_ID',
components: [
{
type: 'BODY',
text: 'Your verification code is {{1}}. Do not share this code.',
example: { body_text: [['123456']] }
},
{
type: 'BUTTONS',
buttons: [{ type: 'OTP', otp_type: 'COPY_CODE', text: 'Copy Code' }]
}
]
});
console.log(`Template ID: ${template.data.id}`);
```
### Marketing Template
Marketing templates support rich media headers, body text with parameters, and call-to-action buttons.
```bash
curl -X POST "https://api.telnyx.com/v2/whatsapp/message_templates" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "spring_promotion",
"category": "MARKETING",
"language": "en_US",
"components": [
{
"type": "HEADER",
"format": "TEXT",
"text": "Spring Sale!"
},
{
"type": "BODY",
"text": "Hi {{1}}, enjoy {{2}}% off your next order with code {{3}}. Offer ends soon!",
"example": {
"body_text": [["Sarah", "25", "SPRING25"]]
}
},
{
"type": "FOOTER",
"text": "Reply STOP to opt out"
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "URL",
"text": "Shop Now",
"url": "https://example.com/shop?promo={{1}}",
"example": ["SPRING25"]
}
]
}
]
}'
```
### Utility Template
Utility templates are for transactional updates like order confirmations, shipping notifications, and account alerts.
```bash
curl -X POST "https://api.telnyx.com/v2/whatsapp/message_templates" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "order_shipped",
"category": "UTILITY",
"language": "en_US",
"components": [
{
"type": "BODY",
"text": "Your order {{1}} has shipped! Expected delivery: {{2}}. Track your package here.",
"example": {
"body_text": [["ORD-12345", "March 20, 2026"]]
}
}
]
}'
```
### Media Header Templates
Templates with an `IMAGE`, `VIDEO`, or `DOCUMENT` header require a media handle as the header sample, not a public URL.
Do not put a public image URL in `example.header_handle`. Meta accepts only a media handle produced by uploading the sample file. Passing a URL returns error subcode `2388273` ("Missing Sample Parameter for Title Type"). Upload the sample first, then reference the returned handle.
**Step 1: Upload the sample media**
Upload the sample file to `POST /v2/whatsapp/media/upload` as `multipart/form-data`. Supported types: JPEG and PNG (up to 5 MB), MP4 (up to 16 MB), and PDF (up to 100 MB).
```bash
curl -X POST "https://api.telnyx.com/v2/whatsapp/media/upload" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@/path/to/sample.jpg;type=image/jpeg"
```
The response returns the media handle in `data.id`:
```json
{
"data": {
"record_type": "uploaded_media_handle",
"id": "4:aW1hZ2UuanBn:aW1hZ2UvanBlZw=="
}
}
```
**Step 2: Create the template with the handle**
Pass the value from `data.id` as `example.header_handle` on the `HEADER` component.
```bash
curl -X POST "https://api.telnyx.com/v2/whatsapp/message_templates" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "receipt_with_image",
"category": "UTILITY",
"language": "en_US",
"components": [
{
"type": "HEADER",
"format": "IMAGE",
"example": {
"header_handle": ["4:aW1hZ2UuanBn:aW1hZ2UvanBlZw=="]
}
},
{
"type": "BODY",
"text": "Hi {{1}}, your receipt is attached.",
"example": {"body_text": [["Sarah"]]}
}
]
}'
```
## Get a Template
Retrieve details of a specific template by ID.
```bash
curl -X GET "https://api.telnyx.com/v2/whatsapp/message_templates/{template_id}" \
-H "Authorization: Bearer YOUR_API_KEY"
```
## Update a Template
Edit a template's content. Only templates in `APPROVED` or `REJECTED` status can be updated. Updated templates are re-submitted for Meta review.
Editing an approved template resets its status to `PENDING` while Meta reviews the changes. Your template will be temporarily unavailable for sending.
```bash
curl -X PATCH "https://api.telnyx.com/v2/whatsapp/message_templates/{template_id}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"components": [
{
"type": "BODY",
"text": "Your order {{1}} has shipped! Estimated arrival: {{2}}.",
"example": {
"body_text": [["ORD-12345", "March 22, 2026"]]
}
}
]
}'
```
Edit and resubmit rejected templates rather than creating new ones. Meta enforces a 30-day restriction on reusing template names.
## Delete a Template
Remove a template permanently. This action cannot be undone.
```bash
curl -X DELETE "https://api.telnyx.com/v2/whatsapp/message_templates/{template_id}" \
-H "Authorization: Bearer YOUR_API_KEY"
```
## Template Categories
| Category | Use Case | Requires Opt-in | Pricing Tier |
|----------|----------|-----------------|-------------|
| **AUTHENTICATION** | OTP codes, login verification | No | Lowest |
| **UTILITY** | Order updates, shipping, account alerts | Yes | Medium |
| **MARKETING** | Promotions, newsletters, product launches | Yes | Highest |
Choose the most specific category. Meta may reclassify templates that don't match their declared category, which can affect pricing and delivery.
### Authentication Rules
- Must contain a one-time code or password
- Must include a copy-code or one-tap button
- Cannot include URLs or media (except the OTP button)
- Limited to one code parameter
### Naming Rules
- Lowercase letters, numbers, and underscores only
- Maximum 512 characters
- Must be unique within your WABA for each language
- Avoid words like `test`, `sample`, `demo` — Meta flags these for extra review
### Approval Tips
- **Always include sample values** — Templates with parameters but no `example` field are almost always rejected
- **Set a display name first** — Templates submitted from numbers without an approved display name get rejected
- **Complete your business profile** — Add website, description, and category before submitting. Incomplete profiles increase rejection rates
- **Edit rejected templates, don't recreate** — Meta enforces a 30-day restriction on reusing template names
- **Match category to content** — Meta may reclassify miscategorized templates, affecting pricing and delivery
For detailed best practices and troubleshooting, see our [WhatsApp Message Templates Guide](https://support.telnyx.com/en/articles/13986483) on the support center.
## Multi-Language Templates
Create the same template in multiple languages. Each language variant is reviewed independently.
```bash
# English version
curl -X POST "https://api.telnyx.com/v2/whatsapp/message_templates" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "order_confirmation",
"category": "UTILITY",
"language": "en_US",
"components": [
{
"type": "BODY",
"text": "Order {{1}} confirmed. Delivery by {{2}}.",
"example": {"body_text": [["ORD-789", "March 20"]]}
}
]
}'
# Spanish version
curl -X POST "https://api.telnyx.com/v2/whatsapp/message_templates" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "order_confirmation",
"category": "UTILITY",
"language": "es",
"components": [
{
"type": "BODY",
"text": "Pedido {{1}} confirmado. Entrega estimada: {{2}}.",
"example": {"body_text": [["ORD-789", "20 de marzo"]]}
}
]
}'
```
## Error Handling
| Error Code | Description | Resolution |
|-----------|-------------|------------|
| `40008` | Template operation failed (catch-all) | Check template status and Meta error details |
| `10004` | Missing required parameter | Ensure all required fields are included |
| `10032` | Invalid enumerated value | Check category and language codes |
Common issues:
- **Rejected templates**: Review [Template Best Practices](/docs/messaging/whatsapp/send-messages) for approval tips
- **Duplicate name**: Template names must be unique per language within a WABA
- **Invalid parameters**: Ensure `example` values match parameter count in template body
## Next Steps
- [Send Messages](/docs/messaging/whatsapp/send-messages) — Use approved templates to send messages
- [Approval Tips](/docs/messaging/whatsapp/manage-templates#approval-tips) — Tips for getting templates approved
- [Quickstart](/docs/messaging/whatsapp/quickstart) — End-to-end setup guide
---
### AI Assistant Conversations
> Source: https://developers.telnyx.com/docs/messaging/whatsapp/ai-assistant-conversations.md
# Start WhatsApp Conversations with an AI Assistant
Use [`POST /v2/ai/assistants/{assistant_id}/chat/whatsapp`](/api-reference/assistants/assistant-whatsapp-chat) to reach out to a customer first. The assistant picks one of its approved WhatsApp templates, fills the variables from your `content`, and Telnyx sends it. When the customer replies, the reply lands in the same conversation and the assistant answers free-form within the 24-hour customer service window.
## Prerequisites
- A connected WhatsApp number on your account. See the [quickstart](/docs/messaging/whatsapp/quickstart).
- The assistant set as the AI assistant on that number's messaging profile. Replies are routed through the profile's assistant, so a request with any other assistant is rejected.
- A `whatsapp_template` tool on the assistant with at least one [approved template](/docs/messaging/whatsapp/manage-templates).
- Data retention enabled and PII redaction disabled in the assistant's privacy settings.
## Send the first message
```bash
curl -X POST "https://api.telnyx.com/v2/ai/assistants/{assistant_id}/chat/whatsapp" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "+13125550001",
"to": "+13125550002",
"content": "Send the login verification code 482913 to the customer.",
"conversation_metadata": {"order_id": "A1"}
}'
```
| Field | Required | Description |
|-------|----------|-------------|
| `from` | Yes | Your WhatsApp number in E.164 format. |
| `to` | Yes | The customer's number in E.164 format, or their WhatsApp business-scoped user ID. |
| `content` | Yes | Instruction for the assistant, including the values for the template variables. |
| `conversation_metadata` | No | Values stored on the conversation. Keys starting with `telnyx_` and the `assistant_id` key are reserved. |
The response returns the conversation and the sent template message:
```json
{
"conversation_id": "59f1f39e-180c-4f5d-81e9-393b46ca4077",
"message_id": "4031a0e8-f724-4e22-a086-0f18720ac829"
}
```
Track delivery of `message_id` with your usual [messaging webhooks](/docs/messaging/whatsapp/send-messages), and read the conversation's messages, including the customer's reply, with the [conversations API](/api-reference/conversations/get-conversation-messages).
## Errors
| Status | Meaning |
|--------|---------|
| `400` | Invalid or missing fields, a reserved metadata key, an invalid `from` or `to`, or the assistant is not the one set on the `from` number's messaging profile. |
| `401` | The API key is missing or invalid. |
| `403` | The `from` number is not on your account, or the account is inactive, blocked, unverified or has messaging disabled. |
| `404` | The assistant does not exist. |
| `409` | The `from` number's messaging profile is disabled, or a request with the same `Idempotency-Key` is still being processed. |
| `413` | The request has an `Idempotency-Key` and its body is larger than 256 KB. |
| `422` | The assistant did not return a template, the template it returned is not available on the `from` number's WhatsApp Business Account, its privacy settings do not allow storing conversations, or an `Idempotency-Key` was reused with a different body. |
| `503` | The assistant could not be reached, or the conversation or template send failed. Retry the request. |
---
## Coexistence
### Overview and onboarding
> Source: https://developers.telnyx.com/docs/messaging/whatsapp/coexistence.md
# WhatsApp Coexistence
WhatsApp Coexistence connects a phone number that is already active in the WhatsApp Business app to the Telnyx WhatsApp API. The business can continue its manual conversations in the app while an integration sends automated and template messages through Telnyx.
The Business app and Cloud API share the phone number, but they do not share billing or customer service window state.
## Prerequisites
Before starting:
- Update the WhatsApp Business app to version 2.24.17 or later.
- Keep the phone number registered in the WhatsApp Business app.
- Confirm that the person completing signup has administrator access to the Meta business portfolio that owns the number.
- Use the coexistence option in Embedded Signup. The standard signup flow attempts to register the number for Cloud API-only use.
Do not deregister the phone number from the WhatsApp Business app. Coexistence requires the existing Business app registration.
## Onboard the number
The steps below describe direct customer onboarding in the Telnyx Portal. Tech Providers can onboard coexistence numbers through a [hosted signup](/docs/messaging/whatsapp/embedded-signup/tech-provider#step-3-choose-your-integration-path) created with `signup_type` set to `coexistence`, or through the custom integration with Meta's coexistence configuration.
In the [Telnyx Portal](https://portal.telnyx.com), open **Messaging Suite** → **WhatsApp Messaging**, then select **Add Account** on the **Business Accounts** tab or **Connect WhatsApp Business** on the getting-started page. In the dialog, tick **Enable WhatsApp Business App coexistence** before starting Embedded Signup. **Add Number** always starts the standard signup flow and does not support coexistence.
Sign in to Meta, choose the option to connect an existing WhatsApp Business app, and enter the phone number that is already active in the app.
Meta sends a message from the official Facebook Business Account to the WhatsApp Business app. Open that message, select **Connect**, select **Connect to the Business Platform**, and confirm the connection.
Choose **Share chats** to synchronize up to six months of individual chat history, or choose **Don't share chats** to continue without history. Copy the verification code shown in the WhatsApp Business app and paste it into the Meta signup window.
Finish the remaining Meta steps. The number stays registered in the Business app and is not registered again through Cloud API.
After Embedded Signup connects the WABA to Telnyx, enable webhook delivery and configure the webhook URL using [Update WABA settings](/api-reference/whatsapp-business-accounts/update-waba-settings). If `webhook_events` is set, include `account_update` to receive coexistence lifecycle events.
Assign the phone number to a messaging profile and configure the profile webhook URL to receive `message.echo` and `message.received` events. Without a messaging profile, `message.echo` events are not delivered.
After onboarding, the number enters `sync_pending`. Telnyx accepts synchronization webhooks from Meta and advances the number through the coexistence lifecycle.
Cloud API sends remain blocked until the number reaches `active`.
List the phone numbers connected to the WhatsApp Business Account and confirm that the number reports `is_on_biz_app: true`.
```bash cURL
curl "https://api.telnyx.com/v2/whatsapp/business_accounts/WABA_ID/phone_numbers" \
-H "Authorization: Bearer $TELNYX_API_KEY"
```
```javascript Node.js
const response = await fetch(
"https://api.telnyx.com/v2/whatsapp/business_accounts/WABA_ID/phone_numbers",
{
headers: {
Authorization: `Bearer ${process.env.TELNYX_API_KEY}`,
},
},
);
const result = await response.json();
for (const phone of result.data) {
console.log(
phone.phone_number,
phone.is_on_biz_app,
phone.coexistence_state,
phone.sync_deadline,
);
}
```
```python Python
import os
import requests
response = requests.get(
"https://api.telnyx.com/v2/whatsapp/business_accounts/WABA_ID/phone_numbers",
headers={"Authorization": f"Bearer {os.environ['TELNYX_API_KEY']}"},
timeout=10,
)
response.raise_for_status()
for phone in response.json()["data"]:
print(
phone["phone_number"],
phone["is_on_biz_app"],
phone["coexistence_state"],
phone["sync_deadline"],
)
```
The phone-number response exposes these coexistence fields:
Use `GET /v2/whatsapp/phone_numbers/{phone_number}` to retrieve one number and inspect its synchronization progress. The phone-number list operations do not populate `sync_progress`.
| Field | Meaning |
| --- | --- |
| `is_on_biz_app` | `true` when the number is connected to both the WhatsApp Business app and Cloud API. |
| `coexistence_state` | Current lifecycle state. Standard Cloud API numbers return `null`. |
| `sync_deadline` | Deadline for initiating the current synchronization cycle. It is `null` when no deadline applies. |
| `sync_progress` | Contact and history synchronization status. `GET /v2/whatsapp/phone_numbers/{phone_number}` populates it while the number is in `syncing`. |
## Lifecycle and send eligibility
| State | Meaning | Cloud API sends |
| --- | --- | --- |
| `pending_onboarding` | Embedded Signup has started but coexistence onboarding is not complete | Blocked |
| `sync_pending` | Onboarding completed and synchronization has not started | Blocked |
| `syncing` | Meta synchronization is in progress | Blocked |
| `sync_complete` | Synchronization completed and activation is being finalized | Blocked |
| `active` | Coexistence is ready | Allowed |
| `history_declined` | The business declined history sharing while onboarding continues | Blocked until activation |
| `sync_deadline_expired` | Required synchronization was not initiated before the deadline | Blocked |
| `offboarded` | Meta offboarded the coexistence connection | Blocked |
| `disconnected` | The business removed Telnyx as its partner | Blocked |
Meta allows synchronization only within 24 hours of completing onboarding in the Meta flow. After that window, Meta rejects the synchronization request and the number cannot reach `active`. Disconnect the number in the WhatsApp Business app and complete coexistence signup again to start a new window.
## Messaging behavior
Messages sent manually from the WhatsApp Business app produce a [`message.echo`](/docs/messaging/whatsapp/coexistence/webhooks#messageecho) webhook. Treat the event as outbound activity.
Coexistence webhooks provide best-effort conversation visibility, not a complete message archive. Unsupported companion clients do not produce all expected webhooks. Do not use this event stream as the only system of record for a conversation.
Business app messages:
- Do not open or extend the Cloud API 24-hour customer service window.
- Do not create a Telnyx billing event.
- Do not trigger inbound opt-out or autoresponse processing.
Cloud API messages continue to follow the standard WhatsApp pricing and customer service window rules. See [Send WhatsApp messages](/docs/messaging/whatsapp/send-messages).
## Throughput
Meta limits each coexistence-enabled phone number to 20 Cloud API messages per second. The limit includes inbound and outbound Cloud API messages of every type. Meta documents this as Cloud API throughput and does not state that messages sent directly from the WhatsApp Business app consume it.
When Cloud API traffic exceeds the limit, Meta returns error `130429` in the send response or a subsequent message status webhook. Telnyx reports the message as `failed` with error code `40011`. Retry with backoff and reduce the sending rate before retrying. See Meta's [Cloud API throughput documentation](https://developers.facebook.com/documentation/business-messaging/whatsapp/throughput).
## Pricing
Messages sent from the WhatsApp Business app and delivered as `message.echo` are free. Telnyx does not create a billing event, and Meta does not charge for the mirrored app message.
Messages sent through Cloud API continue to incur the standard Telnyx and Meta charges that apply to their message category and destination. See [WhatsApp pricing](https://support.telnyx.com/en/articles/13986484-whatsapp-pricing-on-telnyx).
## Disconnect coexistence
The WhatsApp deregistration API cannot deregister a coexistence number. Disconnect it from the WhatsApp Business app:
1. Open **Settings** in the WhatsApp Business app.
2. Select **Account**.
3. Select **Business Platform**.
4. Select **Disconnect**.
Telnyx processes Meta's partner removal event and transitions the number to `disconnected`.
## Limitations
| Capability | Coexistence behavior |
| --- | --- |
| Throughput | Fixed at 20 Cloud API messages per second for a coexistence-enabled number. |
| Individual chats | Supported. Up to six months of recent chat history can be synchronized when the business shares it. |
| Contacts | WhatsApp contacts can be synchronized. |
| Group chats | Continue in the Business app but are not synchronized to Cloud API. |
| Broadcast lists | Existing lists become read-only. New broadcast lists cannot be created in the Business app. |
| Disappearing messages | Not supported while coexistence is enabled. |
| View-once messages | Not supported while coexistence is enabled. |
| Live location | Not supported while coexistence is enabled. |
| Voice and video calls | Continue in the Business app but are not supported through Cloud API. |
| Business and messaging tools | Catalog, orders, status, greetings, away messages, quick replies, and labels remain available in the Business app but are not synchronized to Cloud API. |
| Business profile | Remains available in the Business app but is not synchronized to Cloud API. |
| Channels | Remain available in the Business app but are not synchronized to Cloud API. |
| Companion devices | Existing companion devices are unlinked during onboarding. Supported devices can be linked again afterward. |
| Windows and WearOS companions | Messages sent from these clients might not produce webhooks. |
| Conversation visibility | Best-effort only. Do not use coexistence webhooks as a complete system of record for every Business app conversation. |
## Get support
Include the following identifiers when contacting [Telnyx Support](https://support.telnyx.com):
- Telnyx organization ID
- WhatsApp Business Account ID
- Meta phone number ID
- Phone number in E.164 format
- Telnyx message ID and Meta `wamid`, when the issue involves a message
- Approximate event time and timezone
For lifecycle problems, also include the current coexistence state and the Meta lifecycle event, when available.
If Telnyx Support asks you to open a Meta Direct Support case, use the routing that matches the problem:
| Problem | Question Topic | Request Type |
| --- | --- | --- |
| Coexistence onboarding | `WABiz: Onboarding` or `TechProvider: Onboarding` | `Embedded Signup - Coexistence Onboarding` |
| Synchronization API or webhook | `WABiz: Cloud API` | `Coexistence Data Synchronization APIs and Webhooks` |
See Meta's [onboarding guide for WhatsApp Business app users](https://developers.facebook.com/documentation/business-messaging/whatsapp/embedded-signup/onboarding-business-app-users/) for the current support categories.
## Related documentation
Handle Business app echoes and lifecycle changes.
Review the standard WhatsApp onboarding flow.
Implement coexistence through a custom Tech Provider integration.
---
### Webhooks
> Source: https://developers.telnyx.com/docs/messaging/whatsapp/coexistence/webhooks.md
# WhatsApp Coexistence Webhooks
WhatsApp Coexistence adds customer-facing events for messages sent from the WhatsApp Business app and for changes to the coexistence connection.
Return an HTTP `2xx` response promptly. Verify every webhook using the Telnyx webhook signature before processing it. See [Receiving webhooks](/docs/development/api-fundamentals/webhooks/receiving-webhooks).
## Event summary
| Telnyx event | Source | Purpose |
| --- | --- | --- |
| `message.echo` | Meta `smb_message_echoes` | Mirrors a message sent manually from the WhatsApp Business app. |
| `whatsapp.account.update` | Meta `account_update` | Reports offboarding, reconnection, or partner removal. |
| `message.received` with `body.type: edit` | Meta `messages` | Reports an edit to a user-originated message. |
| `message.received` with `body.type: revoke` | Meta `messages` | Reports a user-originated message deletion. |
Meta also sends `history` and `smb_app_state_sync` synchronization webhooks. Telnyx validates and processes these internally. They are not forwarded to the customer webhook URL.
## `message.echo`
`message.echo` represents a message sent from the WhatsApp Business app. It is outbound and includes `origin: whatsapp_business_app`.
Telnyx delivers `message.echo` to the webhook URL of the messaging profile assigned to the phone number. Assign the number to a messaging profile to receive echoes. The WABA webhook configured in [Update WABA settings](/api-reference/whatsapp-business-accounts/update-waba-settings) does not receive `message.echo`.
If the WABA `webhook_events` also include `messages`, each inbound message is delivered twice: as `whatsapp.messages` to the WABA webhook and as `message.received` to the messaging profile webhook. Remove `messages` from `webhook_events` or process each inbound message once.
```json
{
"data": {
"record_type": "event",
"id": "4f923db9-186f-4c6a-9a14-0b6d46e31d77",
"event_type": "message.echo",
"occurred_at": "2026-09-02T10:15:30.000Z",
"payload": {
"record_type": "message",
"direction": "outbound",
"origin": "whatsapp_business_app",
"id": "c7b87bc9-d220-4b2c-916a-83c11ef9821f",
"type": "WHATSAPP",
"organization_id": "7a7f45eb-6670-45fd-a3e3-5b9e4a4f2103",
"messaging_profile_id": "4001799e-1457-4c7f-a96a-735ed44c7de7",
"from": {
"phone_number": "+13125551234",
"carrier": "",
"line_type": ""
},
"to": "+18655550001",
"body": {
"from": "+18655550001",
"id": "c7b87bc9-d220-4b2c-916a-83c11ef9821f",
"foreign_id": "wamid.HBgLMTg2NTU1NTAwMDEFQIAERgSODQ2",
"timestamp": "1788344130",
"type": "text",
"text": {
"body": "Sent from the WhatsApp Business app"
}
},
"cost": {
"amount": null,
"currency": null
},
"errors": []
}
}
}
```
The payload has the following coexistence-specific semantics:
| Field | Value | Meaning |
| --- | --- | --- |
| `data.event_type` | `message.echo` | Distinguishes the echo from `message.received`. |
| `data.payload.direction` | `outbound` | The business sent the message to the recipient. |
| `data.payload.origin` | `whatsapp_business_app` | The Business app, not the Telnyx API, originated the message. |
| `data.payload.from.phone_number` | Business number | The coexistence-enabled phone number. |
| `data.payload.to` | Recipient | The WhatsApp user who received the message. |
| `data.payload.body.foreign_id` | Meta `wamid` | Correlates the message with Meta events. |
`message.echo` is non-billable and does not open or extend the Cloud API customer service window. Avoid feeding it into inbound autoresponse, opt-out, or bot workflows.
### Handle message echoes
```python Python
def handle_telnyx_webhook(event):
data = event["data"]
if data["event_type"] != "message.echo":
return
payload = data["payload"]
assert payload["direction"] == "outbound"
assert payload["origin"] == "whatsapp_business_app"
store_outbound_business_app_message(payload)
```
```javascript Node.js
function handleTelnyxWebhook(event) {
const data = event.data;
if (data.event_type !== "message.echo") return;
const payload = data.payload;
if (
payload.direction !== "outbound" ||
payload.origin !== "whatsapp_business_app"
) {
throw new Error("Invalid WhatsApp Business app echo");
}
storeOutboundBusinessAppMessage(payload);
}
```
## `whatsapp.account.update`
Coexistence lifecycle changes arrive as `whatsapp.account.update`. Inspect `data.payload.event` to determine the transition.
```json
{
"data": {
"record_type": "event",
"id": "72f74d14-7506-4de7-bd13-62aacf140fa3",
"event_type": "whatsapp.account.update",
"occurred_at": "2026-09-02T10:20:00.000Z",
"payload": {
"record_type": "whatsapp_account",
"waba_id": "106540352242922",
"event": "ACCOUNT_OFFBOARDED"
}
}
}
```
| `data.payload.event` | Meaning | Expected state |
| --- | --- | --- |
| `ACCOUNT_OFFBOARDED` | Meta offboarded the coexistence connection. | `offboarded` |
| `ACCOUNT_RECONNECTED` | The business reconnected after offboarding. A new synchronization cycle begins. | `sync_pending` |
| `PARTNER_REMOVED` | The business disconnected Telnyx from the Business app. | `disconnected` |
Treat lifecycle events as idempotent. Duplicate or delayed Meta events can arrive, and Telnyx fences stale transitions against the current connection lifecycle.
Meta may include `disconnection_info` with a `PARTNER_REMOVED` event. The current Telnyx customer webhook does not expose that object, so integrations must use `data.payload.event` as the lifecycle signal.
## Message edits and revocations
WhatsApp user edits and revocations are delivered through the standard `message.received` event. Inspect `data.payload.body.type` to distinguish them. Telnyx does not emit separate `message.edited` or `message.revoked` event names.
### Edited message
An edit can apply to text or to a media caption. Meta accepts edits for up to 15 minutes after the original message was sent.
```json
{
"data": {
"record_type": "event",
"id": "2ff83f52-fdf7-4235-b64e-326e49786ff3",
"event_type": "message.received",
"occurred_at": "2026-09-02T10:25:00.000Z",
"payload": {
"record_type": "message",
"direction": "inbound",
"id": "6c4cbb69-9c05-4371-9d9b-84a521a2cd20",
"type": "WHATSAPP",
"organization_id": "7a7f45eb-6670-45fd-a3e3-5b9e4a4f2103",
"messaging_profile_id": "4001799e-1457-4c7f-a96a-735ed44c7de7",
"from": {
"phone_number": "+18655550001",
"carrier": "",
"line_type": ""
},
"to": "+13125551234",
"body": {
"from": "+18655550001",
"id": "6c4cbb69-9c05-4371-9d9b-84a521a2cd20",
"foreign_id": "wamid.EDIT_EVENT_ID",
"timestamp": "1788344700",
"type": "edit",
"edit": {
"original_message_id": "fefc7543-0816-4652-a70a-22af8fd935ec",
"message": {
"type": "text",
"text": {
"body": "Updated message text"
}
}
}
},
"cost": {
"amount": null,
"currency": null
},
"received_at": "2026-09-02T10:24:59.000Z",
"errors": []
}
}
}
```
### Revoked message
A user can revoke a message for up to two days after it was sent.
```json
{
"data": {
"record_type": "event",
"id": "cf2a2cf3-277a-472c-a281-57e2d33c2547",
"event_type": "message.received",
"occurred_at": "2026-09-02T10:30:00.000Z",
"payload": {
"record_type": "message",
"direction": "inbound",
"id": "6c7681d6-e590-4a9f-9e4e-2f41435194cf",
"type": "WHATSAPP",
"organization_id": "7a7f45eb-6670-45fd-a3e3-5b9e4a4f2103",
"messaging_profile_id": "4001799e-1457-4c7f-a96a-735ed44c7de7",
"from": {
"phone_number": "+18655550001",
"carrier": "",
"line_type": ""
},
"to": "+13125551234",
"body": {
"from": "+18655550001",
"id": "6c7681d6-e590-4a9f-9e4e-2f41435194cf",
"foreign_id": "wamid.REVOKE_EVENT_ID",
"timestamp": "1788345000",
"type": "revoke",
"revoke": {
"original_message_id": "fefc7543-0816-4652-a70a-22af8fd935ec"
}
},
"cost": {
"amount": null,
"currency": null
},
"received_at": "2026-09-02T10:29:59.000Z",
"errors": []
}
}
}
```
When Telnyx has already mapped the original Meta message ID, `original_message_id` is the Telnyx message UUID. If the mapping is unavailable, the field can contain the original Meta `wamid`, so treat it as an opaque string.
## Delivery and retry handling
- Return an HTTP `2xx` response before starting long-running work.
- Deduplicate message echoes using `data.payload.id` or `data.payload.body.foreign_id`.
- Persist lifecycle processing by WABA ID and event type.
- Do not assume that Meta events arrive in chronological order.
- Keep unknown fields and enum values when storing the payload so integrations remain compatible with future additions.
## Unsupported or internal events
`history` and `smb_app_state_sync` are synchronization protocol events. Telnyx consumes them internally and does not deliver them as customer webhook events.
Messages sent from unsupported companion clients might not generate `message.echo`. Treat the customer webhook stream as best-effort visibility, not a complete archive of Business app activity.
---
## Calling
### Business Calling
> Source: https://developers.telnyx.com/docs/messaging/whatsapp/business-calling.md
WhatsApp Business Calling lets you receive and place voice calls over WhatsApp using your existing Telnyx phone numbers. Calls route through your configured Voice API, SIP, or TeXML connection — the same infrastructure you already use for programmable voice.
With over 2 billion WhatsApp users worldwide, business calling gives your customers a familiar way to reach you by voice without leaving the app.
## How it works
- **Inbound calls** — When a WhatsApp user calls your business number, the call routes through your existing voice connection (Voice API, SIP, TeXML, or AI agent).
- **Outbound calls** — Place calls to WhatsApp users using a SIP URI with the format `@whatsapp-.sip.telnyx.com`.
- **On-net routing** — All WhatsApp voice calls route through Meta's network. These are not PSTN calls.
- **Concurrent call limit** — Meta allows a maximum of 1,000 concurrent calls per business number.
Outbound WhatsApp calling is not available for numbers in the United States, Canada, Egypt, Vietnam, and Nigeria. This is a Meta platform restriction.
## Prerequisites
Before enabling WhatsApp calling, you need:
- A **Telnyx account** with an [API key](https://portal.telnyx.com).
- A **WhatsApp Business Account** connected via [Embedded Signup](/docs/messaging/whatsapp/embedded-signup).
- A **verified phone number** added to your WABA.
- A **voice connection** (Voice API, SIP, or TeXML) configured in the [Telnyx Portal](https://portal.telnyx.com).
- A **WABA daily messaging limit** of at least **2,000 unique recipients** — Meta requires this minimum before calling APIs can be enabled.
### Connect your WhatsApp Business Account
In the Telnyx Portal, navigate to **Messaging Suite → WhatsApp Messaging** to start the Embedded Signup flow. The page walks you through connecting your WhatsApp Business Account to Telnyx.
Click **Connect WhatsApp Business** to sign in with your Facebook account and authorize Telnyx to manage your WhatsApp Business Account.
## Enable WhatsApp calling
Verify whether calling is already enabled on your WhatsApp phone number.
```
GET https://api.telnyx.com/v2/whatsapp/phone_numbers/{phone_number}/calling_settings
```
```bash cURL
curl -X GET "https://api.telnyx.com/v2/whatsapp/phone_numbers/+15551234567/calling_settings" \
-H "Authorization: Bearer YOUR_API_KEY"
```
```python Python
from telnyx import Telnyx
client = Telnyx(api_key="YOUR_API_KEY")
calling_setting = client.whatsapp.phone_numbers.calling_settings.retrieve(
"+15551234567",
)
print(calling_setting.data)
```
```javascript Node.js
import Telnyx from 'telnyx';
const client = new Telnyx({ apiKey: 'YOUR_API_KEY' });
const callingSetting = await client.whatsapp.phoneNumbers.callingSettings.retrieve('+15551234567');
console.log(callingSetting.data);
```
```go Go
package main
import (
"context"
"fmt"
"github.com/team-telnyx/telnyx-go"
"github.com/team-telnyx/telnyx-go/option"
)
func main() {
client := telnyx.NewClient(
option.WithAPIKey("My API Key"),
)
callingSetting, err := client.Whatsapp.PhoneNumbers.CallingSettings.Get(context.TODO(), "+15551234567")
if err != nil {
panic(err.Error())
}
fmt.Printf("%+v\n", callingSetting.Data)
}
```
```java Java
package com.telnyx.sdk.example;
import com.telnyx.sdk.client.TelnyxClient;
import com.telnyx.sdk.client.okhttp.TelnyxOkHttpClient;
import com.telnyx.sdk.models.whatsapp.phonenumbers.callingsettings.CallingSettingRetrieveParams;
import com.telnyx.sdk.models.whatsapp.phonenumbers.callingsettings.CallingSettingRetrieveResponse;
public final class Main {
private Main() {}
public static void main(String[] args) {
TelnyxClient client = TelnyxOkHttpClient.fromEnv();
CallingSettingRetrieveParams params = CallingSettingRetrieveParams.builder()
.phoneNumber("+15551234567")
.build();
CallingSettingRetrieveResponse callingSetting = client.whatsapp().phoneNumbers().callingSettings().retrieve(params);
}
}
```
```ruby Ruby
require "telnyx"
telnyx = Telnyx::Client.new(api_key: "My API Key")
calling_setting = telnyx.whatsapp.phone_numbers.calling_settings.retrieve("+15551234567")
puts(calling_setting)
```
The response includes an `enabled` field — `false` means calling is not yet active.
```json
{
"data": {
"record_type": "whatsapp_calling_settings",
"phone_number": "+15551234567",
"enabled": false,
"updated_at": "2026-04-28T12:00:00Z"
}
}
```
Enable WhatsApp calling by setting `enabled` to `true`.
```
PATCH https://api.telnyx.com/v2/whatsapp/phone_numbers/{phone_number}/calling_settings
```
```bash cURL
curl -X PATCH "https://api.telnyx.com/v2/whatsapp/phone_numbers/+15551234567/calling_settings" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "enabled": true }'
```
```python Python
from telnyx import Telnyx
client = Telnyx(api_key="YOUR_API_KEY")
calling_setting = client.whatsapp.phone_numbers.calling_settings.update(
phone_number="+15551234567",
enabled=True,
)
print(calling_setting.data)
```
```javascript Node.js
import Telnyx from 'telnyx';
const client = new Telnyx({ apiKey: 'YOUR_API_KEY' });
const callingSetting = await client.whatsapp.phoneNumbers.callingSettings.update('+15551234567', {
enabled: true,
});
console.log(callingSetting.data);
```
```go Go
package main
import (
"context"
"fmt"
"github.com/team-telnyx/telnyx-go"
"github.com/team-telnyx/telnyx-go/option"
)
func main() {
client := telnyx.NewClient(
option.WithAPIKey("My API Key"),
)
callingSetting, err := client.Whatsapp.PhoneNumbers.CallingSettings.Update(
context.TODO(),
"+15551234567",
telnyx.WhatsappPhoneNumberCallingSettingUpdateParams{
Enabled: true,
},
)
if err != nil {
panic(err.Error())
}
fmt.Printf("%+v\n", callingSetting.Data)
}
```
```java Java
package com.telnyx.sdk.example;
import com.telnyx.sdk.client.TelnyxClient;
import com.telnyx.sdk.client.okhttp.TelnyxOkHttpClient;
import com.telnyx.sdk.models.whatsapp.phonenumbers.callingsettings.CallingSettingUpdateParams;
import com.telnyx.sdk.models.whatsapp.phonenumbers.callingsettings.CallingSettingUpdateResponse;
public final class Main {
private Main() {}
public static void main(String[] args) {
TelnyxClient client = TelnyxOkHttpClient.fromEnv();
CallingSettingUpdateParams params = CallingSettingUpdateParams.builder()
.phoneNumber("+15551234567")
.enabled(true)
.build();
CallingSettingUpdateResponse callingSetting = client.whatsapp().phoneNumbers().callingSettings().update(params);
}
}
```
```ruby Ruby
require "telnyx"
telnyx = Telnyx::Client.new(api_key: "My API Key")
calling_setting = telnyx.whatsapp.phone_numbers.calling_settings.update("+15551234567", enabled: true)
puts(calling_setting)
```
Inbound WhatsApp calls route through your voice connection. If you haven't set one up yet, configure one of the following:
- [Voice API](/docs/voice/programmable-voice) — Handle calls programmatically with webhooks.
- [SIP Trunking](/docs/voice/sip-trunking/get-started) — Route calls to your SIP infrastructure.
- [TeXML](/docs/voice/texml/rest-api/applications) — Control calls with XML-based instructions.
## Make outbound calls
The WhatsApp user must grant calling permission before you can place an outbound call to them. Permission can be obtained in three ways:
- **Permission request message** — Send a calling permission request via WhatsApp message (rate-limited to 1 request per 24 hours and 2 requests per 7 days per user).
- **Callback permission** — The user calls your business first, which grants temporary permission for 7 days. This requires "Allow Callbacks" to be enabled in Meta Business Suite.
- **User grants via profile** — The user enables calling from your WhatsApp Business Profile settings, granting permanent permission.
Temporary permission (from callback or accepted request) lasts 7 days. Permission granted via profile settings is permanent until the user revokes it. After 2 consecutive unanswered outbound calls, WhatsApp sends the user a nudge notification. After 4 consecutive unanswered calls, permission is automatically revoked.
To place an outbound WhatsApp call, use the SIP URI format:
```
@whatsapp-.sip.telnyx.com
```
For example, to call `+442071234567` from your Telnyx number `+15551234567`:
```
+442071234567@whatsapp-15551234567.sip.telnyx.com
```
```bash cURL
curl -X POST https://api.telnyx.com/v2/calls \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"connection_id": "YOUR_CONNECTION_ID",
"to": "sip:+442071234567@whatsapp-15551234567.sip.telnyx.com",
"from": "+15551234567"
}'
```
Outbound calling is restricted for numbers in the US, Canada, Egypt, Vietnam, and Nigeria. Attempts from these countries return an error.
## Disable calling
To turn off WhatsApp calling, set `enabled` to `false`:
```bash
curl -X PATCH "https://api.telnyx.com/v2/whatsapp/phone_numbers/+15551234567/calling_settings" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "enabled": false }'
```
## Troubleshooting
Verify that your phone number has an active voice connection configured. Inbound WhatsApp calls route through the same connection used for regular voice calls. Check your connection settings in the [Telnyx Portal](https://portal.telnyx.com).
Outbound WhatsApp calling is not available for numbers registered in the US, Canada, Egypt, Vietnam, or Nigeria. This is a Meta platform restriction and cannot be overridden.
Ensure the phone number is verified and actively registered with your WhatsApp Business Account. Numbers that have not completed Meta's verification process cannot enable calling.
WhatsApp calls route through Meta's network, not the PSTN. Call quality depends on the end user's internet connection. This is consistent with standard WhatsApp voice call behavior.
Your WhatsApp Business Account must have a daily messaging limit of at least 2,000 unique recipients before Meta allows calling APIs to be enabled. Increase your messaging volume and quality rating to raise your limit.
Verify that the phone number is associated with the voice connection you are using in the API call. Also check the SIP URI format — the Telnyx number in the host portion must not include the leading `+` (e.g., `whatsapp-15551234567.sip.telnyx.com`, not `whatsapp-+15551234567.sip.telnyx.com`).
After 2 consecutive unanswered outbound calls, WhatsApp sends the user a nudge notification asking if they want to continue receiving calls. After 4 consecutive unanswered calls, calling permission is automatically revoked and you will need to obtain permission again.
## Next steps
Send text, media, and template messages over WhatsApp.
Connect your WhatsApp Business Account to Telnyx.
Build voice applications with the Telnyx Voice API.
---
## API Reference (WhatsApp)
### Callbacks
- [WhatsApp WABA Added](https://developers.telnyx.com/api-reference/callbacks/whatsapp-waba-added.md): Reports the outcome of a WhatsApp Embedded Signup session. Telnyx sends it to the `webhook_url` set with `PATCH /v2/whatsapp/user_data` when the session succee…
- [WhatsApp Business App Message Echo](https://developers.telnyx.com/api-reference/callbacks/whatsapp-business-app-message-echo.md): Delivers a non-billable mirror of a message sent from the WhatsApp Business app on a coexistence-enabled number. This outbound event does not open or extend th…
- [WhatsApp Account Update](https://developers.telnyx.com/api-reference/callbacks/whatsapp-account-update.md): Reports coexistence lifecycle changes for a WhatsApp Business Account.
### Whatsapp Business Accounts
- [List Whatsapp Business Accounts](https://developers.telnyx.com/api-reference/whatsapp-business-accounts/list-whatsapp-business-accounts.md): Retrieve a list of the WhatsApp Business Accounts (WABAs) connected to your Telnyx account.
- [Delete a Whatsapp Business Account](https://developers.telnyx.com/api-reference/whatsapp-business-accounts/delete-a-whatsapp-business-account.md): Delete a WhatsApp Business Account connection from your Telnyx account.
- [Get a single Whatsapp Business Account](https://developers.telnyx.com/api-reference/whatsapp-business-accounts/get-a-single-whatsapp-business-account.md): Retrieve the details of a specific WhatsApp Business Account.
- [List phone numbers for a WABA](https://developers.telnyx.com/api-reference/whatsapp-business-accounts/list-phone-numbers-for-a-waba.md): List the phone numbers associated with a WhatsApp Business Account.
- [Get WABA settings](https://developers.telnyx.com/api-reference/whatsapp-business-accounts/get-waba-settings.md): Retrieve the settings of a WhatsApp Business Account.
- [Update WABA settings](https://developers.telnyx.com/api-reference/whatsapp-business-accounts/update-waba-settings.md): Update the settings of a WhatsApp Business Account.
### Whatsapp Signup
- [List Whatsapp signup sessions](https://developers.telnyx.com/api-reference/whatsapp-signup/list-whatsapp-signup-sessions.md): List finished WhatsApp Embedded Signup sessions, with status `SUCCESS` or `FAILURE`, newest first. Use the `errors` of a failed session to see why provisioning…
- [Get Whatsapp signup session status](https://developers.telnyx.com/api-reference/whatsapp-signup/get-whatsapp-signup-session-status.md): Retrieve the current state and status of a WhatsApp Embedded Signup session.
- [Create a Whatsapp hosted signup](https://developers.telnyx.com/api-reference/whatsapp-signup/create-a-whatsapp-hosted-signup.md): Create a time-limited onboarding URL on the Telnyx-hosted Embedded Signup page for a Tech Provider's end customer. Set `signup_type` to `coexistence` to launch…
- [List Whatsapp hosted signups](https://developers.telnyx.com/api-reference/whatsapp-signup/list-whatsapp-hosted-signups.md): List hosted signups created by the account, newest first. Expired hosted signups are included.
- [Get a Whatsapp hosted signup](https://developers.telnyx.com/api-reference/whatsapp-signup/get-a-whatsapp-hosted-signup.md): Retrieve a hosted signup and its onboarding URL.
### Whatsapp Phone Numbers
- [Initialize Whatsapp phone number verification](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/initialize-whatsapp-phone-number-verification.md): Start the verification process to register a phone number with a WhatsApp Business Account.
- [List Whatsapp phone numbers](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/list-whatsapp-phone-numbers.md): Retrieve a list of the phone numbers registered for WhatsApp on your account.
- [Retrieve a WhatsApp phone number](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/retrieve-a-whatsapp-phone-number.md): Returns one WhatsApp phone number linked to the authenticated Telnyx account. For a coexistence number in the `syncing` state, the response includes `sync_prog…
- [Delete a Whatsapp phone number](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/delete-a-whatsapp-phone-number.md): Delete a phone number's WhatsApp registration from your account.
- [Get calling settings for a phone number](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/get-calling-settings-for-a-phone-number.md): Retrieve the WhatsApp calling settings for a phone number.
- [Enable or disable Whatsapp calling for a phone number](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/enable-or-disable-whatsapp-calling-for-a-phone-number.md): Enable or disable WhatsApp calling for a phone number and manage its calling configuration.
- [Get Connection ID for a WhatsApp Calling BYON number](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/get-connection-id-for-a-whatsapp-calling-byon-number.md): Retrieve the connection ID currently stored for a WhatsApp Calling BYON (Bring Your Own Number) phone number: the connection that inbound WhatsApp calls to the…
- [Update Connection ID for a WhatsApp Calling BYON number](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/update-connection-id-for-a-whatsapp-calling-byon-number.md): Set or clear the connection ID for a WhatsApp Calling BYON (Bring Your Own Number) phone number: the connection that inbound WhatsApp calls to the number are d…
- [Get phone number business profile](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/get-phone-number-business-profile.md): Retrieve the WhatsApp business profile associated with a phone number.
- [Update phone number business profile](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/update-phone-number-business-profile.md): Update the WhatsApp business profile associated with a phone number.
- [Delete Whatsapp profile photo](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/delete-whatsapp-profile-photo.md): Delete the profile photo of a WhatsApp phone number.
- [Get Whatsapp profile photo](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/get-whatsapp-profile-photo.md): Retrieve the profile photo of a WhatsApp phone number.
- [Upload Whatsapp profile photo](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/upload-whatsapp-profile-photo.md): Upload a new profile photo for a WhatsApp phone number.
- [Resend verification code](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/resend-verification-code.md): Resend the verification code for a phone number that is being registered with WhatsApp.
- [Submit verification code for a phone number](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/submit-verification-code-for-a-phone-number.md): Submit the verification code to complete WhatsApp registration of a phone number.
- [Get conversation window status for a phone number](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/get-conversation-window-status-for-a-phone-number.md): Returns whether the 24-hour conversation window is currently open for a given source/destination pair. If window_active is false, only template messages may be…
- [Get phone number conversational components](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/get-phone-number-conversational-components.md): Retrieve the conversational components configuration for a WhatsApp phone number.
- [Update phone number conversational components](https://developers.telnyx.com/api-reference/whatsapp-phone-numbers/update-phone-number-conversational-components.md): Update the conversational components configuration, such as welcome messages and commands, for a WhatsApp phone number.
### Whatsapp Message Templates
- [List Whatsapp message templates](https://developers.telnyx.com/api-reference/whatsapp-message-templates/list-whatsapp-message-templates.md): Retrieve the WhatsApp message templates for your WhatsApp Business Accounts.
- [Create a Whatsapp message template](https://developers.telnyx.com/api-reference/whatsapp-message-templates/create-a-whatsapp-message-template.md): Create a new WhatsApp message template. Templates must be approved by WhatsApp before they can be used in messages.
- [Delete a Whatsapp message template](https://developers.telnyx.com/api-reference/whatsapp-message-templates/delete-a-whatsapp-message-template.md): Permanently deletes the specified WhatsApp message template from your account.
- [Get a Whatsapp message template by ID](https://developers.telnyx.com/api-reference/whatsapp-message-templates/get-a-whatsapp-message-template-by-id.md): Retrieve a specific WhatsApp message template by its ID.
- [Update a Whatsapp message template](https://developers.telnyx.com/api-reference/whatsapp-message-templates/update-a-whatsapp-message-template.md): Update an existing WhatsApp message template.