TavoCRM API
REST API для интеграций. База: https://{поддомен}.tavocrm.com/api/v1. Формат — JSON.
Авторизация
Создайте ключ в админке (Настройки → Интеграции → Ключи доступа) и передавайте его в заголовке. Спека доступна по /openapi.json — её можно импортировать в Scalar, Postman или Insomnia.
Authorization: Bearer tvk_xxxxxxxxxxxxxxxx
У ключа задаются права (scopes) — orders:read/orders:write, contacts:read/contacts:write, products:read — и привязка к магазинам. Пусто = полный доступ ко всем магазинам. Иначе метод без нужного права вернёт 403 forbidden_scope, а данные (списки и отдельные записи) ограничиваются магазинами ключа. Те же права и магазины действуют и в MCP.
Ключ привязывается к поверхностям — REST API и/или MCP. Запрос к поверхности, к которой ключ не привязан, вернёт 403 forbidden_surface. Привязка к MCP даёт полный доступ к управлению админкой в рамках прав ключа — выдавайте её осознанно.
Служебные
Проверка ключа
Проверяет ключ и возвращает код арендатора.
curl https://{поддомен}.tavocrm.com/api/v1/ping \
-H "Authorization: Bearer tvk_..."
const res = await fetch("https://{поддомен}.tavocrm.com/api/v1/ping", {
method: "GET",
headers: {
"Authorization": "Bearer tvk_..."
}
});
const data = await res.json();
$ch = curl_init("https://{поддомен}.tavocrm.com/api/v1/ping");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer tvk_..."]);
$data = json_decode(curl_exec($ch), true);
import requests
r = requests.get("https://{поддомен}.tavocrm.com/api/v1/ping",
headers={"Authorization": "Bearer tvk_..."})
print(r.json())
req, _ := http.NewRequest("GET", "https://{поддомен}.tavocrm.com/api/v1/ping", nil)
req.Header.Set("Authorization", "Bearer tvk_...")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
{
"pong": true,
"tenant": "acme"
}
Заказы
Список заказов
Постранично, новые сверху. Фильтры status / contact_id / q взаимоисключающие (в этом приоритете), плюс custom[]. contact_id=false — заказы без клиента.
status | string | Код статуса |
contact_id | string | ID клиента (или false — без клиента) |
q | string | Поиск по номеру / external_id |
custom[код] | object | Фильтр по кастомным полям: custom[код]=значение (false = пусто/нет значения) |
page | integer | Страница (с 1) |
per_page | integer | Размер страницы, ≤ 100 |
curl https://{поддомен}.tavocrm.com/api/v1/orders \
-H "Authorization: Bearer tvk_..."
const res = await fetch("https://{поддомен}.tavocrm.com/api/v1/orders", {
method: "GET",
headers: {
"Authorization": "Bearer tvk_..."
}
});
const data = await res.json();
$ch = curl_init("https://{поддомен}.tavocrm.com/api/v1/orders");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer tvk_..."]);
$data = json_decode(curl_exec($ch), true);
import requests
r = requests.get("https://{поддомен}.tavocrm.com/api/v1/orders",
headers={"Authorization": "Bearer tvk_..."})
print(r.json())
req, _ := http.NewRequest("GET", "https://{поддомен}.tavocrm.com/api/v1/orders", nil)
req.Header.Set("Authorization", "Bearer tvk_...")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
{
"data": [
{
"id": 1043,
"number": 1043,
"status": "new",
"contact_id": 12,
"amount": 5080,
"currency": "RUB",
"paid_amount": 0,
"items": [
{
"product_id": 5,
"name": "Кроссовки",
"qty": 2,
"price": 2540,
"sum": 5080
}
]
}
],
"page": 1,
"pages": 7,
"total": 312
}
Создать заказ
В позиции достаточно product_id и qty — название и цена берутся из каталога.
contact_id * | integer | Существующий клиент (обязательно) |
items * | array | Позиции (обязательно) |
status | string | Код статуса; по умолчанию первый |
shop_id | integer | |
method | string | |
currency | string | |
paid_amount | number | |
delivery_cost | number | |
external_id | string |
curl https://{поддомен}.tavocrm.com/api/v1/orders \
-X POST \
-H "Authorization: Bearer tvk_..." \
-H "Content-Type: application/json" \
-d '{"contact_id":12,"items":[{"product_id":5,"qty":2}]}'
const res = await fetch("https://{поддомен}.tavocrm.com/api/v1/orders", {
method: "POST",
headers: {
"Authorization": "Bearer tvk_...",
"Content-Type": "application/json"
},
body: JSON.stringify({"contact_id":12,"items":[{"product_id":5,"qty":2}]})
});
const data = await res.json();
$ch = curl_init("https://{поддомен}.tavocrm.com/api/v1/orders");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer tvk_...", "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"contact_id":12,"items":[{"product_id":5,"qty":2}]}');
$data = json_decode(curl_exec($ch), true);
import requests
r = requests.post("https://{поддомен}.tavocrm.com/api/v1/orders",
headers={"Authorization": "Bearer tvk_..."},
json={"contact_id":12,"items":[{"product_id":5,"qty":2}]})
print(r.json())
req, _ := http.NewRequest("POST", "https://{поддомен}.tavocrm.com/api/v1/orders", strings.NewReader(`{"contact_id":12,"items":[{"product_id":5,"qty":2}]}`))
req.Header.Set("Authorization", "Bearer tvk_...")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
{
"data": {
"id": 1043,
"number": 1043,
"status": "new",
"contact_id": 12,
"amount": 5080,
"currency": "RUB",
"paid_amount": 0,
"items": [
{
"product_id": 5,
"name": "Кроссовки",
"qty": 2,
"price": 2540,
"sum": 5080
}
]
}
}
Заказ по id
id * | integer | Идентификатор |
curl https://{поддомен}.tavocrm.com/api/v1/orders/{id} \
-H "Authorization: Bearer tvk_..."
const res = await fetch("https://{поддомен}.tavocrm.com/api/v1/orders/{id}", {
method: "GET",
headers: {
"Authorization": "Bearer tvk_..."
}
});
const data = await res.json();
$ch = curl_init("https://{поддомен}.tavocrm.com/api/v1/orders/{id}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer tvk_..."]);
$data = json_decode(curl_exec($ch), true);
import requests
r = requests.get("https://{поддомен}.tavocrm.com/api/v1/orders/{id}",
headers={"Authorization": "Bearer tvk_..."})
print(r.json())
req, _ := http.NewRequest("GET", "https://{поддомен}.tavocrm.com/api/v1/orders/{id}", nil)
req.Header.Set("Authorization", "Bearer tvk_...")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
{
"data": {
"id": 1043,
"number": 1043,
"status": "new",
"contact_id": 12,
"amount": 5080,
"currency": "RUB",
"paid_amount": 0,
"items": [
{
"product_id": 5,
"name": "Кроссовки",
"qty": 2,
"price": 2540,
"sum": 5080
}
]
}
}
Сменить статус
id * | integer | Идентификатор |
status * | string | Код статуса арендатора |
curl https://{поддомен}.tavocrm.com/api/v1/orders/{id}/status \
-X POST \
-H "Authorization: Bearer tvk_..." \
-H "Content-Type: application/json" \
-d '{"status":"shipped"}'
const res = await fetch("https://{поддомен}.tavocrm.com/api/v1/orders/{id}/status", {
method: "POST",
headers: {
"Authorization": "Bearer tvk_...",
"Content-Type": "application/json"
},
body: JSON.stringify({"status":"shipped"})
});
const data = await res.json();
$ch = curl_init("https://{поддомен}.tavocrm.com/api/v1/orders/{id}/status");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer tvk_...", "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"status":"shipped"}');
$data = json_decode(curl_exec($ch), true);
import requests
r = requests.post("https://{поддомен}.tavocrm.com/api/v1/orders/{id}/status",
headers={"Authorization": "Bearer tvk_..."},
json={"status":"shipped"})
print(r.json())
req, _ := http.NewRequest("POST", "https://{поддомен}.tavocrm.com/api/v1/orders/{id}/status", strings.NewReader(`{"status":"shipped"}`))
req.Header.Set("Authorization", "Bearer tvk_...")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
{
"data": {
"id": 1043,
"number": 1043,
"status": "new",
"contact_id": 12,
"amount": 5080,
"currency": "RUB",
"paid_amount": 0,
"items": [
{
"product_id": 5,
"name": "Кроссовки",
"qty": 2,
"price": 2540,
"sum": 5080
}
]
}
}
Внести оплату
id * | integer | Идентификатор |
amount * | number | Сумма > 0 |
curl https://{поддомен}.tavocrm.com/api/v1/orders/{id}/pay \
-X POST \
-H "Authorization: Bearer tvk_..." \
-H "Content-Type: application/json" \
-d '{"amount":2540}'
const res = await fetch("https://{поддомен}.tavocrm.com/api/v1/orders/{id}/pay", {
method: "POST",
headers: {
"Authorization": "Bearer tvk_...",
"Content-Type": "application/json"
},
body: JSON.stringify({"amount":2540})
});
const data = await res.json();
$ch = curl_init("https://{поддомен}.tavocrm.com/api/v1/orders/{id}/pay");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer tvk_...", "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"amount":2540}');
$data = json_decode(curl_exec($ch), true);
import requests
r = requests.post("https://{поддомен}.tavocrm.com/api/v1/orders/{id}/pay",
headers={"Authorization": "Bearer tvk_..."},
json={"amount":2540})
print(r.json())
req, _ := http.NewRequest("POST", "https://{поддомен}.tavocrm.com/api/v1/orders/{id}/pay", strings.NewReader(`{"amount":2540}`))
req.Header.Set("Authorization", "Bearer tvk_...")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
{
"data": {
"id": 1043,
"number": 1043,
"status": "new",
"contact_id": 12,
"amount": 5080,
"currency": "RUB",
"paid_amount": 0,
"items": [
{
"product_id": 5,
"name": "Кроссовки",
"qty": 2,
"price": 2540,
"sum": 5080
}
]
}
}
Клиенты
Список клиентов
Фильтры q / user_id / shop_id взаимоисключающие (в этом приоритете) + custom[]. shop_id=false / user_id=false — без значения.
q | string | Поиск по имени/телефону/email |
user_id | string | ID менеджера (или false) |
shop_id | string | ID магазина (или false) |
custom[код] | object | Фильтр по кастомным полям: custom[код]=значение (false = пусто/нет значения) |
page | integer | Страница (с 1) |
per_page | integer | Размер страницы, ≤ 100 |
curl https://{поддомен}.tavocrm.com/api/v1/contacts \
-H "Authorization: Bearer tvk_..."
const res = await fetch("https://{поддомен}.tavocrm.com/api/v1/contacts", {
method: "GET",
headers: {
"Authorization": "Bearer tvk_..."
}
});
const data = await res.json();
$ch = curl_init("https://{поддомен}.tavocrm.com/api/v1/contacts");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer tvk_..."]);
$data = json_decode(curl_exec($ch), true);
import requests
r = requests.get("https://{поддомен}.tavocrm.com/api/v1/contacts",
headers={"Authorization": "Bearer tvk_..."})
print(r.json())
req, _ := http.NewRequest("GET", "https://{поддомен}.tavocrm.com/api/v1/contacts", nil)
req.Header.Set("Authorization", "Bearer tvk_...")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
{
"data": [
{
"id": 12,
"first_name": "Иван",
"last_name": "Петров",
"phones": [
"+79990000000"
],
"emails": []
}
],
"page": 1,
"pages": 7,
"total": 312
}
Создать клиента
Нужно указать имя или телефон.
first_name | string | |
last_name | string | |
middle_name | string | |
phones | array | |
emails | array | |
external_id | string |
curl https://{поддомен}.tavocrm.com/api/v1/contacts \
-X POST \
-H "Authorization: Bearer tvk_..." \
-H "Content-Type: application/json" \
-d '{"first_name":"Иван","phones":["+79990000000"]}'
const res = await fetch("https://{поддомен}.tavocrm.com/api/v1/contacts", {
method: "POST",
headers: {
"Authorization": "Bearer tvk_...",
"Content-Type": "application/json"
},
body: JSON.stringify({"first_name":"Иван","phones":["+79990000000"]})
});
const data = await res.json();
$ch = curl_init("https://{поддомен}.tavocrm.com/api/v1/contacts");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer tvk_...", "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"first_name":"Иван","phones":["+79990000000"]}');
$data = json_decode(curl_exec($ch), true);
import requests
r = requests.post("https://{поддомен}.tavocrm.com/api/v1/contacts",
headers={"Authorization": "Bearer tvk_..."},
json={"first_name":"Иван","phones":["+79990000000"]})
print(r.json())
req, _ := http.NewRequest("POST", "https://{поддомен}.tavocrm.com/api/v1/contacts", strings.NewReader(`{"first_name":"Иван","phones":["+79990000000"]}`))
req.Header.Set("Authorization", "Bearer tvk_...")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
{
"data": {
"id": 12,
"first_name": "Иван",
"last_name": "Петров",
"phones": [
"+79990000000"
],
"emails": []
}
}
Клиент по id
id * | integer | Идентификатор |
curl https://{поддомен}.tavocrm.com/api/v1/contacts/{id} \
-H "Authorization: Bearer tvk_..."
const res = await fetch("https://{поддомен}.tavocrm.com/api/v1/contacts/{id}", {
method: "GET",
headers: {
"Authorization": "Bearer tvk_..."
}
});
const data = await res.json();
$ch = curl_init("https://{поддомен}.tavocrm.com/api/v1/contacts/{id}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer tvk_..."]);
$data = json_decode(curl_exec($ch), true);
import requests
r = requests.get("https://{поддомен}.tavocrm.com/api/v1/contacts/{id}",
headers={"Authorization": "Bearer tvk_..."})
print(r.json())
req, _ := http.NewRequest("GET", "https://{поддомен}.tavocrm.com/api/v1/contacts/{id}", nil)
req.Header.Set("Authorization", "Bearer tvk_...")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
{
"data": {
"id": 12,
"first_name": "Иван",
"last_name": "Петров",
"phones": [
"+79990000000"
],
"emails": []
}
}
Товары
Список товаров
Фильтры q (по названию), active (1/0) + custom[].
q | string | Поиск по названию |
active | boolean | Только активные (1) / скрытые (0) |
custom[код] | object | Фильтр по кастомным полям: custom[код]=значение (false = пусто/нет значения) |
page | integer | Страница (с 1) |
per_page | integer | Размер страницы, ≤ 100 |
curl https://{поддомен}.tavocrm.com/api/v1/products \
-H "Authorization: Bearer tvk_..."
const res = await fetch("https://{поддомен}.tavocrm.com/api/v1/products", {
method: "GET",
headers: {
"Authorization": "Bearer tvk_..."
}
});
const data = await res.json();
$ch = curl_init("https://{поддомен}.tavocrm.com/api/v1/products");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer tvk_..."]);
$data = json_decode(curl_exec($ch), true);
import requests
r = requests.get("https://{поддомен}.tavocrm.com/api/v1/products",
headers={"Authorization": "Bearer tvk_..."})
print(r.json())
req, _ := http.NewRequest("GET", "https://{поддомен}.tavocrm.com/api/v1/products", nil)
req.Header.Set("Authorization", "Bearer tvk_...")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
{
"data": [
{
"id": 5,
"name": "Кроссовки",
"brand": "Nike",
"price": 2540,
"sku": "SNK-5",
"category_ids": [
3,
7
],
"active": true
}
],
"page": 1,
"pages": 7,
"total": 312
}
Товар по id
id * | integer | Идентификатор |
curl https://{поддомен}.tavocrm.com/api/v1/products/{id} \
-H "Authorization: Bearer tvk_..."
const res = await fetch("https://{поддомен}.tavocrm.com/api/v1/products/{id}", {
method: "GET",
headers: {
"Authorization": "Bearer tvk_..."
}
});
const data = await res.json();
$ch = curl_init("https://{поддомен}.tavocrm.com/api/v1/products/{id}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer tvk_..."]);
$data = json_decode(curl_exec($ch), true);
import requests
r = requests.get("https://{поддомен}.tavocrm.com/api/v1/products/{id}",
headers={"Authorization": "Bearer tvk_..."})
print(r.json())
req, _ := http.NewRequest("GET", "https://{поддомен}.tavocrm.com/api/v1/products/{id}", nil)
req.Header.Set("Authorization", "Bearer tvk_...")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
{
"data": {
"id": 5,
"name": "Кроссовки",
"brand": "Nike",
"price": 2540,
"sku": "SNK-5",
"category_ids": [
3,
7
],
"active": true
}
}
Вебхуки
Вебхуки — действие триггера «HTTP-запрос»: событие + условия + HTTP-запрос на ваш URL с настраиваемым телом и необязательной подписью HMAC-SHA256 (заголовок X-TavoCRM-Signature: sha256=<hex>). Настройка — Настройки → Интеграции → Триггеры.
Подробно — в справочнике по триггерам.
MCP-сервер
Эндпоинт https://{поддомен}.tavocrm.com/mcp — JSON-RPC 2.0, авторизация тем же Bearer-ключом. Набор инструментов и доступ определяются правами (scopes) и магазинами ключа — так же, как в REST: инструмент доступен, если у ключа есть соответствующий scope (например orders:write). Пустые права у ключа = полный доступ, включая админ-инструменты (справочники, статусы, роли, пользователи). Ключ должен быть привязан к поверхности MCP (в форме ключа), иначе запрос вернёт 403 forbidden_surface. Настройка адреса и публичного доступа — Настройки → MCP-сервер.
curl https://{поддомен}.tavocrm.com/mcp \
-H "Authorization: Bearer tvk_..." \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Публичный MCP (анонимное оформление)
Эндпоинт https://{поддомен}.tavocrm.com/mcp/public — без ключа (включается в Настройки → MCP-сервер). Для ИИ-агента покупателя: каталог (products_search, products_get) и оформление заказа (checkout). Первый checkout с name + phone создаёт клиента и возвращает client_token — «мини-ЛК» клиента: повторные заказы (checkout с client_token) и статусы только своих заказов (my_orders, my_order_get). Токен передаётся аргументом инструмента. Запросы ограничены по частоте на IP. Проверка доступности (для бота): GET /mcp/public с Accept: application/json → 200 с описанием сервера, если включено, иначе 404. При заходе из браузера тот же адрес перенаправляет на страницу-инструкцию /connect — её же можно дать покупателям (подключение ассистента, готовый промпт).
Коды ошибок
| HTTP / code | error | Значение |
|---|---|---|
| 401 | missing_token / invalid_token | нет / неверный ключ |
| 403 | forbidden_scope / shop_forbidden / forbidden_surface | у ключа нет нужного права / запись вне магазинов ключа / ключ не привязан к этой поверхности (API или MCP) |
| 404 | not_found | сущность не найдена |
| 422 | invalid_contact, no_items, invalid_status, invalid_amount, name_or_phone_required | ошибки валидации |
| 422 | limit_reached | достигнут лимит тарифа |
MCP: протокольные ошибки JSON-RPC (-32700 parse, -32601 method, -32602 unknown tool); ошибки инструмента — result.isError = true с текстом.