Wataki API Reference

v1
Get API Key

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": {}
  }
}
StatusMeaning
400Invalid request body or parameters
401Missing or invalid authentication
402Message quota exceeded
403Account not active
404Resource not found
409Conflict (duplicate / limit exceeded)
429Rate limited
500Internal 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

Request 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

Request body

{
  "email": "dev@acme.com"
}

Response

{
  "message": "Check your email for a sign-in link."
}
POST
/v1/auth/verifyVerify a magic link token

Request 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
Requires authentication

Response

{
  "message": "Logged out"
}
GET
/v1/auth/meGet current tenant
Requires authentication

Response

{
  "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
Requires authentication

Request 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
Requires authentication

Response

{
  "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
Requires authentication

Returns 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
Requires authentication

Parameters

?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
Requires authentication

Request 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
Requires authentication
PATCH
/v1/instances/{instance_id}Update an instance
Requires authentication

Request body

{
  "name": "renamed-bot",
  "config": {
    "download_media": true
  }
}
DELETE
/v1/instances/{instance_id}Delete an instance
Requires authentication

Disconnects the WhatsApp session and deletes all associated data. Returns 204.

POST
/v1/instances/{instance_id}/connectConnect (get QR code)
Requires authentication

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
Requires authentication

Response

{
  "state": "connected",
  "last_error": null,
  "updated_at": "..."
}

States: disconnected, connecting, qr_required, connected, reconnecting, logged_out, error

POST
/v1/instances/{instance_id}/disconnectDisconnect session
Requires authentication

Messages

POST
/v1/instances/{instance_id}/messagesSend a message
Requires authentication

Request 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

TypeContent 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
Requires authentication

Parameters

?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
Requires authentication
POST
/v1/instances/{instance_id}/messages/{message_id}/reactReact to a message
Requires authentication

Request body

{
  "emoji": "👍"
}
POST
/v1/instances/{instance_id}/messages/{message_id}/readMark as read
Requires authentication

Sends a read receipt. Returns 202.

POST
/v1/instances/{instance_id}/presenceUpdate typing presence
Requires authentication

Request 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
Requires authentication

Response

{
  "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
Requires authentication
GET
/v1/instances/{instance_id}/media/{media_id}/contentDownload media file
Requires authentication

Returns 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
Requires authentication
POST
/v1/instances/{instance_id}/webhooksCreate a webhook
Requires authentication

Request 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
Requires authentication

Request body

{
  "events": ["message.received"],
  "active": false
}
DELETE
/v1/instances/{instance_id}/webhooks/{webhook_id}Delete a webhook
Requires authentication

Chats & Groups

GET
/v1/instances/{instance_id}/chatsList chats
Requires authentication

Parameters

?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
Requires authentication
GET
/v1/instances/{instance_id}/groupsList groups
Requires authentication

Response

{
  "data": [
    {
      "id": "120...@g.us",
      "name": "Team Chat",
      "participant_count": 12
    }
  ],
  "page": {
    "next_cursor": null
  }
}

Billing

GET
/v1/billing/plansList plans

Response

{
  "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
Requires authentication

Response

{
  "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
Requires authentication

Request 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
Requires authentication

Returns messages, instance health, webhook delivery stats, and API usage in a single response.

GET
/v1/observability/messagesMessage stats + time series
Requires authentication

Parameters

?since=2025-01-01T00:00:00Z&bucket=day&instance_id=...
GET
/v1/observability/webhooksWebhook delivery stats + recent failures
Requires authentication
GET
/v1/observability/api-usageAPI request stats + time series
Requires authentication
GET
/v1/observability/instancesPer-instance health summary
Requires authentication
GET
/v1/observability/errorsError summary across all categories
Requires authentication

Webhook 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

EventDescription
message.receivedNew inbound message
message.statusDelivery status update (sent, delivered, read, failed)
message.reactionReaction received on a message
message.readRead receipt from recipient
connection.updateInstance connection state changed
qr.updatedNew 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:

HeaderDescription
X-RateLimit-LimitMax requests per window
X-RateLimit-RemainingRemaining requests in current window
X-RateLimit-ResetUnix 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