Webhook API

Webhook API — это способ отправки уведомлений в Notix с вашего сервера (бэкенд, CRM, интеграция). Один POST-запрос с токеном и подписью — и уведомление появляется в личном кабинете.

Вебхук — только для серверных интеграций

JavaScript SDK на сайте ходит не сюда, а в свой канал /api/v1/sdk/ingest, привязанный к домену сайта (SDK-токен публичный, поэтому вебхук его не принимает). Вебхук-токен — серверный секрет: его нельзя светить в браузерном коде.

Эндпоинт

POST /api/v1/webhook

Аутентификация

Токен передаётся в заголовке Authorization в формате Bearer:

Authorization: Bearer ntx_xxxxxxxxxxxxxxxxxxxxxxxx

Токен создаётся в личном кабинете при подключении источника «Webhook». Формат токена всегда начинается с префикса ntx_.

Заголовки запроса

ЗаголовокОбязательноОписание
AuthorizationДаBearer-токен: Bearer ntx_...
Content-TypeДаapplication/json
X-Notix-SignatureДа*HMAC-подпись тела запроса: sha256=<hex>. Обязательна, если при создании источника выдан секрет подписи (whsec_...) — см. ниже

Безопасность

Вебхук защищён тремя уровнями: токен + привязка к домену/IP + HMAC-подпись. Даже узнав адрес, посторонний не сможет отправлять уведомления от вашего имени.

1. Привязка к домену и IP

При создании webhook-источника укажите адрес вашего сервера (домен). Notix резолвит его A/AAAA-записи и принимает запросы только с этих IP. Чужой сервер с тем же токеном получит 403 IP not allowed. Если IP сервера сменился (переезд, новый хостинг) — обновите адрес источника или пересоздайте токен.

2. Подпись запроса (HMAC)

При создании источника выдаётся секрет подписи whsec_... (показывается один раз — сохраните его). Каждый запрос подписывается: HMAC-SHA256 от сырого JSON-тела с этим секретом, результат — в заголовок X-Notix-Signature: sha256=<hex>.

BODY='{"title":"Новый заказ #1247","notification_type":"order"}'
SECRET='whsec_xxxxxxxxxxxxxxxxxxxxxxxx'
SIGNATURE=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')

curl -X POST https://notix-hub.ru/api/v1/webhook \
  -H "Authorization: Bearer ntx_..." \
  -H "Content-Type: application/json" \
  -H "X-Notix-Signature: sha256=$SIGNATURE" \
  -d "$BODY"
<?php

$token  = 'ntx_...';
$secret = 'whsec_...';
$body   = json_encode([
    'title' => 'Новый заказ #1247',
    'notification_type' => 'order',
], JSON_UNESCAPED_UNICODE);

// Подпись считается от ТОЙ ЖЕ строки, что уходит в тело запроса
$signature = 'sha256=' . hash_hmac('sha256', $body, $secret);

$ch = curl_init('https://notix-hub.ru/api/v1/webhook');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $token,
    'Content-Type: application/json',
    'X-Notix-Signature: ' . $signature,
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

echo curl_exec($ch);
import crypto from 'node:crypto';

const token  = 'ntx_...';
const secret = 'whsec_...';
const body = JSON.stringify({
    title: 'Новый заказ #1247',
    notification_type: 'order',
});

// Подпись от той же строки, что отправляется в body
const signature = 'sha256=' +
    crypto.createHmac('sha256', secret).update(body).digest('hex');

const response = await fetch('https://notix-hub.ru/api/v1/webhook', {
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json',
        'X-Notix-Signature': signature,
    },
    body,
});

console.log(await response.json());

Важно про подпись

Подпись считается от байт-в-байту того же JSON, который отправляется (сериализуйте один раз и используйте строку и для подписи, и для тела). Если библиотека формирует тело сама — подпись не сойдётся и запрос будет отклонён с 401 Invalid signature.

Тело запроса

ПолеТипОбязательноОписание
titlestringДаЗаголовок уведомления (макс. 255 символов)
bodystringНетТекст уведомления
notification_typestringНетSlug типа: lead, order, event, metric, error_batch. Подробнее о типах
prioritystringНетПриоритет: low, normal, high, urgent
pushbooleanНетОтправлять ли push-уведомление на телефон. По умолчанию true. Установите false чтобы только записать в ленту без push
tagstringНетОдиночный тег (строка). Если нет в БД — создастся с цветом по умолчанию #10B981
tagsstring[] | object[]НетМассив: строки или {name, color?}. Чтобы задать цвет — передайте объект
payloadobjectНетПроизвольный JSON

Как работают теги

При первой отправке укажите объект с name и color — тег создастся с этим цветом:

"tags": ["простая строка", {"name":"Важное","color":"#EF4444"}]

При повторной отправке тег с таким именем уже существует — цвет сохраняется (не перезаписывается). Можно передавать просто строку:

"tags": ["Важное"]  // цвет останется #EF4444

Если тега нет и передан только цвет — создастся с цветом по умолчанию #10B981.

Примеры

curl -X POST https://notix-hub.ru/api/v1/webhook \
  -H "Authorization: Bearer ntx_live_x8k2jp9mQr5vLwY3aN6bHc1d" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Новый заказ #1247",
    "body": "Клиент: Иванов И.И.\nСумма: 34 500 ₽",
    "notification_type": "order",
    "priority": "high",
    "push": true,
    "tags": ["заказ", {"name":"оплата","color":"#10B981"}]
  }'
