Getting Started
Wataki is a REST API for WhatsApp. Create an account, connect a WhatsApp instance, and start sending messages in minutes.
1. Create an account
Sign up at wataki.cloud/auth/signup and verify your email to access the dashboard.
2. Create an API key
Go to Settings and create an API key. Copy it immediately — it's only shown once.
3. Create an instance
curl -X POST https://api.wataki.cloud/v1/instances \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "my-bot"}'4. Connect via QR code
curl -X POST https://api.wataki.cloud/v1/instances/INSTANCE_ID/connect \
-H "X-API-Key: YOUR_API_KEY"The response includes a qr string and qr_image_data_url. Open the data URL in a browser or render the QR string with any QR library. Scan it with WhatsApp on your phone.
5. Send a message
curl -X POST https://api.wataki.cloud/v1/instances/INSTANCE_ID/messages \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"chat_id": "5511999999999@s.whatsapp.net",
"type": "text",
"content": { "text": "Hello from Wataki!" }
}'That's it. You're sending messages. Set up webhooks to receive replies.
Authentication
All API requests (except public endpoints) require authentication via the X-API-Key header:
curl https://api.wataki.cloud/v1/instances \
-H "X-API-Key: YOUR_API_KEY"The dashboard uses cookie-based sessions (magic link login). API keys and session cookies are interchangeable for all authenticated endpoints.
Errors
All errors return a consistent JSON shape:
{
"error": {
"code": "validation_error",
"message": "name is required",
"details": {}
}
}| Status | Meaning |
|---|---|
| 400 | Invalid request body or parameters |
| 401 | Missing or invalid authentication |
| 402 | Message quota exceeded |
| 403 | Account not active |
| 404 | Resource not found |
| 409 | Conflict (duplicate / limit exceeded) |
| 429 | Rate limited |
| 500 | Internal server error |
Pagination
List endpoints support cursor-based pagination with limit (max 200, default 50) and cursor query parameters. The response includes a page.next_cursor field — pass it as cursor to get the next page. When next_cursor is null, there are no more results.
GET /v1/instances?limit=10
GET /v1/instances?limit=10&cursor=MTI=Auth
POST/v1/auth/signupCreate a new account
/v1/auth/signupCreate a new accountRequest body
{
"name": "Acme",
"email": "dev@acme.com"
}Response
{
"message": "Check your email for a sign-in link."
}POST/v1/auth/loginSend a sign-in link
/v1/auth/loginSend a sign-in linkRequest body
{
"email": "dev@acme.com"
}Response
{
"message": "Check your email for a sign-in link."
}POST/v1/auth/verifyVerify a magic link token
/v1/auth/verifyVerify a magic link tokenRequest body
{
"token": "abc123..."
}Response
{
"tenant": {
"id": "...",
"name": "Acme",
"email": "dev@acme.com",
"plan": "free"
}
}Sets a wataki_session cookie on success.
POST/v1/auth/logoutClear session cookie
/v1/auth/logoutClear session cookieResponse
{
"message": "Logged out"
}GET/v1/auth/meGet current tenant
/v1/auth/meGet current tenantResponse
{
"id": "abc123",
"name": "Acme",
"email": "dev@acme.com",
"plan": "free",
"status": "active",
"created_at": "2025-01-01T00:00:00.000Z"
}POST/v1/auth/api-keysCreate an API key
/v1/auth/api-keysCreate an API keyRequest body
{
"name": "production"
}Response
{
"id": "key_abc",
"api_key": "64-char-hex-key-shown-once",
"key_prefix": "64charhe",
"name": "production",
"created_at": "2025-01-01T00:00:00.000Z"
}The raw api_key is only returned once. Store it securely.
GET/v1/auth/api-keysList API keys
/v1/auth/api-keysList API keysResponse
{
"data": [
{
"id": "key_abc",
"key_prefix": "64charhe",
"name": "production",
"created_at": "2025-01-01T00:00:00.000Z",
"last_used_at": "2025-01-15T12:00:00.000Z"
}
]
}DELETE/v1/auth/api-keys/{key_id}Revoke an API key
/v1/auth/api-keys/{key_id}Revoke an API keyReturns 204 on success. The key stops working immediately.
Instances
An instance represents a single WhatsApp connection. Create an instance, connect it via QR code, then send and receive messages.
GET/v1/instancesList instances
/v1/instancesList instancesParameters
?limit=50&cursor=...Response
{
"data": [
{
"id": "inst_abc",
"name": "my-bot",
"status": {
"state": "connected",
"last_error": null
},
"phone_number": "+5511999999999",
"created_at": "..."
}
],
"page": {
"next_cursor": null
}
}POST/v1/instancesCreate an instance
/v1/instancesCreate an instanceRequest body
{
"name": "my-bot",
"description": "Customer support bot",
"config": {
"auto_reconnect": true,
"download_media": false,
"emit_raw": false
}
}Only name is required. Config fields default to sensible values.
GET/v1/instances/{instance_id}Get an instance
/v1/instances/{instance_id}Get an instancePATCH/v1/instances/{instance_id}Update an instance
/v1/instances/{instance_id}Update an instanceRequest body
{
"name": "renamed-bot",
"config": {
"download_media": true
}
}DELETE/v1/instances/{instance_id}Delete an instance
/v1/instances/{instance_id}Delete an instanceDisconnects the WhatsApp session and deletes all associated data. Returns 204.
POST/v1/instances/{instance_id}/connectConnect (get QR code)
/v1/instances/{instance_id}/connectConnect (get QR code)Response
{
"status": {
"state": "qr_required"
},
"qr": "2@abc...",
"qr_image_data_url": "data:image/png;base64,..."
}If already connected, returns state "connected" with no QR. Poll this endpoint to refresh the QR code.
GET/v1/instances/{instance_id}/statusGet connection status
/v1/instances/{instance_id}/statusGet connection statusResponse
{
"state": "connected",
"last_error": null,
"updated_at": "..."
}States: disconnected, connecting, qr_required, connected, reconnecting, logged_out, error
POST/v1/instances/{instance_id}/disconnectDisconnect session
/v1/instances/{instance_id}/disconnectDisconnect sessionMessages
POST/v1/instances/{instance_id}/messagesSend a message
/v1/instances/{instance_id}/messagesSend a messageRequest body
{
"chat_id": "5511999999999@s.whatsapp.net",
"type": "text",
"content": {
"text": "Hello!"
}
}Response
{
"id": "msg_abc",
"chat_id": "5511999999999@s.whatsapp.net",
"direction": "outbound",
"type": "text",
"status": "sent",
"timestamp": "..."
}Returns 202 Accepted. Supports Idempotency-Key header for safe retries. Returns X-Quota-Used and X-Quota-Remaining headers.
Message types
| Type | Content fields |
|---|---|
text | { "text": "..." } |
image | { "media": { "media_id": "..." }, "caption": "..." } |
video | { "media": { "media_id": "..." }, "caption": "..." } |
audio | { "media": { "media_id": "..." }, "ptt": true } |
document | { "media": { "media_id": "..." }, "filename": "..." } |
location | { "latitude": -23.5, "longitude": -46.6 } |
contacts | { "contacts": [{ "name": "...", "phone": "..." }] } |
GET/v1/instances/{instance_id}/messagesList messages
/v1/instances/{instance_id}/messagesList messagesParameters
?chat_id=5511...@s.whatsapp.net&limit=50&cursor=...chat_id query parameter is required.
GET/v1/instances/{instance_id}/messages/{message_id}Get a message
/v1/instances/{instance_id}/messages/{message_id}Get a messagePOST/v1/instances/{instance_id}/messages/{message_id}/reactReact to a message
/v1/instances/{instance_id}/messages/{message_id}/reactReact to a messageRequest body
{
"emoji": "👍"
}POST/v1/instances/{instance_id}/messages/{message_id}/readMark as read
/v1/instances/{instance_id}/messages/{message_id}/readMark as readSends a read receipt. Returns 202.
POST/v1/instances/{instance_id}/presenceUpdate typing presence
/v1/instances/{instance_id}/presenceUpdate typing presenceRequest body
{
"chat_id": "5511...@s.whatsapp.net",
"state": "composing"
}States: composing, paused, recording
Media
Upload files before sending them as image, video, audio, or document messages.
POST/v1/instances/{instance_id}/mediaUpload media
/v1/instances/{instance_id}/mediaUpload mediaResponse
{
"id": "med_abc",
"mime_type": "image/jpeg",
"size_bytes": 245000,
"url": "https://api.wataki.cloud/v1/instances/.../media/med_abc/content",
"created_at": "..."
}Send as multipart/form-data with a "file" field. Max 15 MB by default. Returns a media_id to use in message content.
GET/v1/instances/{instance_id}/media/{media_id}Get media metadata
/v1/instances/{instance_id}/media/{media_id}Get media metadataGET/v1/instances/{instance_id}/media/{media_id}/contentDownload media file
/v1/instances/{instance_id}/media/{media_id}/contentDownload media fileReturns the raw file with appropriate Content-Type header.
Webhooks
Register webhook endpoints on an instance to receive real-time events (incoming messages, delivery status, connection changes).
GET/v1/instances/{instance_id}/webhooksList webhooks
/v1/instances/{instance_id}/webhooksList webhooksPOST/v1/instances/{instance_id}/webhooksCreate a webhook
/v1/instances/{instance_id}/webhooksCreate a webhookRequest body
{
"url": "https://your-server.com/webhook",
"events": ["message.received", "message.status", "connection.update"],
"secret": "optional-hmac-secret"
}URL must be HTTPS (or HTTP in dev). Localhost and private IPs are blocked. Max 10 webhooks per instance.
PATCH/v1/instances/{instance_id}/webhooks/{webhook_id}Update a webhook
/v1/instances/{instance_id}/webhooks/{webhook_id}Update a webhookRequest body
{
"events": ["message.received"],
"active": false
}DELETE/v1/instances/{instance_id}/webhooks/{webhook_id}Delete a webhook
/v1/instances/{instance_id}/webhooks/{webhook_id}Delete a webhookChats & Groups
GET/v1/instances/{instance_id}/chatsList chats
/v1/instances/{instance_id}/chatsList chatsParameters
?limit=50&cursor=...Response
{
"data": [
{
"id": "5511...@s.whatsapp.net",
"name": "John",
"is_group": false
}
],
"page": {
"next_cursor": null
}
}GET/v1/instances/{instance_id}/chats/{chat_id}Get chat details
/v1/instances/{instance_id}/chats/{chat_id}Get chat detailsGET/v1/instances/{instance_id}/groupsList groups
/v1/instances/{instance_id}/groupsList groupsResponse
{
"data": [
{
"id": "120...@g.us",
"name": "Team Chat",
"participant_count": 12
}
],
"page": {
"next_cursor": null
}
}Billing
GET/v1/billing/plansList plans
/v1/billing/plansList plansResponse
{
"data": [
{
"id": "free",
"name": "Free",
"price_cents": 0,
"included_messages": 100
},
{
"id": "growth",
"name": "Growth",
"price_cents": 2900,
"included_messages": 5000
},
{
"id": "scale",
"name": "Scale",
"price_cents": 9900,
"included_messages": 25000
}
]
}GET/v1/billing/usageGet current usage
/v1/billing/usageGet current usageResponse
{
"plan": {
"id": "free",
"name": "Free",
"price_cents": 0,
"included_messages": 100
},
"billing_cycle": {
"start": "...",
"end": "...",
"days_remaining": 22
},
"usage": {
"sent": 42,
"included": 100,
"remaining": 58
}
}POST/v1/billing/upgradeUpgrade plan
/v1/billing/upgradeUpgrade planRequest body
{
"plan": "growth"
}Response
{
"plan": "growth",
"checkout_url": "https://checkout...",
"session_id": "..."
}Returns a checkout URL to redirect the user to for payment.
Observability
All observability endpoints accept optional since, until (ISO 8601), and bucket (hour/day) query parameters.
GET/v1/observability/overviewCombined overview
/v1/observability/overviewCombined overviewReturns messages, instance health, webhook delivery stats, and API usage in a single response.
GET/v1/observability/messagesMessage stats + time series
/v1/observability/messagesMessage stats + time seriesParameters
?since=2025-01-01T00:00:00Z&bucket=day&instance_id=...GET/v1/observability/webhooksWebhook delivery stats + recent failures
/v1/observability/webhooksWebhook delivery stats + recent failuresGET/v1/observability/api-usageAPI request stats + time series
/v1/observability/api-usageAPI request stats + time seriesGET/v1/observability/instancesPer-instance health summary
/v1/observability/instancesPer-instance health summaryGET/v1/observability/errorsError summary across all categories
/v1/observability/errorsError summary across all categoriesWebhook Events
When events occur, Wataki sends a POST to your registered webhook URLs with this envelope:
{
"id": "evt_abc",
"event": "message.received",
"instance_id": "inst_abc",
"timestamp": "2025-01-01T00:00:00.000Z",
"data": { ... }
}Event types
| Event | Description |
|---|---|
message.received | New inbound message |
message.status | Delivery status update (sent, delivered, read, failed) |
message.reaction | Reaction received on a message |
message.read | Read receipt from recipient |
connection.update | Instance connection state changed |
qr.updated | New QR code generated for scanning |
If you provided a secret when creating the webhook, Wataki includes an X-Webhook-Signature header (HMAC-SHA256 of the raw body). Verify it to ensure authenticity.
Wataki retries failed deliveries up to 3 times with exponential backoff. Return a 2xx status to acknowledge receipt.
Rate Limits
Default: 60 requests per minute per tenant. Rate limit headers are included in every response:
| Header | Description |
|---|---|
X-RateLimit-Limit | Max requests per window |
X-RateLimit-Remaining | Remaining requests in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
When rate limited, the API returns 429 Too Many Requests. Wait until the reset time before retrying.
Base URL: https://api.wataki.cloud
Need help? Email support@wataki.cloud