# Authentication
Source: https://timelines.ai/docs/authentication
How to authenticate with the TimelinesAI API
# Authentication
The TimelinesAI API uses Bearer token authentication. Every request must include your API token in the `Authorization` header.
## Getting your API token
Go to [app.timelines.ai](https://app.timelines.ai)
Navigate to **Integrations** → **Public API**
Your token is displayed on this page. Click **Copy** to copy it.
## Using your token
Include the token in the `Authorization` header of every request:
```
Authorization: Bearer YOUR_API_TOKEN
```
### Example request
```bash cURL theme={null}
curl -X GET "https://app.timelines.ai/integrations/api/chats" \
-H "Authorization: Bearer 4d2d0239-e28c-4f4a-8a4d-3a3ca40056b8"
```
```javascript JavaScript theme={null}
const response = await fetch('https://app.timelines.ai/integrations/api/chats', {
headers: {
'Authorization': 'Bearer 4d2d0239-e28c-4f4a-8a4d-3a3ca40056b8'
}
});
```
```python Python theme={null}
import requests
response = requests.get(
'https://app.timelines.ai/integrations/api/chats',
headers={'Authorization': 'Bearer 4d2d0239-e28c-4f4a-8a4d-3a3ca40056b8'}
)
```
## Token security
Your API token provides full access to your workspace. Treat it like a password.
### Best practices
Never hardcode tokens in your source code. Use environment variables:
```bash theme={null}
export TIMELINESAI_API_TOKEN="your-token-here"
```
```javascript theme={null}
const token = process.env.TIMELINESAI_API_TOKEN;
```
Add your environment files to `.gitignore`:
```
.env
.env.local
.env.*.local
```
If you suspect your token has been exposed, generate a new one immediately from the API settings page.
Never expose your API token in client-side code (browsers, mobile apps). Always make API calls from your backend server.
## Error responses
### 401 Unauthorized
If your token is missing or invalid:
```json theme={null}
{
"status": "error",
"message": "Invalid or expired token"
}
```
**Common causes:**
* Missing `Authorization` header
* Token copied incorrectly (check for extra spaces)
* Token has been regenerated
### 403 Forbidden
If your token is valid but lacks permission:
```json theme={null}
{
"status": "error",
"message": "Access denied"
}
```
**Common causes:**
* Attempting to access resources from another workspace
* Feature not available on your plan
## Token scope
Your API token has access to:
| Resource | Access |
| ----------------- | ------------ |
| Chats | Read & Write |
| Messages | Read & Write |
| Labels | Read & Write |
| Files | Read & Write |
| WhatsApp Accounts | Read only |
| Webhooks | Read & Write |
All operations are scoped to your workspace. You cannot access data from other TimelinesAI accounts.
# File Attachments
Source: https://timelines.ai/docs/guides/file-attachments
Send images, documents, and other files via WhatsApp
# File Attachments
Send images, documents, audio, and other files as WhatsApp messages.
## Workflow
Sending a file is a two-step process:
Upload to TimelinesAI and get a `file_uid`
Include `file_uid` when sending your message
## Uploading files
### Option 1: Upload from URL
If your file is publicly accessible:
```bash theme={null}
curl -X POST "https://app.timelines.ai/integrations/api/files" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/brochure.pdf",
"filename": "product-brochure.pdf"
}'
```
Response:
```json theme={null}
{
"status": "ok",
"data": {
"uid": "90d353e6-44c1-48ff-b15b-69b7721e5450",
"filename": "product-brochure.pdf",
"mime_type": "application/pdf",
"size": 245678
}
}
```
### Option 2: Upload file directly
For local files, use multipart form data:
```bash theme={null}
curl -X POST "https://app.timelines.ai/integrations/api/files_upload" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@/path/to/document.pdf" \
-F "filename=document.pdf"
```
## Sending files
Once uploaded, use the `file_uid` to send:
### File with text
```bash theme={null}
curl -X POST "https://app.timelines.ai/integrations/api/messages" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"phone": "+14155551234",
"text": "Here is the document you requested!",
"file_uid": "90d353e6-44c1-48ff-b15b-69b7721e5450"
}'
```
## Supported file types
| Category | Formats | Max Size |
| --------- | ------------------------- | -------- |
| Images | JPG, PNG, GIF, WebP | 16 MB |
| Documents | PDF, DOC, DOCX, XLS, XLSX | 100 MB |
| Audio | MP3, OGG, OGA, AAC | 16 MB |
| Video | MP4, 3GP | 16 MB |
## Managing uploaded files
### List files
```bash theme={null}
curl -X GET "https://app.timelines.ai/integrations/api/files" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### Get file details
```bash theme={null}
curl -X GET "https://app.timelines.ai/integrations/api/files/90d353e6-44c1-48ff-b15b-69b7721e5450" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### Delete file
```bash theme={null}
curl -X DELETE "https://app.timelines.ai/integrations/api/files/90d353e6-44c1-48ff-b15b-69b7721e5450" \
-H "Authorization: Bearer YOUR_TOKEN"
```
## Next steps
More messaging options
Get notified of incoming files
# Managing Chats
Source: https://timelines.ai/docs/guides/managing-chats
Organize, filter, and manage your WhatsApp conversations
# Managing Chats
Learn how to list, filter, assign, and organize your WhatsApp chats programmatically.
## Listing chats
Get all chats in your workspace:
```bash theme={null}
curl -X GET "https://app.timelines.ai/integrations/api/chats" \
-H "Authorization: Bearer YOUR_TOKEN"
```
Response:
```json theme={null}
{
"status": "ok",
"data": {
"has_more_pages": true,
"chats": [
{
"id": 123456,
"name": "John Doe",
"phone": "+14155551234",
"is_group": false,
"whatsapp_account_id": "15559876543@s.whatsapp.net",
"responsible": "agent@company.com",
"closed": false,
"read": true
}
]
}
}
```
## Filtering chats
Combine filters to find specific chats. Multiple filters use AND logic.
### By label
Find chats with specific labels:
```bash theme={null}
# Chats with "vip" OR "enterprise" label
curl -X GET "https://app.timelines.ai/integrations/api/chats?label=vip,enterprise" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### By assigned agent
```bash theme={null}
# Chats assigned to specific team member
curl -X GET "https://app.timelines.ai/integrations/api/chats?responsible=agent@company.com" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### By read/unread status
```bash theme={null}
# Unread chats only
curl -X GET "https://app.timelines.ai/integrations/api/chats?read=false" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### By chat type
```bash theme={null}
# Group chats only
curl -X GET "https://app.timelines.ai/integrations/api/chats?group=true" \
-H "Authorization: Bearer YOUR_TOKEN"
# Direct chats only
curl -X GET "https://app.timelines.ai/integrations/api/chats?group=false" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### By name
```bash theme={null}
# Chats containing "acme" in the name
curl -X GET "https://app.timelines.ai/integrations/api/chats?name=acme" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### By date range
```bash theme={null}
# Chats created after January 1, 2024
curl -X GET "https://app.timelines.ai/integrations/api/chats?created_after=2024-01-01T00:00:00Z" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### Combined filters
```bash theme={null}
# Unread VIP chats assigned to a specific agent
curl -X GET "https://app.timelines.ai/integrations/api/chats?read=false&label=vip&responsible=agent@company.com" \
-H "Authorization: Bearer YOUR_TOKEN"
```
## Pagination
Results are paginated with 50 chats per page.
```bash theme={null}
# Get page 2
curl -X GET "https://app.timelines.ai/integrations/api/chats?page=2" \
-H "Authorization: Bearer YOUR_TOKEN"
```
Check `has_more_pages` in the response to know if more pages exist.
## Getting chat details
Retrieve full details for a specific chat:
```bash theme={null}
curl -X GET "https://app.timelines.ai/integrations/api/chats/123456" \
-H "Authorization: Bearer YOUR_TOKEN"
```
## Updating chats
### Assign to team member
```bash theme={null}
curl -X PATCH "https://app.timelines.ai/integrations/api/chats/123456" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "responsible": "agent@company.com" }'
```
### Unassign
Set `responsible` to an empty string:
```bash theme={null}
curl -X PATCH "https://app.timelines.ai/integrations/api/chats/123456" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "responsible": "" }'
```
### Close/reopen chat
```bash theme={null}
# Close chat
curl -X PATCH "https://app.timelines.ai/integrations/api/chats/123456" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "closed": true }'
# Reopen chat
curl -X PATCH "https://app.timelines.ai/integrations/api/chats/123456" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "closed": false }'
```
### Mark as read/unread
```bash theme={null}
# Mark as read
curl -X PATCH "https://app.timelines.ai/integrations/api/chats/123456" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "read": true }'
```
### Rename chat
```bash theme={null}
curl -X PATCH "https://app.timelines.ai/integrations/api/chats/123456" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "John Doe - VIP Customer" }'
```
Chat names must be unique within your workspace.
### Enable/disable AI auto-response
```bash theme={null}
curl -X PATCH "https://app.timelines.ai/integrations/api/chats/123456" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "chatgpt_autoresponse_enabled": true }'
```
## Working with labels
### Get chat labels
```bash theme={null}
curl -X GET "https://app.timelines.ai/integrations/api/chats/123456/labels" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### Add labels
Add labels without removing existing ones:
```bash theme={null}
curl -X PUT "https://app.timelines.ai/integrations/api/chats/123456/labels" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "labels": ["follow-up", "high-priority"] }'
```
### Replace labels
Replace all labels with a new set:
```bash theme={null}
curl -X POST "https://app.timelines.ai/integrations/api/chats/123456/labels" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "labels": ["vip", "enterprise"] }'
```
Labels are created automatically if they don't exist.
## Adding notes
Add internal notes visible only to your team:
```bash theme={null}
curl -X POST "https://app.timelines.ai/integrations/api/chats/123456/notes" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "text": "Customer prefers email for follow-ups. Next call scheduled for Monday." }'
```
Notes are not sent to WhatsApp—they're internal annotations for your team.
## Common workflows
### Route new chats to agents
```python theme={null}
import requests
def route_chat(chat_id, labels):
"""Assign chat to agent based on labels"""
# Get chat labels
response = requests.get(
f'https://app.timelines.ai/integrations/api/chats/{chat_id}/labels',
headers={'Authorization': 'Bearer YOUR_TOKEN'}
)
chat_labels = response.json()['data']['labels']
# Route based on labels
if 'enterprise' in chat_labels:
agent = 'senior@company.com'
elif 'support' in chat_labels:
agent = 'support@company.com'
else:
agent = 'sales@company.com'
# Assign chat
requests.patch(
f'https://app.timelines.ai/integrations/api/chats/{chat_id}',
headers={
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json'
},
json={'responsible': agent}
)
```
### Close inactive chats
```python theme={null}
from datetime import datetime, timedelta
def close_inactive_chats(days_inactive=30):
"""Close chats with no activity in X days"""
cutoff = datetime.utcnow() - timedelta(days=days_inactive)
response = requests.get(
f'https://app.timelines.ai/integrations/api/chats?closed=false',
headers={'Authorization': 'Bearer YOUR_TOKEN'}
)
for chat in response.json()['data']['chats']:
# Check last message date (you'd need to fetch messages)
# If inactive, close
requests.patch(
f'https://app.timelines.ai/integrations/api/chats/{chat["id"]}',
headers={
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json'
},
json={'closed': True}
)
```
## Next steps
Send messages to your chats
Get notified of new messages
# Sending Messages
Source: https://timelines.ai/docs/guides/sending-messages
Learn how to send WhatsApp messages via the API
# Sending Messages
TimelinesAI provides multiple ways to send WhatsApp messages depending on your use case.
## Sending methods
| Method | Best for | Endpoint |
| --------------- | --------------------------------- | -------------------------------- |
| By phone number | New contacts, phone from your CRM | `POST /messages` |
| By chat ID | Existing conversations | `POST /chats/{chat_id}/messages` |
| By chat name | When you know the contact name | `POST /messages/to_chat_name` |
| By JID | Groups, technical integrations | `POST /messages/to_jid` |
## Send to phone number
The most common method. Works even if you haven't messaged this number before.
```bash cURL theme={null}
curl -X POST "https://app.timelines.ai/integrations/api/messages" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"phone": "+14155551234",
"text": "Hello! Thanks for your interest."
}'
```
```javascript JavaScript theme={null}
const response = await fetch('https://app.timelines.ai/integrations/api/messages', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
phone: '+14155551234',
text: 'Hello! Thanks for your interest.'
})
});
```
```python Python theme={null}
import requests
response = requests.post(
'https://app.timelines.ai/integrations/api/messages',
headers={
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json'
},
json={
'phone': '+14155551234',
'text': 'Hello! Thanks for your interest.'
}
)
```
### Phone number format
Use international format with country code:
* ✅ `+14155551234`
* ✅ `14155551234`
* ❌ `(415) 555-1234`
* ❌ `415-555-1234`
## Send to existing chat
When you have a `chat_id` (from listing chats or webhook events):
```bash cURL theme={null}
curl -X POST "https://app.timelines.ai/integrations/api/chats/123456/messages" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"text": "Following up on your question..."
}'
```
```javascript JavaScript theme={null}
const chatId = 123456;
const response = await fetch(
`https://app.timelines.ai/integrations/api/chats/${chatId}/messages`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
text: 'Following up on your question...'
})
}
);
```
## Choosing a WhatsApp account
If you have multiple WhatsApp numbers connected, specify which one to send from:
```json theme={null}
{
"phone": "+14155551234",
"text": "Hello!",
"whatsapp_account_id": "15551234567@s.whatsapp.net"
}
```
If you don't specify `whatsapp_account_id`, the most recently connected account is used.
## Message formatting
### Line breaks
Use `\n` for line breaks:
```json theme={null}
{
"text": "Hello!\n\nHere's your summary:\n- Item 1\n- Item 2"
}
```
Renders as:
> Hello!
>
> Here's your summary:
>
> * Item 1
> * Item 2
### Emojis
Emojis are fully supported:
```json theme={null}
{
"text": "Thanks for reaching out! 🎉 We'll get back to you soon. 👍"
}
```
## Auto-apply labels
Automatically label chats when sending messages:
```json theme={null}
{
"phone": "+14155551234",
"text": "Welcome to our service!",
"label": "onboarding"
}
```
This is useful for tracking outreach campaigns or categorizing contacts as you message them.
## Tracking delivery
### Get message status
After sending, use the `message_uid` to track delivery:
```bash theme={null}
curl -X GET "https://app.timelines.ai/integrations/api/messages/{message_uid}" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### Status values
| Status | Meaning |
| ----------- | --------------------------------------- |
| `queued` | Message is waiting to be sent |
| `sent` | Message sent to WhatsApp servers |
| `delivered` | Message delivered to recipient's device |
| `read` | Recipient opened the message |
| `failed` | Message could not be sent |
### Status history
Get the complete timeline:
```bash theme={null}
curl -X GET "https://app.timelines.ai/integrations/api/messages/{message_uid}/status_history" \
-H "Authorization: Bearer YOUR_TOKEN"
```
Response:
```json theme={null}
{
"status": "ok",
"data": [
{ "status": "queued", "timestamp": "2024-01-15T10:30:00Z" },
{ "status": "sent", "timestamp": "2024-01-15T10:30:02Z" },
{ "status": "delivered", "timestamp": "2024-01-15T10:30:05Z" },
{ "status": "read", "timestamp": "2024-01-15T10:32:15Z" }
]
}
```
## Rate limits
Messages are sent with a \~2 second delay between each to avoid WhatsApp spam detection.
Sending too many messages too quickly may result in WhatsApp flagging your number. Always respect rate limits.
If you have high-volume needs, [contact support](mailto:support@timelines.ai) to discuss Business plan options.
## Credits usage
| Message type | Credits |
| ----------------- | ------- |
| Text only | 1 |
| Text + attachment | 2 |
| Failed (refunded) | 0 |
## Common errors
```json theme={null}
{ "status": "error", "message": "Invalid phone number format" }
```
**Solution:** Use international format with country code (e.g., `+14155551234`)
```json theme={null}
{ "status": "error", "message": "No connected WhatsApp account" }
```
**Solution:** Ensure at least one WhatsApp account is connected in your workspace
```json theme={null}
{ "status": "error", "message": "Message quota exceeded" }
```
**Solution:** Upgrade your plan or wait for quota to reset
## Next steps
Learn to send images and files
Get notified when messages arrive
# Webhooks
Source: https://timelines.ai/docs/guides/webhooks
Receive real-time notifications for WhatsApp events
# Webhooks
Webhooks allow your application to receive real-time notifications when events occur in your TimelinesAI workspace—like new messages, sent messages, or chat updates.
## How webhooks work
Tell TimelinesAI which events you care about and where to send them
A new message arrives, a message is sent, etc.
Your endpoint receives the event data in real-time
Respond with 2xx status within 5 seconds
## Available events
| Event | Trigger |
| ---------------------- | ------------------------- |
| `message:received:new` | New incoming message |
| `message:sent:new` | Message sent successfully |
| `chat:created` | New chat created |
See the [full event list](https://timelinesai.mintlify.app/webhook-reference/overview) for all available events.
## Creating a webhook
```bash theme={null}
curl -X POST "https://app.timelines.ai/integrations/api/webhooks" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"event_type": "message:received:new",
"url": "https://your-app.com/webhooks/timelines",
"enabled": true
}'
```
Response:
```json theme={null}
{
"status": "ok",
"data": {
"id": 7654321,
"event_type": "message:received:new",
"url": "https://your-app.com/webhooks/timelines",
"enabled": true,
"errors_counter": 0
}
}
```
## Webhook endpoint requirements
Your endpoint must:
Be publicly accessible (no localhost)
Use HTTPS
Respond with 2xx status within 5 seconds
Accept POST requests with JSON body
## Handling webhook events
### Example: Node.js / Express
```javascript theme={null}
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhooks/timelines', (req, res) => {
const event = req.body;
console.log('Received event:', event.event_type);
switch (event.event_type) {
case 'message:received:new':
handleNewMessage(event.data);
break;
case 'message:sent:new':
handleSentMessage(event.data);
break;
default:
console.log('Unknown event type');
}
// Always respond quickly with 200
res.status(200).send('OK');
});
function handleNewMessage(data) {
console.log(`New message from ${data.chat_name}: ${data.text}`);
// Your logic here: save to database, notify team, auto-reply, etc.
}
function handleSentMessage(data) {
console.log(`Message sent: ${data.message_uid}`);
}
app.listen(3000);
```
### Example: Python / Flask
```python theme={null}
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhooks/timelines', methods=['POST'])
def handle_webhook():
event = request.json
print(f"Received event: {event['event_type']}")
if event['event_type'] == 'message:received:new':
handle_new_message(event['data'])
elif event['event_type'] == 'message:sent:new':
handle_sent_message(event['data'])
# Always respond quickly
return jsonify({'status': 'ok'}), 200
def handle_new_message(data):
print(f"New message from {data['chat_name']}: {data.get('text', '')}")
# Your logic here
def handle_sent_message(data):
print(f"Message sent: {data['message_uid']}")
if __name__ == '__main__':
app.run(port=3000)
```
## Managing webhooks
### List all webhooks
```bash theme={null}
curl -X GET "https://app.timelines.ai/integrations/api/webhooks" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### Get webhook details
```bash theme={null}
curl -X GET "https://app.timelines.ai/integrations/api/webhooks/7654321" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### Update webhook
```bash theme={null}
# Disable webhook
curl -X PUT "https://app.timelines.ai/integrations/api/webhooks/7654321" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "enabled": false }'
# Change URL
curl -X PUT "https://app.timelines.ai/integrations/api/webhooks/7654321" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "url": "https://new-endpoint.com/webhooks" }'
```
### Delete webhook
```bash theme={null}
curl -X DELETE "https://app.timelines.ai/integrations/api/webhooks/7654321" \
-H "Authorization: Bearer YOUR_TOKEN"
```
## Error handling
### Delivery failures
If your endpoint returns a non-2xx status or times out:
1. TimelinesAI retries the delivery **2 additional times** (3 attempts total)
2. If all attempts fail, the `errors_counter` increments
3. After several failures, TimelinesAI emails the workspace owner
4. Webhooks are **not** automatically disabled — fix your endpoint
Monitor your `errors_counter` and fix issues promptly to avoid missing events.
### Check error count
```bash theme={null}
curl -X GET "https://app.timelines.ai/integrations/api/webhooks/7654321" \
-H "Authorization: Bearer YOUR_TOKEN"
```
Look for `errors_counter` in the response.
## Best practices
Process events asynchronously. Return 200 immediately, then handle the event in the background.
```javascript theme={null}
app.post('/webhooks', (req, res) => {
// Respond immediately
res.status(200).send('OK');
// Process async
processEventAsync(req.body);
});
```
Webhooks may be delivered more than once. Use `message_uid` or event IDs to deduplicate.
```javascript theme={null}
const processedEvents = new Set();
function handleEvent(event) {
if (processedEvents.has(event.message_uid)) {
return; // Already processed
}
processedEvents.add(event.message_uid);
// Process event...
}
```
For high volume, push events to a message queue (Redis, RabbitMQ, SQS) and process separately.
Log incoming webhooks for debugging:
```javascript theme={null}
app.post('/webhooks', (req, res) => {
console.log(JSON.stringify(req.body, null, 2));
// ...
});
```
## Testing webhooks locally
Use a tunnel service to expose your local server:
### ngrok
```bash theme={null}
# Start your local server
npm start # Running on port 3000
# In another terminal, create tunnel
ngrok http 3000
# Use the ngrok URL for your webhook
# https://abc123.ngrok.io/webhooks/timelines
```
### localtunnel
```bash theme={null}
npx localtunnel --port 3000
```
Remember to update your webhook URL to production when deploying.
## Example: Auto-reply bot
```javascript theme={null}
const express = require('express');
const app = express();
app.use(express.json());
const TIMELINES_TOKEN = process.env.TIMELINES_TOKEN;
app.post('/webhooks/timelines', async (req, res) => {
res.status(200).send('OK'); // Respond immediately
const event = req.body;
if (event.event_type !== 'message:received:new') return;
const { chat_id, text } = event.data;
// Simple keyword auto-reply
let reply = null;
if (text?.toLowerCase().includes('hours')) {
reply = 'Our business hours are Monday-Friday, 9am-5pm EST.';
} else if (text?.toLowerCase().includes('pricing')) {
reply = 'Please visit https://timelines.ai/pricing for our plans.';
}
if (reply) {
await fetch(`https://app.timelines.ai/integrations/api/chats/${chat_id}/messages`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${TIMELINES_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ text: reply })
});
}
});
app.listen(3000);
```
## Next steps
Learn to send replies
Organize incoming conversations
# Introduction
Source: https://timelines.ai/docs/introduction
Welcome to the TimelinesAI API documentation
# TimelinesAI API
The TimelinesAI API enables you to programmatically manage WhatsApp communications for your business. Build integrations, automate workflows, and connect WhatsApp to your existing systems.
Get up and running in 5 minutes
Set up your API token
Learn how to send WhatsApp messages
Receive real-time notifications
## Our APIs
TimelinesAI provides two complementary APIs and their webhook systems, each designed for different audiences and use cases.
### Public API
The Public API is your primary interface for managing WhatsApp communications within a single workspace. Use it to send and receive messages, manage chats, upload files, organize conversations with labels, and subscribe to real-time webhook events.
**Best for:** Businesses integrating WhatsApp into their own workflows — CRMs, support systems, marketing tools, and custom applications.
Explore all messaging and chat management endpoints
Real-time events for messages, chats, and account status
### Partner API
The Partner API is designed for platform integrators, resellers, and SaaS providers who manage multiple TimelinesAI workspaces on behalf of their customers. It lets you automate workspace provisioning, user management, WhatsApp onboarding, and API token lifecycle.
**Best for:** Partners building white-label solutions, managed services, or multi-tenant platforms on top of TimelinesAI.
Workspace, user, and account management endpoints
Quota alerts, account events, and token change notifications
## What you can do
Send text messages, images, documents, and voice notes to any WhatsApp number. Retrieve message history and track delivery status from queued through read.
List, filter, and organize your WhatsApp conversations. Assign chats to team members, add labels, manage chat status, and add internal notes.
Upload files from URLs or directly. Send images, PDFs, audio, and video as WhatsApp message attachments.
Get instant notifications when messages arrive, are sent, chats are created, or account statuses change. Partner webhooks cover quota alerts and workspace-level events.
Provision and configure customer workspaces, add users, generate QR codes for WhatsApp onboarding, and manage API tokens programmatically.
Track messaging quotas, API call limits, and seat utilization. Partner webhooks alert you when workspaces approach their limits.
## Key concepts
| Concept | Description |
| --------------- | ---------------------------------------------------------------------------------------- |
| **Workspace** | Your TimelinesAI account containing all chats, messages, and connected WhatsApp accounts |
| **Chat** | A conversation thread with a contact or group |
| **WID** | WhatsApp ID format: `phonenumber@s.whatsapp.net` (e.g., `14155551234@s.whatsapp.net`) |
| **JID** | Jabber ID — used for groups: `groupid@g.us` |
| **Message UID** | Unique identifier for each message (UUID format) |
## Rate limits & credits
Messages sent via API consume credits from your messaging quota:
| Action | Credits |
| ----------------------- | -------- |
| Text message | 1 |
| Message with attachment | 2 |
| Failed message | Refunded |
Messages are sent with a \~2 second delay between each to comply with WhatsApp guidelines. [Contact support](mailto:support@timelines.ai) to customize this on Business plans.
## Need help?
Email our support team
Access your TimelinesAI workspace
# Partner API Overview
Source: https://timelines.ai/docs/partner-api-reference/overview
Programmatically manage TimelinesAI workspaces, users, and WhatsApp connections for your platform
# Partner API
The TimelinesAI Partner API enables B2B partners to programmatically provision and manage workspaces, users, and WhatsApp account connections. This server-to-server API allows partners to integrate TimelinesAI capabilities directly into their own platforms without requiring end-users to interact with the TimelinesAI UI.
The Partner API uses JWT authentication, separate from the Public API's bearer token. See [Getting started](#getting-started) below to set up your partner credentials.
## Billing
Understanding the billing model is essential before provisioning workspaces and connecting WhatsApp accounts.
Partners are invoiced monthly in arrears based on **connected WhatsApp accounts**, not provisioned seats or created users. A WhatsApp account counts toward billing from the moment it is connected until it is explicitly disconnected.
| Concept | How it works |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Billing unit** | Each connected WhatsApp account (not seats or users) |
| **Billing cycle** | Monthly in arrears |
| **Seats** | Control the maximum number of users/WhatsApp accounts in a workspace (1–99). Seats are a capacity limit, not a billing unit. |
| **Trials & offboarding** | If a customer trial does not convert, **actively disconnect their WhatsApp account** using `POST /workspaces/{id}/users/{user_id}/whatsapp/disconnect` to stop billing. Simply deleting or ignoring the workspace is not enough — the connected account will continue to be billed. |
| **Seat downgrades** | When you reduce `seats_purchased` below current utilization, the platform may automatically suspend users and their associated WhatsApp accounts to comply with the new limit. |
| **Quota monitoring** | Subscribe to `workspace:seats_full` [Partner API Webhooks](/docs/partner-api-webhook-reference/overview) to get alerted when all seats are utilized, so you can proactively manage capacity. |
Always track the mapping between your customers and their `workspace_id` / `user_id` / WhatsApp account connections. This allows you to promptly disconnect accounts when a customer churns, preventing unnecessary charges.
## Getting started
To begin using the Partner API, you need to have your partner records created in the TimelinesAI system. This is a one-time setup process.
Reach out to TimelinesAI at [support@timelines.ai](mailto:support@timelines.ai) to request partner access. The team will set up your partner records in the system.
Once your partner account is provisioned, you will receive two credentials:
* **Partner ID** — your unique partner identifier (e.g., `your-company-name`)
* **Partner Secret** — a secure key used to sign JWT tokens (keep this confidential)
Use your credentials to generate a short-lived JWT token for authenticating API requests. See the [Authentication](#authentication) section below for code examples.
Include the generated JWT token in the `Authorization` header and your Partner ID in the `X-TL-Partner-Id` header with every request.
## Base URL
```
https://app.timelines.ai/partner/api/v1
```
## Authentication
The Partner API uses JWT (JSON Web Token) bearer authentication. Each request must include a valid JWT token signed with your Partner Secret using the HS256 algorithm.
### Required headers
Include the following headers in all API requests:
| Header | Value |
| ----------------- | -------------------- |
| `Authorization` | `Bearer ` |
| `X-TL-Partner-Id` | Your partner ID |
| `Content-Type` | `application/json` |
### JWT token generation
Generate a short-lived JWT token with the following payload claims:
| Claim | Type | Description |
| ------------ | ------ | ------------------------------------------------------------ |
| `partner_id` | string | Your partner identifier |
| `nbf` | number | Not before — set to 3 minutes in the past (Unix timestamp) |
| `exp` | number | Expiration — set to 3 minutes in the future (Unix timestamp) |
The token must be signed using the **HS256** algorithm with your **Partner Secret**.
```javascript JavaScript theme={null}
const jwt = require('jsonwebtoken');
// Configuration
const PARTNER_ID = 'your-partner-id';
const PARTNER_SECRET = 'your-partner-secret';
const BASE_URL = 'https://app.timelines.ai/partner/api/v1';
// Generate JWT token
function generateToken() {
const now = Math.floor(Date.now() / 1000);
return jwt.sign({
partner_id: PARTNER_ID,
nbf: now - 180, // Valid from 3 min ago
exp: now + 180 // Expires in 3 min
}, PARTNER_SECRET);
}
// Make authenticated request
async function apiRequest(method, endpoint, body = null) {
const token = generateToken();
const options = {
method: method,
headers: {
'Authorization': `Bearer ${token}`,
'X-TL-Partner-Id': PARTNER_ID,
'Content-Type': 'application/json'
}
};
if (body) options.body = JSON.stringify(body);
const response = await fetch(`${BASE_URL}${endpoint}`, options);
return response.json();
}
```
```python Python theme={null}
import jwt
import requests
from datetime import datetime, timedelta
# Configuration
PARTNER_ID = 'your-partner-id'
PARTNER_SECRET = 'your-partner-secret'
BASE_URL = 'https://app.timelines.ai/partner/api/v1'
# Generate JWT token
def generate_token():
now = datetime.utcnow()
payload = {
'partner_id': PARTNER_ID,
'nbf': int((now - timedelta(minutes=3)).timestamp()),
'exp': int((now + timedelta(minutes=3)).timestamp())
}
return jwt.encode(payload, PARTNER_SECRET, algorithm='HS256')
# Make authenticated request
def api_request(method, endpoint, json_body=None):
token = generate_token()
headers = {
'Authorization': f'Bearer {token}',
'X-TL-Partner-Id': PARTNER_ID,
'Content-Type': 'application/json'
}
url = f'{BASE_URL}{endpoint}'
response = requests.request(method, url, headers=headers, json=json_body)
return response.json()
```
JWT tokens are short-lived (6-minute window). Generate a fresh token for each API request rather than caching tokens.
## Capabilities
Create and manage customer workspaces with configurable seat allocations. Update display names and scale seats up or down.
Create placeholder users in batch — each user consumes one seat and can have a WhatsApp account connected.
Generate QR code links for users to connect their WhatsApp accounts by scanning. Supports embedded (iframe) and standalone modes.
Forcibly disconnect WhatsApp accounts from users when needed.
Retrieve and rotate Public API tokens for any managed workspace. Tokens don't expire unless explicitly rotated.
## Available endpoints
| Group | Endpoint | Description |
| -------------- | --------------------------------------------------------------------- | -------------------------------------------------------------------- |
| **Workspaces** | `POST /workspaces` | Create a new partner-managed workspace with specified seats |
| | `GET /workspaces/{workspace_id}` | Get workspace details including quotas, users, and WhatsApp accounts |
| | `PATCH /workspaces/{workspace_id}` | Update workspace display name and/or seat count |
| **Users** | `POST /workspaces/{workspace_id}/users` | Create placeholder users in batch (each consumes one seat) |
| **QR Codes** | `POST /workspaces/{workspace_id}/users/{user_id}/qr` | Generate a QR code link for WhatsApp connection |
| **WhatsApp** | `POST /workspaces/{workspace_id}/users/{user_id}/whatsapp/disconnect` | Disconnect a WhatsApp account from a user |
| **API Tokens** | `GET /workspaces/{workspace_id}/api-token` | Retrieve the Public API token (creates one if needed) |
| | `POST /workspaces/{workspace_id}/api-token` | Rotate the Public API token (invalidates the old one immediately) |
## Use case examples
Embed WhatsApp messaging into your own SaaS product without exposing TimelinesAI to your end users.
1. Use `POST /workspaces` to create a workspace when a customer signs up
2. Create users with `POST /workspaces/{id}/users` (one per WhatsApp number they need)
3. Generate QR codes with `POST /workspaces/{id}/users/{user_id}/qr` — embed in your UI using `display_mode=embedding`
4. Retrieve their Public API token with `GET /workspaces/{id}/api-token`
5. Use the Public API token in your backend to send/receive messages on their behalf
Offer WhatsApp as a service to multiple clients, managing everything from a single integration.
1. Provision workspaces per client with `POST /workspaces`
2. Configure seat allocations with `PATCH /workspaces/{id}`
3. Onboard client WhatsApp numbers via QR codes
4. Monitor workspace health using Partner API Webhooks (quota alerts, account connectivity)
5. Rotate API tokens periodically for security with `POST /workspaces/{id}/api-token`
Streamline the setup process so new customers can start using WhatsApp within minutes.
1. Create workspace automatically when customer completes your sign-up flow
2. Create users in batch for the number of WhatsApp accounts they need
3. Present QR codes in your UI (iframe with `display_mode=embedding` to avoid session logout)
4. Listen for `whatsapp_account:connected` webhook to detect successful connection
5. Retrieve the Public API token and begin message syncing automatically
Maintain control over API access across all managed workspaces.
1. Rotate Public API tokens periodically with `POST /workspaces/{id}/api-token`
2. Subscribe to `api-token:rotated` webhook to track all token changes
3. Disconnect compromised WhatsApp accounts with the disconnect endpoint
4. Monitor quota webhooks for billing compliance and to prevent service interruption
## Typical integration flow
`POST /workspaces` with `display_name` and `seats_purchased` — save the returned `workspace_id` for all subsequent operations
`POST /workspaces/{id}/users` with the `count` of users needed — each user consumes one seat
`POST /workspaces/{id}/users/{user_id}/qr` for each user — present the QR link in your UI for WhatsApp scanning
`GET /workspaces/{id}/api-token` — use this token with the Public API to send/receive messages
## QR code embedding
By default, the QR link opens a standalone page. To embed it within your application (e.g., in an iframe), append `display_mode=embedding` to the QR link URL.
| Mode | Behavior |
| --------------------------------------- | ------------------------------------------------------------------------------ |
| **Embedded** (`display_mode=embedding`) | Does **not** log the user out of your application. Optimized for iframe usage. |
| **Standalone** (default) | Opens a full page — **will trigger a logout** from the current session. |
Generating a new QR code disconnects any existing WhatsApp account for that user. Cache and reuse QR links while a connection attempt is active — only request a new one after the previous attempt completes or expires.
## Important notes
Public API tokens obtained via `GET /api-token` **do not expire automatically**. A token remains valid indefinitely unless explicitly rotated via `POST /api-token`. The old token is invalidated immediately upon rotation.
Always store `workspace_id` and `user_id` mappings in your system. There is currently no list-workspaces endpoint — losing a workspace ID requires a support ticket.
Warm up new WhatsApp accounts before automation — exchange messages with 3–5 contacts first. Poll delivery statuses before running automation sequences. See [WhatsApp mass messaging best practices](https://help.timelines.ai/en/articles/12383621-whatsapp-mass-messaging-best-practices) for details.
## Next steps
Get real-time notifications for workspace events
Learn about the Public API for message management
# Get public API token
Source: https://timelines.ai/docs/partner-api-reference/publicapi/get-public-api-token
get /workspaces/{workspace_id}/api-token
Retrieves the current Public API token for a partner-managed workspace. If a token already exists it is returned as the raw token value so that the partner can configure it in their integration. If no token exists yet, the platform creates a new one and returns it. Only workspaces owned by the calling partner and not billed via are eligible. The response must be treated as sensitive: tokens are returned only over authenticated Partner API calls .
# Rotate public API token
Source: https://timelines.ai/docs/partner-api-reference/publicapi/rotate-public-api-token
post /workspaces/{workspace_id}/api-token
Rotates the Public API token for a partner-managed workspace. A new token is generated, persisted according to the platform’s security rules, and the previous token is invalidated immediately. Each successful rotation returns the new raw token value and the rotated_at timestamp. Subsequent calls rotate the token again; the operation is not idempotent. Workspace ownership and billing eligibility checks apply. Partners are expected to update their integrations to use the newly issued token as soon as it is returned.
# Embedding overview
Source: https://timelines.ai/docs/partner-api-reference/qr/embedding-overview
Embed the WhatsApp QR connection page in your own product, with default or fully custom UI
The `qr_link` returned by [Generate QR code for user](/docs/partner-api-reference/qr/generate-qr-code-for-user) opens a TimelinesAI-hosted page that walks the end user through connecting their WhatsApp account. Partners building white-label experiences typically embed this page in an iframe so the user never leaves the partner's product.
This section covers everything that controls how that embedded experience looks and behaves: the URL parameter that styles the page for embedding, and a `postMessage`-based API for partners who want to drive the flow with their own UI.
## Two integration modes
Embed the QR connection page in an iframe and let TimelinesAI render the QR code, phone-pairing fallback, and instructions. Minimal partner-side code. Use the `display_mode=embedding` URL parameter for iframe-friendly styling — see [Partner API Overview → QR code embedding](/docs/partner-api-reference/overview#qr-code-embedding).
Hide the iframe entirely and drive the connection flow through the [postMessage API](/docs/partner-api-reference/qr/embedding-postmessage-api). Receive the raw QR payload and pairing code as events, render them in your own design system, and listen for connection and error events.
Both modes use the same `qr_link` URL — the difference is purely partner-side.
## Prerequisite: register your embedding domain
Embedding the QR page in your application requires your partner record to have one or more **embedding domains** registered. This is the allowlist of parent-window origins to which TimelinesAI will dispatch outbound `postMessage` events and from which it will accept inbound `postMessage` commands.
This applies in both integration modes — even a partner using the default UI inside an iframe receives the `TIMELINES_QR_CONNECTED` outbound event on successful connection, which requires the allowlist to be configured.
Email [support@timelines.ai](mailto:support@timelines.ai) with the list of origins where you will embed the iframe (e.g. `https://app.your-product.com`, `https://staging.your-product.com`). Include exact scheme, host, and port for each.
The TimelinesAI team registers the origins on your partner record. Multiple origins are supported.
Once registered, embed the iframe from one of the allowed origins and confirm a postMessage round-trip succeeds. Messages from any other origin are silently ignored by the iframe.
Origin matching is exact: scheme, host, and port must all match. `https://app.example.com` is **not** the same as `https://www.app.example.com` or `http://app.example.com`. If you embed from redirects, rewrites, or a non-standard port, list every variant.
## Next
Inbound and outbound message catalog for driving the flow programmatically.
# postMessage API
Source: https://timelines.ai/docs/partner-api-reference/qr/embedding-postmessage-api
Drive the QR and phone-pairing flows from your own UI via postMessage events
The embedded QR page exposes a two-way `postMessage` API between the parent window (your application) and the iframe (the TimelinesAI QR page). Partners use it to:
* **Trigger** flows programmatically (start QR generation, request a pairing code) without the user clicking inside the iframe.
* **Receive** raw payloads (QR data, pairing code) that can be rendered in the partner's own UI, optionally with the iframe hidden entirely.
* **Observe** connection state and errors.
This is the contract used by partners who want full control over the user experience — for example, rendering the QR code with their own QR-image library and styling, or showing the pairing code in a non-iframe surface elsewhere on the page.
**Prerequisite:** your partner record must have your embedding domain(s) registered. Messages from origins not on the allowlist are silently ignored. See [Embedding overview → Prerequisite](/docs/partner-api-reference/qr/embedding-overview#prerequisite-register-your-embedding-domain).
## Message direction summary
| Direction | Type | Purpose |
| --------------- | ------------------------------- | ---------------------------------------------------- |
| Parent → iframe | `TIMELINES_START_QR` | Start the QR-code generation flow |
| Parent → iframe | `TIMELINES_START_PHONE_LINKING` | Request a pairing code for a phone number |
| iframe → Parent | `TIMELINES_QR_CODE_DATA` | A new QR code is available (fires on every rotation) |
| iframe → Parent | `TIMELINES_PAIRING_CODE` | The 8-digit pairing code is available |
| iframe → Parent | `TIMELINES_QR_CONNECTED` | The WhatsApp account has connected successfully |
| iframe → Parent | `TIMELINES_ERROR` | The flow failed; details in payload |
## Inbound messages (parent → iframe)
Send messages to the iframe with `iframe.contentWindow.postMessage(payload, targetOrigin)`. The iframe validates `event.origin` against your registered embedding domain before processing.
### `TIMELINES_START_QR`
Starts QR-code generation. Equivalent to the user clicking the "Generate QR Code" button in the default UI.
```js theme={null}
iframe.contentWindow.postMessage(
{ type: "TIMELINES_START_QR" },
"https://app.timelines.ai"
);
```
The iframe will then dispatch `TIMELINES_QR_CODE_DATA` outbound events as QR codes are generated and rotated (\~every 45 seconds while waiting for a scan).
### `TIMELINES_START_PHONE_LINKING`
Requests an 8-digit pairing code for a specific phone number. Equivalent to the user typing their number into the phone-pairing form and clicking "Request pairing code".
```js theme={null}
iframe.contentWindow.postMessage(
{
type: "TIMELINES_START_PHONE_LINKING",
phone: "+31612345678"
},
"https://app.timelines.ai"
);
```
| Field | Type | Description |
| ------- | ------ | ----------------------------------------------------------------------- |
| `type` | string | Must be `"TIMELINES_START_PHONE_LINKING"`. |
| `phone` | string | Phone number in international format with leading `+` and country code. |
The iframe will dispatch `TIMELINES_PAIRING_CODE` once the code is issued, then `TIMELINES_QR_CONNECTED` when the user enters it on their phone.
## Outbound messages (iframe → parent)
Listen with `window.addEventListener("message", handler)`. Always validate `event.origin` against `https://app.timelines.ai` before trusting the payload.
### `TIMELINES_QR_CODE_DATA`
Fires every time a new QR code is generated, including rotations (\~every 45 seconds while waiting for a scan). The `data` field is a **base64-encoded SVG data URL** — drop it directly into an `
` element, a CSS `background-image`, or any surface that accepts a data URL.
```js theme={null}
{
type: "TIMELINES_QR_CODE_DATA",
data: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi..."
}
```
| Field | Type | Description |
| ------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data` | string | Ready-to-render SVG of the QR code, encoded as a `data:image/svg+xml;base64,...` URL. Already includes the QR's quiet zone, scale, and styling — use as-is. |
This event is dispatched only to partners that started the flow with `TIMELINES_START_QR`. If you embed the iframe with the default UI and never send any inbound message, the QR is rendered inside the iframe instead and this event does not fire.
You will receive this event **multiple times per session** as the QR code rotates. Always replace the previous image with the latest payload — stale QR codes will fail to scan.
### `TIMELINES_PAIRING_CODE`
Fires when an 8-digit pairing code has been issued in response to `TIMELINES_START_PHONE_LINKING`.
```js theme={null}
{ type: "TIMELINES_PAIRING_CODE", code: "12345678" }
```
| Field | Type | Description |
| ------ | ------ | ---------------------------------------------------------------------------------------- |
| `code` | string | 8-digit pairing code, no separators. Display to the user as `1234-5678` for readability. |
### `TIMELINES_QR_CONNECTED`
Fires once the WhatsApp account is successfully connected, regardless of whether the user used QR or phone pairing. The payload is empty.
```js theme={null}
{ type: "TIMELINES_QR_CONNECTED" }
```
This event is dispatched to **all** registered embedding domains for your partner record, regardless of whether you opened the flow with an inbound `TIMELINES_START_QR` / `TIMELINES_START_PHONE_LINKING` message. Default-UI iframes that never send an inbound message still receive this event.
After this event the QR link is invalidated server-side. To check the connected account's identifier and phone number, call the Public API or [Get workspace details](/docs/partner-api-reference/workspaces/get-workspace-details). Generating a new QR for the same user requires a fresh call to [Generate QR code for user](/docs/partner-api-reference/qr/generate-qr-code-for-user).
### `TIMELINES_ERROR`
Fires on flow failures. The `message` field carries a human-readable description suitable for display.
```js theme={null}
{
type: "TIMELINES_ERROR",
error: "",
message: "QR Code mismatch. Please generate a new one."
}
```
| Field | Type | Description |
| --------- | ------ | ----------------------------------------------------------------------------------------------------------------- |
| `error` | string | **Currently always empty.** Reserved for a future machine-readable error code. Do not branch on this field today. |
| `message` | string | Human-readable description of the failure. Suitable for displaying directly to the user. |
Conditions that emit `TIMELINES_ERROR`:
* QR-code mismatch on scan
* Server-side ban (account or device)
* WhatsApp logout (`logged_out`, `logged_out_client`)
* WhatsApp-side ban (`banned`)
* Account flagged for payment (`payment_required`)
* Pairing-code timeout (during the phone-linking flow)
* Pairing-code request failure (e.g. invalid phone number, blacklisted number)
**QR-code timeout does not emit `TIMELINES_ERROR` today.** When the QR rotates without being scanned for too long, the iframe shows an in-iframe "QR Code timed out" message but does not notify the parent. If you've hidden the iframe entirely, you will not learn that the QR expired. Keep the iframe visible at least minimally, or send a fresh `TIMELINES_START_QR` periodically to refresh.
## End-to-end example: custom QR UI
Hide the iframe and render the QR code yourself. The `data` field is a ready-to-use SVG data URL — drop it into an `
`:
```html theme={null}
```
If you need to apply your own visual treatment to the QR (different colors, embedded logo, custom margins), inline the SVG into the DOM instead of using `
`:
```js theme={null}
case "TIMELINES_QR_CODE_DATA":
// Strip "data:image/svg+xml;base64," prefix and decode.
const svgMarkup = atob(msg.data.split(",")[1]);
document.getElementById("my-qr-container").innerHTML = svgMarkup;
break;
```
## End-to-end example: custom phone-pairing UI
Same skeleton, different inbound trigger and a different outbound event to handle:
```js theme={null}
window.addEventListener("message", (event) => {
if (event.origin !== "https://app.timelines.ai") return;
const msg = event.data;
if (msg.type === "TIMELINES_PAIRING_CODE") {
// Format as 1234-5678 and display in your own UI.
const formatted = `${msg.code.slice(0, 4)}-${msg.code.slice(4)}`;
document.getElementById("my-pairing-code").textContent = formatted;
}
if (msg.type === "TIMELINES_QR_CONNECTED") {
// Same as QR flow — handle success.
}
if (msg.type === "TIMELINES_ERROR") {
// Same as QR flow — handle errors.
}
});
// Trigger phone pairing programmatically.
iframe.contentWindow.postMessage(
{ type: "TIMELINES_START_PHONE_LINKING", phone: "+31612345678" },
"https://app.timelines.ai"
);
```
## Implementation notes
* **The iframe must be loaded** (`load` event fired) before the parent can send inbound messages. Sending earlier is a no-op.
* **Origin validation is exact.** If your registered embedding domain is `https://app.example.com`, sending from `https://www.app.example.com` will fail silently. Register every variant you embed from.
* **`TIMELINES_QR_CODE_DATA` and `TIMELINES_PAIRING_CODE` are gated.** They only fire once you've sent `TIMELINES_START_QR` or `TIMELINES_START_PHONE_LINKING` respectively. Default-UI partners who never send an inbound message will not receive these events — the QR / pairing code is rendered inside the iframe instead.
* **`TIMELINES_QR_CONNECTED` is not gated.** It fires for any embedded session — both for partners using the default UI and for partners driving the flow with `postMessage` — so you can always rely on it as a connection-success signal.
* **QR-code timeout has no outbound notification today.** If you've hidden the iframe entirely and rely only on `postMessage`, you will not learn that the QR has expired. See the warning under [`TIMELINES_ERROR`](#timelines_error).
* **Phone-pairing UI always renders inside the iframe.** There is no toggle to disable it. If you want a QR-only flow, hide the iframe entirely and drive QR via `TIMELINES_START_QR`.
* **The default UI remains active even if you hide the iframe.** Hiding the iframe prevents the user from seeing TimelinesAI's UI but does not disable it server-side.
* **The `qr_link` is single-use across connections.** After `TIMELINES_QR_CONNECTED`, generating another QR for the same user requires a fresh call to the [QR endpoint](/docs/partner-api-reference/qr/generate-qr-code-for-user).
## See also
* [Embedding overview](/docs/partner-api-reference/qr/embedding-overview)
* [Generate QR code for user](/docs/partner-api-reference/qr/generate-qr-code-for-user)
* [Partner API Overview → QR code embedding](/docs/partner-api-reference/overview#qr-code-embedding) — `display_mode=embedding` URL parameter
# Generate QR code for user
Source: https://timelines.ai/docs/partner-api-reference/qr/generate-qr-code-for-user
post /workspaces/{workspace_id}/users/{user_id}/qr
Issues a long-lived WhatsApp QR-code link for a specific user in a partner-managed workspace. If the user already has a connected WhatsApp account, that account is disconnected first and the previous QR link (if any) is revoked. Only one active QR link per user is allowed at any given time; generating a new link invalidates any existing one. The request optionally accepts expires_in_hours (1–168, default 24) to control how long the QR link remains valid. The response includes the workspace_id, user_id, an unguessable qr_link URL, its expires_at timestamp, and flags indicating whether an existing WhatsApp account was disconnected and whether a previously active QR link was revoked. Generating a new QR code link is a destructive action: any currently connected WhatsApp account for this user will be forcibly disconnected.
# Disconnect WhatsApp account
Source: https://timelines.ai/docs/partner-api-reference/whatsappaccounts/disconnect-whatsapp-account
post /workspaces/{workspace_id}/users/{user_id}/whatsapp/disconnect
Forcibly disconnects the WhatsApp account currently linked to a specific user. If the user has an active WhatsApp connection, the session is terminated, sync is stopped and credentials are revoked. The response includes identifiers of the disconnected WhatsApp account and phone number together with a disconnected=true flag and the disconnection timestamp. If the user has no active WhatsApp account, the operation is treated as an idempotent no-op and returns disconnected=false. The call is still subject to standard partner authentication and workspace ownership checks.
# Create a workspace
Source: https://timelines.ai/docs/partner-api-reference/workspaces/create-a-workspace
post /workspaces
Creates a new partner-managed workspace and automatically provisions an Owner user. The workspace_id identifier is derived from the requested display_name (lowercase, alphanumeric and dashes, up to 30 characters). The workspace is created on the calling partner's plan and linked to that partner for ownership and billing-eligibility checks. An Owner user is created with a non-login, system-managed email of the form `-@partners.timelines.ai` and added to the default workspace group. Seats are allocated according to seats_purchased (1-999); the Owner consumes one seat and the remaining seats become available for additional agents. On success the response returns workspace metadata, seat counters and the newly created owner_user_id. Conflicts on identifier generation (duplicate workspace_id) are reported with a 409 WorkspaceCreationFailed error.
# Create users in workspace
Source: https://timelines.ai/docs/partner-api-reference/workspaces/create-users-in-workspace
post /workspaces/{workspace_id}/users
Bulk-creates placeholder agent users in a partner-managed workspace. Each request specifies a count of users to create. For every user the platform: assigns the agent role and activated status, creates a non-login, system-managed email `-@partners.timelines.ai`, assigns the user to the workspace's Default group, consumes one seat from the workspace.
The operation is all-or-nothing: if there are not enough available seats to satisfy count, the request fails with a seats_full error and no users are created. On success the response returns updated seat counters and the list of created users, including their identifiers and metadata needed for downstream QR-link generation.
# Get workspace details
Source: https://timelines.ai/docs/partner-api-reference/workspaces/get-workspace-details
get /workspaces/{workspace_id}
Returns a consolidated, read-only summary of a partner-managed workspace. The payload includes basic workspace identity (workspace_id, display_name), seat allocation and utilization, messaging and API quotas, any non-recurring quota balance, the list of users in the workspace, WhatsApp accounts associated with the workspace, and currently active QR-code links. Only workspaces that are owned by the calling partner and are not -billing managed are eligible. If the workspace does not exist or is not linked to the partner, a 404 error is returned.
# Update workspace
Source: https://timelines.ai/docs/partner-api-reference/workspaces/update-workspace
patch /workspaces/{workspace_id}
Updates mutable workspace attributes for a partner-managed workspace. Partners can change the human-readable display_name and/or adjust the number of purchased seats (seats_purchased, 1-999). When the purchased seats are reduced below current utilization, the platform may automatically suspend users and their associated WhatsApp accounts to comply with the new limit. The response returns the updated workspace representation, including seat counters and, when applicable, details of suspended members so partners can reconcile the downgrade on their side. Standard partner ownership and billing eligibility checks apply before processing the update.
# API Token Rotated
Source: https://timelines.ai/docs/partner-api-webhook-reference/api_tokens/triggered-when-a-workspace-public-api-token-is-rotated-via-the-partner-api
webhook api_token:rotated
Triggered when a workspace Public API token is rotated via the Partner API.
# Partner API Webhooks Overview
Source: https://timelines.ai/docs/partner-api-webhook-reference/overview
Receive real-time notifications for workspace, account, and quota events across your managed workspaces
# Partner API Webhooks
PartnerAPI Webhooks notify your platform in real-time when important events occur across the workspaces you manage. Monitor quota usage, track WhatsApp account connections, and stay informed about API token changes — all without polling.
These webhooks are separate from the PublicAPI Webhooks. While PublicAPI Webhooks cover message-level and chat-level events within a single workspace, PartnerAPI Webhooks focus on workspace-level operational events relevant to platform administrators and resellers.
## Available events
### Workspace quota events
Get alerted when your managed workspaces approach or hit their usage limits. These events enable proactive billing management and prevent service interruptions.
| Event | Trigger | Description |
| ---------------------------- | ------------------------- | ---------------------------------------------------------------------------------------- |
| Seats fully utilized | All seats in use | Every purchased seat in the workspace is occupied — no more users can be added |
| Messaging quota at 90% | Nearing message limit | The workspace has used 90% or more of its messaging quota for the current billing period |
| Messaging quota at 100% | Message limit reached | The workspace has exhausted its messaging quota — new messages will be blocked |
| Transactions quota exhausted | Transaction limit reached | All available transactions for the workspace have been consumed |
| API calls quota at 100% | API call limit reached | The workspace has used 100% of its Public API call quota for the current billing period |
### WhatsApp account events
Track when WhatsApp numbers are connected or disconnected across your managed workspaces.
| Event | Trigger | Description |
| -------------------- | ----------------- | ---------------------------------------------------------------------- |
| Account connected | WhatsApp linked | A user successfully connected a WhatsApp account by scanning a QR code |
| Account disconnected | WhatsApp unlinked | A WhatsApp account was disconnected (manually or automatically) |
### API token events
Monitor security-sensitive changes to Public API tokens.
| Event | Trigger | Description |
| ------------- | ------------------------ | -------------------------------------------------------------- |
| Token rotated | Public API token changed | A workspace's Public API token was rotated via the Partner API |
## Use case examples
Prevent service disruptions by acting on quota warnings before limits are reached.
1. Subscribe to the 90% messaging quota event
2. When triggered, notify your customer that they are approaching their limit
3. Offer an upsell or auto-upgrade their plan
4. If the 100% event fires, display a warning in your UI and queue messages for later
Keep your billing system in sync with actual resource consumption.
1. Subscribe to all quota events (seats, messaging, transactions, API calls)
2. Log each event with the workspace ID and timestamp
3. Trigger billing adjustments, overage charges, or plan upgrades automatically
4. Generate usage reports for your customers
Know instantly when a customer's WhatsApp account is ready.
1. After generating a QR code via the Partner API, subscribe to the `account connected` event
2. When the event fires, update your onboarding flow to mark WhatsApp as connected
3. Trigger welcome messages or next-step guides for the customer
4. Monitor for disconnections and prompt users to reconnect
Track API token changes and account access across all workspaces.
1. Subscribe to the `token rotated` event to audit all token changes
2. Log which workspaces had tokens rotated and when
3. Subscribe to account disconnection events to detect unexpected access changes
4. Alert your security team if a workspace loses its WhatsApp connection unexpectedly
## Payload example
When a workspace's messaging quota reaches 90%, your endpoint receives:
```json theme={null}
{
"event_type": "workspace:messaging_quota:90",
"data": {
"workspace_id": 12345,
"quota_limit": 10000,
"quota_used": 9150,
"billing_period_end": "2024-02-01T00:00:00Z"
}
}
```
## Best practices
* **Act on 90% warnings** — don't wait for the 100% event. Proactively notify customers or auto-scale their plans.
* **Log all events** — maintain an audit trail of all webhook events for billing reconciliation and support troubleshooting.
* **Monitor account connectivity** — WhatsApp disconnections can disrupt your customer's business. React to disconnection events quickly.
* **Secure token rotation** — when you receive a `token rotated` event, update any cached tokens in your system immediately.
## Next steps
Manage workspaces, users, and accounts
Message and chat-level event notifications
# Account Connected
Source: https://timelines.ai/docs/partner-api-webhook-reference/whatsapp_accounts/triggered-when-a-whatsapp-account-is-successfully-connected-for-a-workspace-user
webhook whatsapp_account:connected
Triggered when a WhatsApp account is successfully connected for a workspace user.
# Account Disconnected
Source: https://timelines.ai/docs/partner-api-webhook-reference/whatsapp_accounts/triggered-when-a-whatsapp-account-is-successfully-disconnected-for-a-workspace-user
webhook whatsapp_account:disconnected
Triggered when a WhatsApp account is disconnected for a workspace user.
# Seats Full
Source: https://timelines.ai/docs/partner-api-webhook-reference/workspaces/triggered-when-all-purchased-seats-in-a-workspace-are-utilized-no-available-seats-remain
webhook workspace:seats_full
Triggered when all purchased seats in a workspace are utilized (no available seats remain).
# Messaging Quota Full
Source: https://timelines.ai/docs/partner-api-webhook-reference/workspaces/triggered-when-the-messaging-quota-for-a-workspace-reaches-or-exceeds-100-percent-utilization-within-the-current-billing-period
webhook workspace:quota_full:messaging
Triggered when the messaging quota for a workspace reaches or exceeds 100% utilization within the current billing period.
# Messaging Quota Near Full
Source: https://timelines.ai/docs/partner-api-webhook-reference/workspaces/triggered-when-the-messaging-quota-for-a-workspace-reaches-or-exceeds-90-percent-utilization-within-the-current-billing-period
webhook workspace:quota_near_full:messaging
Triggered when the messaging quota for a workspace reaches or exceeds 90% utilization within the current billing period.
# API Calls Quota Full
Source: https://timelines.ai/docs/partner-api-webhook-reference/workspaces/triggered-when-the-public-api-calls-quota-for-a-workspace-reaches-or-exceeds-100-percent-utilization-within-the-current-billing-period
webhook workspace:quota_full:api_calls
Triggered when the Public API calls quota for a workspace reaches or exceeds 100% utilization within the current billing period.
# Transactions Quota Full
Source: https://timelines.ai/docs/partner-api-webhook-reference/workspaces/triggered-when-the-transactions-quota-of-a-workspace-has-been-fully-utilized
webhook workspace:quota_full:transactions
Triggered when the transactions quota of a workspace has been fully utilized.
# Add Note
Source: https://timelines.ai/docs/public-api-reference/add-a-note-to-existing-chat
post /chats/{chat_id}/notes
Add a note to an existing WhatsApp chat or group specified by chat_id.
# Add Labels
Source: https://timelines.ai/docs/public-api-reference/adds-labels-for-the-chat
put /chats/{chat_id}/labels
Add labels to a chat without removing existing ones.
# Create Webhook
Source: https://timelines.ai/docs/public-api-reference/create-webhook
post /webhooks
Create a new webhook subscription for a specified event.
# Delete File
Source: https://timelines.ai/docs/public-api-reference/delete-the-specified-uploaded-file
delete /files/{file_uid}
Delete the specified uploaded file.
# Delete Webhook
Source: https://timelines.ai/docs/public-api-reference/delete-webhook
delete /webhooks/{webhook_id}
Permanently delete a webhook subscription. Deliveries stop immediately.
# Get File
Source: https://timelines.ai/docs/public-api-reference/get-details-and-temporary-download-url-for-a-specified-uploaded-file
get /files/{file_uid}
Get details and a temporary download URL (valid for 15 minutes) for a specified uploaded file.
# Get Chat
Source: https://timelines.ai/docs/public-api-reference/get-details-of-a-chat
get /chats/{chat_id}
Get details of a specific chat by chat ID.
# List Messages
Source: https://timelines.ai/docs/public-api-reference/get-filtered-chat-history-messages-only-of-the-chat
get /chats/{chat_id}/messages
Get filtered chat history (messages only) for a specific chat. Supports filtering by direction, date range, and message UID.
# List Chats
Source: https://timelines.ai/docs/public-api-reference/get-full-or-filtered-list-of-all-chats-in-the-workspace
get /chats
Get full or filtered list of all chats in the workspace. Supports filtering by label, WhatsApp account, phone, responsible, and more.
# Get Reactions
Source: https://timelines.ai/docs/public-api-reference/get-the-current-reactions-map-for-a-message
get /messages/{message_uid}/reactions
Get the current reactions map for a message.
# Get Message
Source: https://timelines.ai/docs/public-api-reference/get-the-details-of-a-message-specified-by-the-messages-uid
get /messages/{message_uid}
Get the details of a message specified by its UID.
# Get Message Status
Source: https://timelines.ai/docs/public-api-reference/get-the-sending-history-of-a-message-specified-by-the-messages-uid
get /messages/{message_uid}/status_history
Get the sending history of a message specified by its UID.
# Get Webhook
Source: https://timelines.ai/docs/public-api-reference/get-webhook
get /webhooks/{webhook_id}
Get the webhook subscription identified by webhook_id.
# Invite Teammate
Source: https://timelines.ai/docs/public-api-reference/invite-new-teammate-to-the-workspace
post /workspace/invitations
Send an invitation to a new teammate by email.
# List Teammates
Source: https://timelines.ai/docs/public-api-reference/list-all-teammates-in-the-workspace
get /workspace/teammates
List all teammates in the workspace.
# List Files
Source: https://timelines.ai/docs/public-api-reference/list-files-uploaded-in-your-timelinesai-workspace
get /files
List files uploaded in your TimelinesAI workspace. Supports filtering by filename.
# List Labels
Source: https://timelines.ai/docs/public-api-reference/list-labels-for-the-specified-chat
get /chats/{chat_id}/labels
List labels for the specified chat.
# List Webhooks
Source: https://timelines.ai/docs/public-api-reference/list-webhooks
get /webhooks
List all webhook subscriptions for the current workspace.
# List WhatsApp Accounts
Source: https://timelines.ai/docs/public-api-reference/list-whatsapp-accounts-connected-in-your-timelinesai-workspace
get /whatsapp_accounts
List WhatsApp accounts connected in your TimelinesAI workspace.
# Public API Overview
Source: https://timelines.ai/docs/public-api-reference/overview
Manage WhatsApp communications programmatically with the TimelinesAI Public API
# Public API
The TimelinesAI Public API gives you full control over your WhatsApp conversation resources. Send and receive messages, manage conversations, handle file attachments, organize chats with labels, and set up real-time webhooks — all through a simple REST API
## Base URL
```
https://app.timelines.ai/integrations/api
```
All requests require Bearer token authentication. See [Authentication](/docs/authentication) for details.
## Capabilities
Send text messages, attachments, and voice notes to any WhatsApp number. Track delivery status from queued through read.
List, filter, and update conversations. Assign chats to team members, close/reopen, and manage read status.
Upload files from URLs or directly. Send images, PDFs, audio, and video as WhatsApp attachments.
Organize chats with labels. Add, replace, or list labels to categorize and filter conversations.
Get and update emoji reactions on messages.
Subscribe to real-time events like new messages, sent messages, and chat updates.
List all connected WhatsApp numbers in your workspace.
View workspace details, plan, and current quota utilization.
## Available endpoints
| Group | Endpoints | Description |
| --------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| **Chats** | `GET /chats`, `GET /chats/{id}`, `PATCH /chats/{id}` | List, get details, and update chats |
| **Messages** | `POST /messages`, `POST /chats/{id}/messages`, `GET /chats/{id}/messages`, and more | Send messages by phone, chat ID, JID, or chat name. Retrieve history and status. |
| **Reactions** | `GET /messages/{uid}/reactions`, `PUT /messages/{uid}/reactions` | Read and update message reactions |
| **Files** | `GET /files`, `POST /files`, `POST /files_upload`, `GET /files/{uid}`, `DELETE /files/{uid}` | Upload, list, download, and delete files |
| **Labels** | `GET /chats/{id}/labels`, `PUT /chats/{id}/labels`, `POST /chats/{id}/labels` | List, add, and replace chat labels |
| **WhatsApp Accounts** | `GET /whatsapp_accounts` | List connected WhatsApp numbers |
| **Teammates** | `GET /workspace/teammates`, `POST /workspace/invitations`, `DELETE /workspace/invitations/{user_id}` | List teammates, invite, and revoke invitations |
| **Workspace** | `GET /workspace` | Workspace info, plan, and quota utilization |
| **Webhooks** | `GET /webhooks`, `POST /webhooks`, `GET/PUT/DELETE /webhooks/{id}` | Manage webhook subscriptions |
## Use case examples
Sync WhatsApp conversations with your CRM. When a new lead messages you, automatically create a contact in your CRM and assign the chat to the right sales rep.
1. Set up a webhook for `message:received:new`
2. On new message, check if the phone number exists in your CRM
3. Create or update the CRM contact
4. Use `PATCH /chats/{id}` to assign the chat to the appropriate agent
5. Add a label like `crm-synced` for tracking
Send order updates, appointment reminders, or shipping notifications directly to WhatsApp.
1. Use `POST /messages` with the customer's phone number
2. Include relevant details in the message text
3. Attach documents (invoices, receipts) using the file upload endpoints
4. Track delivery with `GET /messages/{uid}/status_history`
Route incoming WhatsApp messages to your support team based on content or labels.
1. Listen for new messages via webhooks
2. Parse the message content to determine the category
3. Add appropriate labels with `PUT /chats/{id}/labels`
4. Assign to the right agent with `PATCH /chats/{id}`
5. Add internal notes with `POST /chats/{id}/notes` for context
Send personalized messages to a list of contacts for marketing or onboarding campaigns.
1. Iterate over your contact list
2. Use `POST /messages` for each recipient with personalized text
3. Auto-apply labels (e.g., `campaign-jan-2025`) when sending
4. Respect the \~2 second rate limit between messages
5. Monitor delivery status and quota usage via `GET /workspace`
## Rate limits & credits
| Action | Credits |
| ----------------------- | -------- |
| Text message | 1 |
| Message with attachment | 2 |
| Failed message | Refunded |
Messages are sent with a \~2 second delay between each to comply with WhatsApp guidelines.
Check your current usage anytime with `GET /workspace`. [Contact support](mailto:support@timelines.ai) to discuss higher limits on Business plans.
## Next steps
Send your first message in 5 minutes
Set up your API token
# Send Voice Message
Source: https://timelines.ai/docs/public-api-reference/post-chats-voice_message
post /chats/{chat_id}/voice_message
Send a voice note into an existing WhatsApp chat or group. Supports OGG, OGA, and MP3 audio files.
# Replace Labels
Source: https://timelines.ai/docs/public-api-reference/replaces-labels-for-the-chat
post /chats/{chat_id}/labels
Replace all labels for a chat with the specified set.
# Revoke Invitation
Source: https://timelines.ai/docs/public-api-reference/revoke-a-pending-invitation
delete /workspace/invitations/{user_id}
Revoke a pending teammate invitation.
# Send Message to Chat
Source: https://timelines.ai/docs/public-api-reference/send-message-in-existing-chat
post /chats/{chat_id}/messages
Send a message into an existing WhatsApp chat or group specified by chat_id.
# Send Message to Chat Name
Source: https://timelines.ai/docs/public-api-reference/send-message-in-existing-chat-specified-by-chat-name
post /messages/to_chat_name
Send a message to an existing WhatsApp chat or group specified by its name in TimelinesAI.
**Deprecated** — This endpoint will be removed in a future release. Use [Send Message to Chat](/docs/public-api-reference/send-message-in-existing-chat) with `chat_id` or [Send Message to Phone](/docs/public-api-reference/send-message-to-phone-number) instead.
# Send Message to JID
Source: https://timelines.ai/docs/public-api-reference/send-message-to-jid
post /messages/to_jid
Send a message to a WhatsApp chat or group specified by JID.
# Send Message to Phone
Source: https://timelines.ai/docs/public-api-reference/send-message-to-phone-number
post /messages
Send a message to a WhatsApp phone number. Doesn't require a previous chat or contact with the recipient.
# Update Chat
Source: https://timelines.ai/docs/public-api-reference/update-chat
patch /chats/{chat_id}
Update chat's name, assign responsible, or close/re-open a chat.
# Update Reactions
Source: https://timelines.ai/docs/public-api-reference/update-reactions-for-a-message
patch /messages/{message_uid}/reactions
Add or remove emoji reactions for a message.
# Update Webhook
Source: https://timelines.ai/docs/public-api-reference/update-webhook
put /webhooks/{webhook_id}
Update fields of a webhook subscription. Only supplied properties are changed.
# Upload File (Form)
Source: https://timelines.ai/docs/public-api-reference/upload-a-file-in-x-form-encoded-http-request
post /files_upload
Upload a file using a multipart/form-data encoded HTTP request.
# Upload File (URL)
Source: https://timelines.ai/docs/public-api-reference/upload-a-file-using-a-publicly-accessible-url
post /files
Upload a file using a publicly accessible URL. Filename and mime-type are auto-detected.
# Add WABA Chat Labels
Source: https://timelines.ai/docs/public-api-reference/waba/adds-labels-for-the-waba-chat
put /waba/chats/{chat_id}/labels
Add labels to a WABA chat.
# Get WABA Account
Source: https://timelines.ai/docs/public-api-reference/waba/get-details-of-a-waba-account
get /waba/accounts/{account_id}
Get details of a specific WABA account.
# Get WABA Chat
Source: https://timelines.ai/docs/public-api-reference/waba/get-details-of-a-waba-chat
get /waba/chats/{chat_id}
Get details of a specific WABA chat by chat ID.
# Get WABA Template
Source: https://timelines.ai/docs/public-api-reference/waba/get-details-of-a-waba-template
get /waba/templates/{template_id}
Get details of a specific WABA template.
# Get WABA Chat History
Source: https://timelines.ai/docs/public-api-reference/waba/get-filtered-waba-chat-history-messages-only-of-the-chat
get /waba/chats/{chat_id}/messages
Get the filtered chat history (messages only) of a WABA chat.
# List WABA Chats
Source: https://timelines.ai/docs/public-api-reference/waba/get-full-or-filtered-list-of-all-waba-chats-in-the-workspace
get /waba/chats
Get a full or filtered list of all WABA chats in the workspace.
# Get WABA Reaction
Source: https://timelines.ai/docs/public-api-reference/waba/get-the-current-reaction-for-a-waba-message
get /waba/messages/{message_uid}/reactions
Get the current reaction for a WABA message.
# Get WABA Message
Source: https://timelines.ai/docs/public-api-reference/waba/get-the-details-of-a-waba-message-specified-by-the-messages-uid
get /waba/messages/{message_uid}
Get the details of a WABA message specified by its UID.
# List WABA Accounts
Source: https://timelines.ai/docs/public-api-reference/waba/get-the-list-of-all-waba-accounts-in-the-workspace
get /waba/accounts
Get the list of all WABA accounts in the workspace.
# List WABA Templates
Source: https://timelines.ai/docs/public-api-reference/waba/get-the-list-of-all-waba-templates-in-the-workspace
get /waba/templates
Get the list of all WABA templates in the workspace.
# Get WABA Message History
Source: https://timelines.ai/docs/public-api-reference/waba/get-the-sending-history-of-a-waba-message-specified-by-the-messages-uid
get /waba/messages/{message_uid}/status_history
Get the sending/status history of a WABA message specified by its UID.
# List WABA Chat Labels
Source: https://timelines.ai/docs/public-api-reference/waba/list-labels-for-the-specified-waba-chat
get /waba/chats/{chat_id}/labels
List labels for the specified WABA chat.
# WABA API Overview
Source: https://timelines.ai/docs/public-api-reference/waba/overview
Manage WhatsApp Business Platform (WABA) conversations, messages, templates, and accounts programmatically
# WABA API
The WABA endpoints extend the TimelinesAI Public API to the WhatsApp Business Platform (Cloud API). Use them to work with WABA chats and messages, manage message templates, read account analytics, and organize conversations with labels — all through the same REST API and Bearer authentication as the rest of the Public API.
## Base URL
```
https://app.timelines.ai/integrations/api
```
All requests require Bearer token authentication. See [Authentication](/docs/authentication) for details.
## Capabilities
List, filter, get, and update WABA chats.
Send WABA messages by phone number or in an existing chat, and retrieve message details and status history.
Get, set, and clear emoji reactions on WABA messages.
Organize WABA chats with labels — list, add, and replace.
List WABA accounts and request account-level messaging analytics.
List and inspect approved WABA message templates.
## Available endpoints
| Group | Endpoints | Description |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| **Chats** | `GET /waba/chats`, `GET /waba/chats/{chat_id}`, `PATCH /waba/chats/{chat_id}` | List, get details, and update WABA chats |
| **Messages** | `POST /waba/messages`, `POST /waba/chats/{chat_id}/messages`, `GET /waba/chats/{chat_id}/messages`, `GET /waba/messages/{message_uid}`, `GET /waba/messages/{message_uid}/status_history` | Send WABA messages and retrieve history and status |
| **Reactions** | `GET /waba/messages/{message_uid}/reactions`, `PATCH /waba/messages/{message_uid}/reactions` | Read, set, and clear WABA message reactions |
| **Labels** | `GET /waba/chats/{chat_id}/labels`, `POST /waba/chats/{chat_id}/labels`, `PUT /waba/chats/{chat_id}/labels` | List, replace, and add WABA chat labels |
| **Accounts** | `GET /waba/accounts`, `GET /waba/accounts/{account_id}`, `POST /waba/accounts/{account_id}/analytics`, `GET /waba/accounts/{account_id}/analytics/{handle_id}` | List accounts and request/poll account analytics |
| **Templates** | `GET /waba/templates`, `GET /waba/templates/{template_id}` | List and get WABA message templates |
A failed WABA message exposes a structured `failure_reason` object with `code`, `title`, and `details` — see [Get WABA Message](/docs/public-api-reference/waba/get-the-details-of-a-waba-message-specified-by-the-messages-uid).
# Poll WABA Account Analytics
Source: https://timelines.ai/docs/public-api-reference/waba/poll-a-waba-account-analytics-request
get /waba/accounts/{account_id}/analytics/{handle_id}
Poll a previously requested WABA account analytics job by its handle.
# Replace WABA Chat Labels
Source: https://timelines.ai/docs/public-api-reference/waba/replaces-labels-for-the-waba-chat
post /waba/chats/{chat_id}/labels
Replace all labels for a WABA chat with the specified set.
# Request WABA Account Analytics
Source: https://timelines.ai/docs/public-api-reference/waba/request-waba-account-analytics
post /waba/accounts/{account_id}/analytics
Request WABA account analytics; returns a handle to poll for the result.
# Send WABA Message in Chat
Source: https://timelines.ai/docs/public-api-reference/waba/send-waba-message-in-existing-chat
post /waba/chats/{chat_id}/messages
Send a WABA message in an existing chat.
# Send WABA Message to Phone
Source: https://timelines.ai/docs/public-api-reference/waba/send-waba-message-to-phone-number
post /waba/messages
Send a WABA message to a phone number.
# Set WABA Reaction
Source: https://timelines.ai/docs/public-api-reference/waba/set-or-clear-the-reaction-for-a-waba-message
patch /waba/messages/{message_uid}/reactions
Set or clear the reaction for a WABA message.
# Update WABA Chat
Source: https://timelines.ai/docs/public-api-reference/waba/update-waba-chat
patch /waba/chats/{chat_id}
Update a WABA chat (assignment, status, and read state).
# Get Workspace Info
Source: https://timelines.ai/docs/public-api-reference/workspace-info-and-all-current-quotas-and-utilization-stats
get /workspace
Returns workspace identity, plan, and current quota utilization.
# Quickstart
Source: https://timelines.ai/docs/quickstart
Send your first WhatsApp message in 5 minutes
# Quickstart
Get started with the TimelinesAI API in just a few steps.
## Prerequisites
A TimelinesAI account with at least one connected WhatsApp number
Basic familiarity with REST APIs
## Step 1: Get your API token
Go to [app.timelines.ai](https://app.timelines.ai) and sign in to your account.
Click **Integrations** → **Public API** in the left sidebar.
Click **Copy** to copy your API token. Keep this secure—it provides full access to your workspace.
Never share your API token publicly or commit it to version control. Use environment variables in production.
## Step 2: Test your connection
Verify your token works by listing your WhatsApp accounts:
```bash cURL theme={null}
curl -X GET "https://app.timelines.ai/integrations/api/whatsapp_accounts" \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
```javascript JavaScript theme={null}
const response = await fetch('https://app.timelines.ai/integrations/api/whatsapp_accounts', {
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
});
const data = await response.json();
console.log(data);
```
```python Python theme={null}
import requests
response = requests.get(
'https://app.timelines.ai/integrations/api/whatsapp_accounts',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'}
)
print(response.json())
```
You should see a response like:
```json theme={null}
{
"status": "ok",
"data": [
{
"id": "14155551234@s.whatsapp.net",
"phone": "+14155551234",
"name": "Business WhatsApp",
"connected": true
}
]
}
```
## Step 3: Send your first message
Now send a message to a phone number:
```bash cURL theme={null}
curl -X POST "https://app.timelines.ai/integrations/api/messages" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"phone": "+14155559999",
"text": "Hello from TimelinesAI API! 🎉"
}'
```
```javascript JavaScript theme={null}
const response = await fetch('https://app.timelines.ai/integrations/api/messages', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
phone: '+14155559999',
text: 'Hello from TimelinesAI API! 🎉'
})
});
const data = await response.json();
console.log(data);
```
```python Python theme={null}
import requests
response = requests.post(
'https://app.timelines.ai/integrations/api/messages',
headers={
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
json={
'phone': '+14155559999',
'text': 'Hello from TimelinesAI API! 🎉'
}
)
print(response.json())
```
Replace `+14155559999` with a real phone number. The recipient must have WhatsApp installed.
Success response:
```json theme={null}
{
"status": "ok",
"data": {
"message_uid": "a5bbb005-37f2-402c-96fa-e479a2e09b02"
}
}
```
Messages are queued and sent asynchronously. Use the `message_uid` to track delivery status.
## Step 4: Check message status
Track your message delivery:
```bash cURL theme={null}
curl -X GET "https://app.timelines.ai/integrations/api/messages/a5bbb005-37f2-402c-96fa-e479a2e09b02/status_history" \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
```javascript JavaScript theme={null}
const response = await fetch(
'https://app.timelines.ai/integrations/api/messages/a5bbb005-37f2-402c-96fa-e479a2e09b02/status_history',
{
headers: { 'Authorization': 'Bearer YOUR_API_TOKEN' }
}
);
const data = await response.json();
console.log(data);
```
```python Python theme={null}
import requests
response = requests.get(
'https://app.timelines.ai/integrations/api/messages/a5bbb005-37f2-402c-96fa-e479a2e09b02/status_history',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'}
)
print(response.json())
```
## Next steps
Learn to send images and documents
Receive real-time notifications
Organize and assign conversations
Explore all endpoints
# Rate Limits
Source: https://timelines.ai/docs/rate-limits
Understand API rate limits and how to handle them gracefully
## Overview
TimelinesAI APIs enforce rate limits to ensure fair usage and platform stability for all users. Requests that exceed the rate limit will receive an HTTP `429 Too Many Requests` response.
## Rate Limit Details
| Parameter | Value |
| --------------------- | ---------------------------- |
| **Rate limit** | 30 requests per second |
| **Per** | IP address |
| **Burst allowance** | 10 additional requests |
| **Exceeded response** | HTTP `429 Too Many Requests` |
The rate limit applies **per IP address**, not per API token. If you have multiple integrations running from the same IP, they share the same rate limit budget.
## How It Works
Incoming requests are evaluated against a limit of **30 requests per second** per IP address. When you exceed this rate:
1. Up to **10 additional requests** are queued (burst allowance) and processed once capacity becomes available.
2. Any requests beyond the burst allowance are immediately rejected with an HTTP `429` status code.
```
Normal traffic: ✅ ≤ 30 req/s → Processed immediately
Burst traffic: ⏳ 31–40 req/s → Queued and processed shortly
Over limit: ❌ > 40 req/s → Rejected with HTTP 429
```
## Handling Rate Limits
When you receive a `429` response, implement an **exponential backoff** strategy to retry requests:
```javascript JavaScript theme={null}
async function requestWithRetry(url, options, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status === 429) {
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s, 8s, 16s
console.warn(`Rate limited. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}
```
```python Python theme={null}
import time
import requests
def request_with_retry(url, headers=None, max_retries=5):
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
if response.status_code == 429:
delay = (2 ** attempt) # 1s, 2s, 4s, 8s, 16s
print(f"Rate limited. Retrying in {delay}s...")
time.sleep(delay)
continue
return response
raise Exception("Max retries exceeded")
```
```bash cURL theme={null}
# Example 429 response
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
{
"error": "Too Many Requests",
"message": "Rate limit exceeded. Please retry after a short delay."
}
```
## Best Practices
Instead of sending bulk requests all at once, distribute them evenly. For example, if you need to send 100 messages, space them out at \~30 per second rather than sending all 100 simultaneously.
When you receive a `429` response, wait progressively longer between retries — e.g., 1 second, then 2, then 4. This prevents a "thundering herd" effect when rate limits lift.
For batch processing (e.g., sending messages to many contacts), implement a client-side queue that respects the 30 req/s limit. Process items from the queue at a controlled rate.
Track your request rates in your application logs. If you're consistently hitting rate limits, consider optimizing your integration to make fewer, more targeted API calls.
Repeatedly exceeding rate limits without implementing backoff may result in temporarily extended throttling for your IP address.
# Incoming Call Ended
Source: https://timelines.ai/docs/webhook-reference/calls/triggered-when-an-incoming-whatsapp-call-ended
webhook call:incoming:ended
Triggered when an incoming WhatsApp voice or video call ended. Status is Answered when the call was picked up, or Rejected when it was actively declined by the recipient.
# Incoming Call Missed
Source: https://timelines.ai/docs/webhook-reference/calls/triggered-when-an-incoming-whatsapp-call-was-missed
webhook call:incoming:missed
Triggered when an incoming WhatsApp voice or video call was missed (not answered before the caller ended the call).
# Outgoing Call Ended
Source: https://timelines.ai/docs/webhook-reference/calls/triggered-when-an-outgoing-whatsapp-call-ended
webhook call:outgoing:ended
Triggered when an outgoing WhatsApp voice or video call ended. Status is Ended for completed calls, No Answer when the recipient never picked up, or Rejected when the recipient actively declined.
# Chat Assigned
Source: https://timelines.ai/docs/webhook-reference/chats/triggered-when-a-chat-was-reassigned-to-a-workspace-member
webhook chat:responsible:assigned
Triggered when a chat is (re)assigned to a workspace member.
# Chat Unassigned
Source: https://timelines.ai/docs/webhook-reference/chats/triggered-when-a-chat-was-unassigned
webhook chat:responsible:unassigned
Triggered when a chat is unassigned from a workspace member.
# New Chat
Source: https://timelines.ai/docs/webhook-reference/chats/triggered-when-there-is-a-new-direct-or-group-chat
webhook chat:new
Triggered when there is a new direct or group chat.
# New Incoming Chat
Source: https://timelines.ai/docs/webhook-reference/chats/triggered-when-there-is-a-new-incoming-direct-chat
webhook chat:incoming:new
Triggered when there is a new incoming direct chat.
# New Outgoing Chat
Source: https://timelines.ai/docs/webhook-reference/chats/triggered-when-there-is-a-new-outgoing-direct-chat
webhook chat:outgoing:new
Triggered when there is a new outgoing direct chat.
# New Outgoing Message
Source: https://timelines.ai/docs/webhook-reference/messages/triggered-when-a-new-outgoing-message
webhook message:sent:new
Triggered when a new outgoing message is sent from a WhatsApp account in the workspace.
# Message Reaction
Source: https://timelines.ai/docs/webhook-reference/messages/triggered-when-a-whatsapp-message-reaction-is-set-or-cleared
webhook message:reaction
Triggered when a WhatsApp message reaction is set or cleared. The payload includes the full current reactions map for the message after the change.
# New Incoming Message
Source: https://timelines.ai/docs/webhook-reference/messages/triggered-when-there-is-a-new-incoming-message
webhook message:received:new
Triggered when there is a new incoming message received by a WhatsApp account in the workspace.
# New Message
Source: https://timelines.ai/docs/webhook-reference/messages/triggered-when-there-is-a-new-message
webhook message:new
Triggered when there is any new message (incoming or outgoing) on a WhatsApp account in the workspace.
# PublicAPI Webhooks Overview
Source: https://timelines.ai/docs/webhook-reference/overview
Receive real-time notifications for WhatsApp events in your workspace
# PublicAPI Webhooks
PublicAPI Webhooks deliver real-time notifications to your application when events occur in your TimelinesAI workspace. Instead of polling the API for changes, your server receives instant HTTP POST callbacks whenever messages arrive, chats are created, accounts change status, and more.
## How it works
Use `POST /webhooks` to subscribe to an event type and provide your endpoint URL
A new message arrives, a chat is created, or an account status changes
TimelinesAI sends the event payload to your endpoint in real-time
Your server responds with a 2xx status within 5 seconds
## Available events
### Message events
Real-time notifications for all message activity in your workspace.
| Event | Trigger | Description |
| ---------------------- | ----------------------- | ------------------------------------------------------- |
| `message:new` | Any new message | Fires for both incoming and outgoing messages |
| `message:received:new` | Incoming message | A customer or contact sends you a message |
| `message:sent:new` | Outgoing message | A message you sent is processed |
| `message:reaction` | Reaction set or cleared | An emoji reaction is added to or removed from a message |
### Chat events
Track conversation lifecycle and assignment changes.
| Event | Trigger | Description |
| ----------------------------- | ----------------- | ------------------------------------------- |
| `chat:new` | New chat (any) | A new direct or group chat appears |
| `chat:incoming:new` | New incoming chat | A contact initiates a conversation with you |
| `chat:outgoing:new` | New outgoing chat | You start a new conversation |
| `chat:responsible:assigned` | Chat reassigned | A chat is assigned to a team member |
| `chat:responsible:unassigned` | Chat unassigned | A chat's assignment is removed |
### Call events
Receive call history events for WhatsApp voice and video calls. Fired only after the call ends.
| Event | Trigger | Description |
| ---------------------- | -------------------- | ------------------------------------------------------------------------ |
| `call:incoming:missed` | Incoming call missed | The recipient never picked up before the caller ended |
| `call:incoming:ended` | Incoming call ended | Status `Answered` (call was picked up) or `Rejected` (actively declined) |
| `call:outgoing:ended` | Outgoing call ended | Status `Ended` (completed), `No Answer`, or `Rejected` |
### WhatsApp account events
Monitor the connection status of your WhatsApp numbers.
| Event | Trigger | Description |
| -------------------- | -------------------- | --------------------------------------------------- |
| Account reconnected | WhatsApp reconnects | A previously disconnected account comes back online |
| Account disconnected | WhatsApp disconnects | An account loses its connection |
| Syncing suspended | Syncing paused | Message syncing stops (e.g., subscription change) |
| Syncing resumed | Syncing restarted | Message syncing resumes after suspension |
## Use case examples
Build an automated responder that handles common questions instantly.
1. Subscribe to `message:received:new`
2. Parse the incoming message text for keywords
3. Send an appropriate reply using `POST /chats/{id}/messages`
4. Escalate complex queries by assigning the chat to a human agent
Keep your CRM updated the moment a conversation happens.
1. Subscribe to `message:received:new` and `chat:new`
2. On new chat — create a lead/contact in your CRM
3. On new message — log the interaction as an activity
4. Use chat assignment events to sync agent ownership
Alert your team in Slack, Teams, or email when important events happen.
1. Subscribe to `message:received:new` and `chat:responsible:assigned`
2. Filter events by label or content keywords
3. Forward the notification to Slack, Teams, or a custom dashboard
4. Monitor WhatsApp account events to alert admins about disconnections
Track message delivery rates and response times.
1. Subscribe to `message:sent:new` to track outgoing deliveries
2. Subscribe to `message:received:new` to measure response times
3. Aggregate data to build delivery rate dashboards
4. Alert on anomalies like high failure rates or unusual activity
## Webhook payload example
When a new message arrives, your endpoint receives a payload like:
```json theme={null}
{
"event_type": "message:received:new",
"chat": {
"full_name": "John Doe",
"chat_url": "https://app.timelines.ai/chat/123456/messages/",
"chat_id": 123456,
"is_group": false,
"phone": "+15551234567",
"responsible_name": "Agent Brown",
"responsible_email": "agent-brown@example.com"
},
"whatsapp_account": {
"full_name": "Agent Brown",
"email": "agent-brown@example.com",
"phone": "+15559876543"
},
"message": {
"text": "Hi, here is the signed contract",
"direction": "received",
"origin": "WhatsApp",
"timestamp": "2024-01-15 10:30:00 +0200",
"message_uid": "a5bbb005-37f2-402c-96fa-e479a2e09b02",
"reply_to_uid": "c7ec509d-0171-1ead-a84b-c6943a644768",
"sender": {
"full_name": "John Doe",
"phone": "+15551234567"
},
"recipient": {
"full_name": "Agent Brown",
"phone": "+15559876543"
},
"attachments": [
{
"temporary_download_url": "https://example.s3.amazonaws.com/att/...",
"filename": "contract.pdf",
"size": 1234567,
"mimetype": "application/pdf"
}
]
}
}
```
**Attachments** — The `temporary_download_url` for each attachment is a pre-signed URL valid for **15 minutes** from the moment the webhook is delivered. Download or forward attachment files immediately upon receiving the webhook. After expiration, you can retrieve the file again using the Public API.
**Reactions** — Emoji reactions on messages are delivered via the dedicated `message:reaction` event, not on `message:new`. The payload includes the full current reactions map for the message (emoji → integer count) along with the action (`set` or `clear`) and the reactor's identity.
## Endpoint requirements
Your webhook endpoint must:
Be publicly accessible (no localhost in production)
Use HTTPS
Respond with a 2xx status within 5 seconds
Accept POST requests with JSON body
## Retry policy
If your endpoint returns a non-2xx status or the request times out, TimelinesAI retries the delivery **2 additional times** (3 attempts total). If all attempts fail, the webhook's `errors_counter` increments.
## Best practices
* **Respond immediately** — return 200 first, then process the event asynchronously
* **Handle duplicates** — use `message_uid` to deduplicate events that may be delivered more than once
* **Monitor errors** — check `errors_counter` on your webhooks to catch delivery failures early
* **Use a queue** — for high-volume workspaces, push events to a message queue (Redis, SQS, RabbitMQ) and process separately
If your endpoint consistently fails, TimelinesAI will notify the workspace owner via email. Fix endpoint issues promptly to avoid missing events.
## Managing webhooks
Webhook subscriptions are managed via the Public API:
| Action | Endpoint |
| ----------- | ----------------------- |
| List all | `GET /webhooks` |
| Create | `POST /webhooks` |
| Get details | `GET /webhooks/{id}` |
| Update | `PUT /webhooks/{id}` |
| Delete | `DELETE /webhooks/{id}` |
See the [Webhooks guide](/docs/guides/webhooks) for setup instructions and code examples.
## Next steps
Step-by-step setup with code examples
Explore all API endpoints
# WABA API Webhooks Overview
Source: https://timelines.ai/docs/webhook-reference/waba-overview
Receive real-time notifications for WhatsApp Business Platform (WABA) events in your workspace
# WABA API Webhooks
WABA API Webhooks deliver real-time notifications when WhatsApp Business Platform events occur in your TimelinesAI workspace. They use the same subscription mechanism, delivery model, and retry policy as the regular [PublicAPI Webhooks](/docs/webhook-reference/overview) — the difference is the set of WABA-specific event types and payload shapes described below.
## How it works
Use `POST /webhooks` to subscribe to a WABA event type and provide your endpoint URL
A WABA message arrives, a WABA chat changes state, a template is reviewed by Meta, or an account status changes
TimelinesAI sends the event payload to your endpoint in real-time
Your server responds with a 2xx status within 5 seconds
## Available events
### WABA message events
| Event | Trigger | Description |
| ------------------------ | -------------------------- | ----------------------------------------------------------------------------- |
| `waba:message:received` | Incoming WABA message | A contact sends a message on a WABA account |
| `waba:message:delivered` | Outbound message delivered | An outbound WABA message is delivered to the contact |
| `waba:message:failed` | Outbound message failed | An outbound WABA message fails to send (includes structured `failure_reason`) |
| `waba:message:read` | Outbound message read | An outbound WABA message is read by the contact |
### WABA chat events
| Event | Trigger | Description |
| ---------------------- | ---------------------- | -------------------------------------------------------------------------------------- |
| `waba:chat:incoming` | New incoming WABA chat | A contact initiates a WABA conversation |
| `waba:chat:outgoing` | New outgoing WABA chat | You start a new WABA conversation |
| `waba:chat:assigned` | WABA chat (re)assigned | A WABA chat is assigned to a workspace member (payload carries the responsible member) |
| `waba:chat:unassigned` | WABA chat unassigned | A WABA chat's assignment is removed |
| `waba:chat:closed` | WABA chat closed | A WABA chat is closed |
| `waba:chat:reopened` | WABA chat reopened | A WABA chat is reopened |
### WABA account events
| Event | Trigger | Description |
| --------------------------- | -------------------- | ---------------------------------------- |
| `waba:account:active` | Account active | A WABA account becomes active |
| `waba:account:disabled` | Account disabled | A WABA account is disabled |
| `waba:account:disconnected` | Account disconnected | A WABA account is disconnected (removed) |
### WABA template events
| Event | Trigger | Description |
| ------------------------ | ----------------- | ------------------------------------------------------- |
| `waba:template:approved` | Template approved | A WABA message template is approved by Meta |
| `waba:template:rejected` | Template rejected | A WABA message template is rejected by Meta |
| `waba:template:disabled` | Template disabled | A WABA message template is disabled or pending deletion |
**Failed messages** — `waba:message:failed` carries a structured `failure_reason` object with `code`, `title`, and `details`. It is present only on the failed event and omitted for `received`, `delivered`, and `read`.
**Payload shapes** — WABA message webhook events bundle the account, chat, and message objects together in a single payload. Each event group has its own schema — see the individual event pages for the exact fields.
## Endpoint requirements
Your webhook endpoint must:
Be publicly accessible (no localhost in production)
Use HTTPS
Respond with a 2xx status within 5 seconds
Accept POST requests with JSON body
## Managing webhooks
WABA webhook subscriptions are managed via the same Public API endpoints as regular webhooks:
| Action | Endpoint |
| ----------- | ----------------------- |
| List all | `GET /webhooks` |
| Create | `POST /webhooks` |
| Get details | `GET /webhooks/{id}` |
| Update | `PUT /webhooks/{id}` |
| Delete | `DELETE /webhooks/{id}` |
See the [Webhooks guide](/docs/guides/webhooks) for setup instructions and code examples.
## Next steps
Explore all WABA API endpoints
Regular WhatsApp webhook events
# WABA Account Active
Source: https://timelines.ai/docs/webhook-reference/waba_accounts/triggered-when-a-waba-account-becomes-active
webhook waba:account:active
Triggered when a WABA account becomes active in the workspace.
# WABA Account Disabled
Source: https://timelines.ai/docs/webhook-reference/waba_accounts/triggered-when-a-waba-account-is-disabled
webhook waba:account:disabled
Triggered when a WABA account is disabled.
# WABA Account Disconnected
Source: https://timelines.ai/docs/webhook-reference/waba_accounts/triggered-when-a-waba-account-is-disconnected
webhook waba:account:disconnected
Triggered when a WABA account is disconnected (removed) from the workspace.
# WABA Chat Closed
Source: https://timelines.ai/docs/webhook-reference/waba_chats/triggered-when-a-waba-chat-is-closed
webhook waba:chat:closed
Triggered when a WABA chat is closed.
# WABA Chat Assigned
Source: https://timelines.ai/docs/webhook-reference/waba_chats/triggered-when-a-waba-chat-is-reassigned-to-a-workspace-member
webhook waba:chat:assigned
Triggered when a WABA chat is assigned or reassigned to a workspace member.
# WABA Chat Reopened
Source: https://timelines.ai/docs/webhook-reference/waba_chats/triggered-when-a-waba-chat-is-reopened
webhook waba:chat:reopened
Triggered when a WABA chat is reopened.
# WABA Chat Unassigned
Source: https://timelines.ai/docs/webhook-reference/waba_chats/triggered-when-a-waba-chat-is-unassigned
webhook waba:chat:unassigned
Triggered when a WABA chat is unassigned from a workspace member.
# New Incoming WABA Chat
Source: https://timelines.ai/docs/webhook-reference/waba_chats/triggered-when-there-is-a-new-incoming-waba-chat
webhook waba:chat:incoming
Triggered when there is a new incoming WABA chat on a WABA account in the workspace.
# New Outgoing WABA Chat
Source: https://timelines.ai/docs/webhook-reference/waba_chats/triggered-when-there-is-a-new-outgoing-waba-chat
webhook waba:chat:outgoing
Triggered when there is a new outgoing WABA chat on a WABA account in the workspace.
# New Incoming WABA Message
Source: https://timelines.ai/docs/webhook-reference/waba_messages/triggered-when-a-new-waba-message-is-received
webhook waba:message:received
Triggered when a new WABA message is received from a contact on a WABA account in the workspace.
# WABA Message Failed
Source: https://timelines.ai/docs/webhook-reference/waba_messages/triggered-when-an-outbound-waba-message-fails-to-send
webhook waba:message:failed
Triggered when an outbound WABA message fails to send. The payload includes a structured failure_reason with code, title, and details.
# WABA Message Delivered
Source: https://timelines.ai/docs/webhook-reference/waba_messages/triggered-when-an-outbound-waba-message-is-delivered
webhook waba:message:delivered
Triggered when an outbound WABA message is delivered to the contact.
# WABA Message Read
Source: https://timelines.ai/docs/webhook-reference/waba_messages/triggered-when-an-outbound-waba-message-is-read
webhook waba:message:read
Triggered when an outbound WABA message is read by the contact.
# WABA Template Approved
Source: https://timelines.ai/docs/webhook-reference/waba_templates/triggered-when-a-waba-template-is-approved
webhook waba:template:approved
Triggered when a WABA message template is approved by Meta.
# WABA Template Disabled
Source: https://timelines.ai/docs/webhook-reference/waba_templates/triggered-when-a-waba-template-is-disabled
webhook waba:template:disabled
Triggered when a WABA message template is disabled or pending deletion.
# WABA Template Rejected
Source: https://timelines.ai/docs/webhook-reference/waba_templates/triggered-when-a-waba-template-is-rejected
webhook waba:template:rejected
Triggered when a WABA message template is rejected by Meta.
# Message Sync Resumed
Source: https://timelines.ai/docs/webhook-reference/whatsapp_accounts/triggered-when-a-whatsapp-account-message-syncing-is-resumed
webhook whatsapp:account:resumed
Triggered when a WhatsApp account message syncing is resumed.
# Message Sync Suspended
Source: https://timelines.ai/docs/webhook-reference/whatsapp_accounts/triggered-when-a-whatsapp-account-message-syncing-is-suspended-for-a-reason-such-as-subscription-cancellation-or-downgrade
webhook whatsapp:account:suspended
Triggered when a WhatsApp account message syncing is suspended due to subscription cancellation or downgrade.
# Account Disconnected
Source: https://timelines.ai/docs/webhook-reference/whatsapp_accounts/triggered-when-whatsapp-account-was-disconnected
webhook whatsapp:account:disconnected
Triggered when a WhatsApp account is permanently disconnected from the workspace.
# Account Reconnected
Source: https://timelines.ai/docs/webhook-reference/whatsapp_accounts/triggered-when-whatsapp-account-was-reconnected
webhook whatsapp:account:connected
Triggered when a WhatsApp account is (re)connected to the workspace.