API v2 (бета)
API v2
API v2 — новый REST API WbizTool: отдельный ключ для каждого приложения ровно с теми правами, которые ему нужны, URL в стиле ресурсов, настоящие коды состояния HTTP, подписанные вебхуки JSON и безопасные повторы запросов. На этой странице описано то, что общее для всех запросов. Каждый endpoint описан на страницах ресурсов.
Базовый URL#
https://wbiztool.com/api/v2
У путей нет завершающей косой черты (/api/v2/messages), но и с ней запрос тоже принимается. Адрес, которого не существует, возвращает 404 с кодом not_found.
Быстрый пример#
Проверьте ключ запросом GET /me. Он работает с любой областью доступа и ничего не отправляет:
curl https://wbiztool.com/api/v2/me \
-H "Authorization: Bearer $WBIZTOOL_API_KEY"import os
import requests
response = requests.get(
"https://wbiztool.com/api/v2/me",
headers={"Authorization": f"Bearer {os.environ['WBIZTOOL_API_KEY']}"},
timeout=30,
)
print(response.status_code, response.json())// Node.js 18+ (built-in fetch). Save as .mjs to use top-level await.
const response = await fetch("https://wbiztool.com/api/v2/me", {
headers: { Authorization: `Bearer ${process.env.WBIZTOOL_API_KEY}` },
});
console.log(response.status, await response.json());<?php
$ch = curl_init('https://wbiztool.com/api/v2/me');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('WBIZTOOL_API_KEY')],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = json_decode(curl_exec($ch), true);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n";
print_r($body);
curl_close($ch);{
"workspace": { "id": 5012, "name": "Acme Bakery" },
"key": {
"id": 7,
"name": "Order system",
"mode": "live",
"scopes": ["messages:send", "messages:read"],
"limited_to_numbers": false,
"number_ids": [],
"expires_at": null
},
"plan": { "name": "Starter" },
"limits": {
"requests_per_minute": 60,
"requests_per_day": 10000,
"messages_per_number_per_day": 1000,
"messages_remaining": null
}
}
messages_remaining равен null, если тариф даёт безлимитные сообщения. plan равен null, если у рабочего пространства нет активного тарифа.
Ключи#
Передавайте ключ в заголовке Authorization в каждом запросе:
Authorization: Bearer wbz_live_…
client_id не нужен: ключ сам указывает на своё рабочее пространство. Ключ v2 начинается с wbz_live_ или wbz_test_, за которыми идут 38 букв и цифр. Ключи v1 не работают в v2 (они получают v1_key_on_v2), а ключи v2 не работают в v1.
Создание ключа#
Владельцы создают ключи на странице Настройки → API-ключи, в карточке API-ключи v2: нажмите Создать ключ v2 и выберите:
- Название: приложение, которое будет им пользоваться, до 50 символов.
- Тип ключа: Рабочий отправляет настоящие сообщения; Тестовый всё проверяет и ничего не отправляет (см. Тестовый режим).
- Что он может делать: области доступа или Всё.
- Какие номера он может использовать: все номера, включая добавленные позже, или только некоторые (см. Ключи только для некоторых номеров).
- Истекает: никогда или через 30 дней, 90 дней либо 1 год.
Ключ показывается один раз. Сразу скопируйте его и сохраните на своём сервере, например в переменной окружения. WbizTool хранит только его хеш и последние четыре символа, поэтому потерянный ключ нельзя показать снова: отзовите его и создайте новый.
В рабочем пространстве может быть до 10 неотозванных ключей v2. В карточке у каждого ключа показаны последние четыре символа, области доступа, номера, срок действия, а также когда и с какого IP-адреса он использовался в последний раз. Отозвать сразу останавливает ключ, а вместе с ним и созданные им webhook endpoint. Ключ с истёкшим сроком действия получает api_key_expired.
Области доступа#
Каждый ключ может делать только то, что разрешают его области доступа (scopes). Запрос за их пределами возвращает 403 с missing_scope, а required_scope в ошибке говорит, какая область нужна.
| Область | Что она разрешает |
|---|---|
messages:send | Отправлять сообщения и пакеты, отменять ожидающие сообщения: POST /messages, POST /messages/batch, DELETE /messages/{id} |
messages:read | Читать сообщения и их статус (GET /messages, GET /messages/{id}) и управлять webhook endpoint |
reminders | Создавать, просматривать, изменять, ставить на паузу и удалять напоминания |
numbers | Просматривать номера, их подключение и подключать номера WhatsApp |
media | Загружать, просматривать и удалять файлы в Медиакаталоге |
contacts | Проверки номеров (endpoint для контактов появятся позже) |
GET /me не требует особой области доступа. Для отправки сохранённому контакту через to.contact_id нужна только messages:send.
Ключи только для некоторых номеров#
Ключ, ограниченный некоторыми номерами, работает только с ними:
- Он отправляет только с этих номеров и просматривает, читает и отменяет только их сообщения. Другой номер в
fromполучаетnumber_not_allowed. GET /numbersпоказывает только эти номера. Ключ может переподключать их, но не может добавить новый номер.- Он видит и изменяет только напоминания, которые отправляются с этих номеров, а новое напоминание должно указывать один из них в
from. - Он вообще не может управлять webhook endpoint, потому что они получают события всего рабочего пространства.
GET /me показывает limited_to_numbers и number_ids, которые ключ может использовать.
Запросы#
- JSON на входе и на выходе. Передавайте тело как
Content-Type: application/json. Единственное исключение — загрузка файла, тамmultipart/form-data. Каждый ответ — JSON, включая ошибки. - Имена полей в
snake_case, а неизвестные поля отклоняются: опечатка вродеtxtвозвращаетvalidation_failed, а не игнорируется. - Номера телефонов — в международном формате с
+(E.164), например+919876543210. Пробелы и дефисы допустимы; номер без+отклоняется сinvalid_phone. - Время — в ISO 8601 со смещением, например
2026-10-20T09:30:00+05:30или2026-10-20T04:00:00Z. Время без смещения отклоняется сinvalid_datetime. Время в ответах указывается в UTC и заканчивается наZ, кромеnext_run_atнапоминания: оно в часовом поясе напоминания. - ID — целые числа, например
idномера (fromпри отправке) илиidсообщения. - Коды состояния значат то, что написано:
200OK,201создано,202принято (в очереди),400запрос неверен,401ключ неверен,402закончились сообщения,403не разрешено,404не найдено,409конфликт,413слишком большой,415неверный тип содержимого,422значение нельзя использовать,429слишком много запросов,5xxпроблема на нашей стороне.
Ответы содержат Cache-Control: no-store.
Ошибки#
У каждой ошибки одинаковый формат:
{
"error": {
"code": "invalid_phone",
"message": "This is not a valid phone number. Write it in international format with +, e.g. +919876543210.",
"field": "to.phone",
"docs": "https://wbiztool.com/docs/api/v2/errors/#invalid_phone",
"request_id": "req_4f1c2a9b8e7d6c5b4a392817"
}
}
codeне меняется: опирайтесь на него в своём коде.message— текст на английском для разработчика, он может меняться.fieldназывает поле, к которому относится ошибка (to.phone,to[3].phone,schedule.cron), или равенnull.docsведёт к описанию кода на странице ошибок.request_idсовпадает с заголовкомX-Request-Id. Укажите его, если обращаетесь к нам.- Некоторые ошибки добавляют подробности, например
statusуnot_cancellableилиusedиlimitуstorage_full. Если в запросе несколько проблем,errorsперечисляет остальные, каждую сfieldиmessage.
Все коды, их статусы HTTP и что делать, описаны на странице ошибок.
Idempotency-Key#
После сетевой ошибки или тайм-аута непонятно, сработал ли запрос. Передайте заголовок Idempotency-Key с запросом POST, который что-то создаёт, и его можно будет безопасно отправить ещё раз:
curl -X POST https://wbiztool.com/api/v2/messages \
-H "Authorization: Bearer $WBIZTOOL_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042-ready" \
-d '{"to": {"phone": "+919876543210"}, "text": "Hi Asha, your order #1042 is ready."}'
- Значение выбираете вы: любая уникальная строка из 1–255 печатаемых символов, например UUID или ваш собственный номер заказа. Создавайте новое значение для каждого нового запроса и используйте его повторно только для повтора того же запроса.
- Повтор получает первый ответ с заголовком
Idempotent-Replayed: true. Ничего не выполняется дважды: ни второго сообщения, ни напоминания, ни файла, ни endpoint. - Ключи живут 24 часа с первого запроса и принадлежат вашему API-ключу: другой API-ключ рабочего пространства может использовать то же значение без конфликта.
- Тот же ключ, другой запрос: запрос с ключом, который уже использовался для другого метода, пути или тела, возвращает
422idempotency_key_reused. - Ещё выполняется: пока первый запрос с этим ключом ещё обрабатывается, повтор возвращает
409idempotency_in_progressсRetry-After: 1. Подождите немного и отправьте его снова. - Ошибки не сохраняются. Если первый запрос вернул ошибку, ничего не сохраняется: исправьте запрос и отправьте его снова с тем же ключом.
- Секреты не повторяются. Повтор
POST /webhooksне содержитsecretendpoint: он показывается только в первом ответе.
Это работает для POST /messages, POST /messages/batch, POST /numbers, POST /reminders, POST /media, POST /number-checks и POST /webhooks и везде необязательно. Другие запросы игнорируют этот заголовок. Пустое или слишком длинное значение возвращает 400 invalid_idempotency_key.
Лимиты запросов#
Каждый ключ может делать 60 запросов в минуту и 10 000 запросов в день. Окна фиксированные: минутное окно начинается с начала минуты по часам, суточное — в 00:00 UTC. При превышении лимита запрос возвращает 429 rate_limited.
Ответы на запросы с действительным ключом содержат такие заголовки:
| Заголовок | Значение |
|---|---|
RateLimit-Limit | Сколько запросов разрешено в текущем окне (60 или 10000, когда достигнут суточный лимит) |
RateLimit-Remaining | Сколько запросов осталось в этом окне |
RateLimit-Reset | Через сколько секунд это окно начнётся заново |
RateLimit-Policy | Оба лимита: 60;w=60, 10000;w=86400 |
Retry-After | При 429: сколько секунд подождать перед новой попыткой |
Отдельно от этого:
- Неверные ключи: больше 30 запросов в минуту с неверным ключом с одного IP-адреса возвращают
429too_many_auth_failuresсRetry-After: 60. - Сообщения на номер: номер отправляет не больше 1000 сообщений за 24 часа (честное использование). Отправка сверх этого возвращает
429number_daily_cap_reached.
Пагинация#
Списки, которые могут расти (GET /messages, /reminders, /media, /number-checks и /results проверки), приходят страницами, сначала самые новые:
{
"data": [ … ],
"next_cursor": "eyJpZCI6OTgxNzI2MywidCI6IjIwMjYtMTAtMThUMTA6MTU6MDIuMzExWiIsImYiOiI0NGIxMzZmYTM1NWIifQ"
}
limitзадаёт размер страницы, от 1 до 100 (по умолчанию 25).- Чтобы получить следующую страницу, отправьте тот же запрос с
cursor, равнымnext_cursorпредыдущей страницы. На последней страницеnext_cursorравенnull. - Не меняйте остальные фильтры: курсор с другими фильтрами возвращает
400invalid_cursor. Начните заново без курсора. - Считайте курсор непрозрачной строкой. Элементы, добавленные, пока вы листаете страницы, не сдвигают страницы.
curl "https://wbiztool.com/api/v2/messages?limit=100&cursor=eyJpZCI6OTgxNzI2MywidCI6..." \
-H "Authorization: Bearer $WBIZTOOL_API_KEY"
GET /numbers и GET /webhooks возвращают всё одним списком data.
ID запросов#
У каждого ответа, включая ошибки, есть заголовок X-Request-Id, например req_4f1c2a9b8e7d6c5b4a392817. В ошибках то же значение приходит как request_id. Записывайте его в лог и указывайте, если обращаетесь к нам: по нему мы найдём запрос в наших логах, где записан ID вашего ключа, но никогда не сам ключ.
Если вы передаёте собственный X-Request-Id (до 100 печатаемых символов), он возвращается как X-Client-Request-Id, чтобы вы могли сопоставить ответы со своими логами.
Тестовый режим#
Тестовый ключ (wbz_test_…) позволяет создавать и проверять интеграцию, ничего не отправляя. Создайте его на странице Настройки → API-ключи с Тип ключа: Тестовый. GET /me показывает "mode": "test".
Что тестовый ключ делает так же, как рабочий:
POST /messagesиPOST /messages/batchпроверяют всё, что проверяет настоящая отправка: получателя, текст, медиафайл,send_atиexpires_at, номер отправителя, области доступа и номера ключа. Одни и те же ошибки дают одни и те же коды.- Области доступа, номера, лимиты запросов и
Idempotency-Keyработают так же. - Любое чтение остальных данных рабочего пространства (
GET /me,/numbers,/reminders,/media,/number-checks,/webhooks) показывает реальные данные, только для чтения.
Чем он отличается:
- Ничего не отправляется. Тестовая отправка сохраняется как тестовое сообщение и не расходует ни сообщения тарифа, ни суточные 1000 номера, поэтому никогда не получает
no_creditsилиnumber_daily_cap_reached. - Его статус меняется сам:
queued, затемsentчерез 5 секунд иdeliveredчерез 15 секунд после создания (для запланированного — считая отsend_at). Сообщение, у которогоexpires_atнаступает раньше отправки, становитсяexpired. Тестовые сообщения никогда не становятсяreadилиfailed. - Тестовый ключ видит только свои тестовые сообщения.
GET /messagesиGET /messages/{id}показывают их и ничего больше, аDELETE /messages/{id}отменяет сообщение, пока оноqueuedилиscheduled. Рабочие ключи никогда не видят тестовых сообщений. ФильтрstatusвGET /messagesдля тестовых ключей не поддерживается и возвращаетtest_mode_unsupported;number,sinceиuntilработают. - Остальные изменения отклоняются с
403test_mode_unsupported: подключение номера, создание, изменение и удаление напоминаний, загрузка и удаление медиафайлов, запуск проверки номеров, добавление, удаление, замена секрета и тестирование webhook endpoint. - События вебхуков для тестовых сообщений не отправляются.
- Тестовые сообщения хранятся 30 дней.
Ресурсы#
- Сообщения: отправка сейчас или позже, отправка до 1000 получателям, история, отмена.
- Номера: список ваших номеров WhatsApp и аккаунтов Telegram, подключение номера WhatsApp по QR-коду или коду привязки.
- Напоминания: повторяющиеся сообщения по расписанию cron.
- Медиафайлы: загрузите файлы один раз и отправляйте их по
media_id. - Проверки номеров: узнайте, какие номера есть в WhatsApp.
- Вебхуки: подписанные события, когда сообщение отправлено, не отправлено, отменено или истекло.
- Ошибки: все коды ошибок.
- Переход с v1 на v2: каждый endpoint и каждое поле v1 и их аналоги в v2.