<?php

$token = 'ntx_live_x8k2jp9mQr5vLwY3aN6bHc1d';
$data = [
    'title' => 'Новый заказ #1247',
    'body' => "Клиент: Иванов И.И.\nСумма: 34 500 ₽",
    'notification_type' => 'order',
    'priority' => 'high',
];

$ch = curl_init('https://notix-hub.ru/api/v1/webhook');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $token,
    'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

echo $response;
import requests

token = "ntx_live_x8k2jp9mQr5vLwY3aN6bHc1d"
data = {
    "title": "Новый заказ #1247",
    "body": "Клиент: Иванов И.И.\nСумма: 34 500 ₽",
    "notification_type": "order",
    "priority": "high",
}
headers = {
    "Authorization": f"Bearer {token}",
    "Content-Type": "application/json",
}

response = requests.post(
    "https://notix-hub.ru/api/v1/webhook",
    json=data,
    headers=headers,
)

print(response.json())
const token = 'ntx_live_x8k2jp9mQr5vLwY3aN6bHc1d';

const response = await fetch('https://notix-hub.ru/api/v1/webhook', {
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({
        title: 'Новый заказ #1247',
        body: 'Клиент: Иванов И.И.\nСумма: 34 500 ₽',
        notification_type: 'order',
        priority: 'high',
    }),
});

const result = await response.json();
console.log(result);
import java.net.URI;
import java.net.http.*;

var token = "ntx_live_x8k2jp9mQr5vLwY3aN6bHc1d";
var json = """
{
    "title": "Новый заказ #1247",
    "body": "Клиент: Иванов И.И.\\nСумма: 34 500 ₽",
    "notification_type": "order",
    "priority": "high"
}
""";

var request = HttpRequest.newBuilder()
    .uri(URI.create("https://notix-hub.ru/api/v1/webhook"))
    .header("Authorization", "Bearer " + token)
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(json))
    .build();

var response = HttpClient.newHttpClient()
    .send(request, HttpResponse.BodyHandlers.ofString());

System.out.println(response.body());
var token = "ntx_live_x8k2jp9mQr5vLwY3aN6bHc1d";
var data = new
{
    title = "Новый заказ #1247",
    body = "Клиент: Иванов И.И.\nСумма: 34 500 ₽",
    notification_type = "order",
    priority = "high"
};

using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post,
    "https://notix-hub.ru/api/v1/webhook");
request.Headers.Add("Authorization", "Bearer " + token);
request.Content = new StringContent(
    System.Text.Json.JsonSerializer.Serialize(data),
    System.Text.Encoding.UTF8,
    "application/json"
);

var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);

Ответы

КодОписание
201 CreatedУведомление создано. {"message":"Notification received","id":"..."}
401 UnauthorizedТокен недействителен/истёк, либо неверная подпись (Invalid signature)
403 ForbiddenЗапрос с неразрешённого IP (IP not allowed) или попытка использовать SDK/chat-токен в вебхуке
402 Payment RequiredИсчерпан лимит уведомлений тарифа
422 UnprocessableОшибка валидации: неверный payload, отсутствуют обязательные поля

Примеры по типам уведомлений

Заявка (lead) — cURL

curl -X POST https://notix-hub.ru/api/v1/webhook \
  -H "Authorization: Bearer ntx_..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Новая заявка с лендинга",
    "body": "Заявка на консультацию",
    "notification_type": "lead",
    "priority": "high",
    "push": true,
    "tags": ["лендинг"],
    "payload": {
      "name": "Иван Петров",
      "phone": "+7 917 000-00-00",
      "email": "ivan@example.com"
    }
  }'

Заказ (order) — cURL

curl -X POST https://notix-hub.ru/api/v1/webhook \
  -H "Authorization: Bearer ntx_..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Новый заказ #1247",
    "body": "Заказ оформлен через сайт",
    "notification_type": "order",
    "priority": "high",
    "tags": ["заказ"],
    "payload": {
      "fname": "Иван Петров",
      "phone": "+7 917 000-00-00",
      "order_id": "1247",
      "items": [
        {"name": "Футболка", "price": 2500, "qty": 2},
        {"name": "Кроссовки", "price": 7000, "qty": 1}
      ],
      "address": "г. Москва, ул. Ленина, 15",
      "location": "https://shop.ru/orders/1247"
    }
  }'

Событие (event) — cURL

curl -X POST https://notix-hub.ru/api/v1/webhook \
  -H "Authorization: Bearer ntx_..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Обновление склада",
    "body": "Товар перемещён",
    "notification_type": "event",
    "priority": "normal",
    "push": false,
    "payload": {
      "event_code": "warehouse_update",
      "event_title": "Обновление склада",
      "properties": [
        {"title": "Склад", "value": "Коледино"},
        {"title": "Товар", "value": "Футболка мужская"},
        {"title": "Количество", "value": "120 шт."}
      ]
    }
  }'

Без push-уведомления

Флаг push (boolean) управляет отправкой push на телефон. По умолчанию true. Для «тихих» уведомлений (только в ленту) укажите "push": false.

Перегенерация токена

Если токен был скомпрометирован, его можно перегенерировать в личном кабинете. Старый токен перестаёт работать немедленно.