Получайте изменения через вебхуки
Владелец включает «Тариф и кошелёк», «Интеграции», «Задачи» и «Webhook задач» в разделе «Модули». Установите Webhook API по показанным условиям. Для GET /v1/developer/integrations нужен scope integrations.installations.read; для настроек нужны независимые integrations.webhooks.read и integrations.webhooks.write и включённый «API проекта». Метод PUT …/outbound-webhook принимает endpointUrl, eventTypes и expectedVersion.
Для создания expectedVersion = null; для изменения укажите текущую version. Используйте Idempotency-Key. signingSecret показывается только при первом успешном создании или ротации: replay её не возвращает. При потере ответа выполните отдельную ротацию.
Текущий каталог событий
Доступные события зависят от выбранного источника: проверьте eventTypes в ответе состояния webhook. «Webhook задач» поддерживает создание и завершение самостоятельных и клиентских задач, включая задачи API; data.resource различает crm.personal_task и crm.task.
- crm.deal.created.v1
- crm.deal.updated.v1
- crm.deal.stage_changed.v1
- crm.task.created.v1
- crm.task.completed.v1
{
"id": "EVENT_ID",
"type": "crm.deal.created.v1",
"api_version": "2026-07-31",
"created_at": "2026-10-12T12:00:00.000Z",
"data": {
"resource": "crm.deal",
"resource_id": "DEAL_ID"
}
}data содержит минимальные идентификаторы, а не карточку клиента. contact_id и stage_id необязательны. Задача может иметь resource = crm.personal_task: отдельный public read для неё пока не обещается. Для текущего состояния сделки или CRM-задачи используйте соответствующий scoped read.
Проверка подписи
v1=HMAC-SHA256(secret, timestamp + "." + rawBody)
X-Nova-Webhook-Timestamp— Unix seconds времени попытки, не события.X-Nova-Webhook-Signature— v1= и 64 hex-символа. Сравнивайте constant-time.X-Nova-Webhook-Id— ID доставки. Для dedupe используйте id из JSON.X-Nova-Webhook-Event— тип события, совпадает с type в JSON.
Передайте точные байты до JSON parsing. Пересериализация меняет подпись. Допустимое окно в примере ±300 секунд; часы сервера должны быть синхронизированы. У каждой повторной попытки новая подпись и timestamp.
Скачать проверяющий модуль для Node.jsИсходный код проверяющего модуля
import { createHmac, timingSafeEqual } from "node:crypto";
/** Node 24 receiver helper: supply exact raw bytes and lowercase Node request headers. */
export function verifyNovaWebhook({ body, headers, secret, now = Date.now(), tolerance = 300 }) {
const reject = () => {
throw new Error("Invalid Nova webhook");
};
if (
!(body instanceof Uint8Array) ||
body.byteLength > 256 * 1024 ||
typeof secret !== "string" ||
!secret.startsWith("whsec_") ||
secret.length < 16 ||
!Number.isFinite(now) ||
!Number.isFinite(tolerance) ||
tolerance <= 0 ||
tolerance > 300
)
reject();
const timestamp = headers?.["x-nova-webhook-timestamp"];
const signature = headers?.["x-nova-webhook-signature"];
if (
typeof timestamp !== "string" ||
!/^[1-9][0-9]{0,10}$/.test(timestamp) ||
typeof signature !== "string" ||
!/^v1=[0-9a-f]{64}$/.test(signature) ||
Math.abs(Math.floor(now / 1000) - Number(timestamp)) > tolerance
)
reject();
const expected = createHmac("sha256", secret).update(timestamp).update(".").update(body).digest();
if (!timingSafeEqual(expected, Buffer.from(signature.slice(3), "hex"))) reject();
let event;
try {
event = JSON.parse(Buffer.from(body).toString("utf8"));
} catch {
reject();
}
if (
!event ||
typeof event !== "object" ||
Array.isArray(event) ||
typeof event.id !== "string" ||
!event.id ||
event.id.length > 160 ||
typeof event.type !== "string" ||
!event.type ||
event.type.length > 160 ||
event.api_version !== "2026-07-31" ||
typeof event.created_at !== "string" ||
!Number.isFinite(Date.parse(event.created_at)) ||
!event.data ||
typeof event.data !== "object" ||
Array.isArray(event.data) ||
event.type !== headers?.["x-nova-webhook-event"]
)
reject();
// Durable event-id deduplication and enqueue must be implemented by the receiver.
// Unknown additive event types should be durably acknowledged and skipped safely.
return event;
}
Надёжный обработчик
- Ограничьте body, проверьте подпись и envelope. Invalid signature → 401, invalid JSON → 400.
- В одной транзакции сохраните unique event ID и задачу обработки. Дубликат уже сохранённого события → 2xx. Если транзакция не завершилась → 503.
- После commit верните 2xx, обработайте событие асинхронно. Не подтверждайте событие до durable enqueue.
- Повторы и порядок не гарантируют однократную бизнес-обработку. Делайте handlers идемпотентными, сверяйте текущее состояние через API. Неизвестный additive event можно принять и пропустить.
Доставка и восстановление
Только публичный HTTPS, без redirects. 2xx подтверждает приём; network/timeout/408/425/429/5xx повторяются с ограниченным exponential backoff. Другие ответы становятся dead letter. HTTP transport ограничен 10 секундами. После исправления используйте POST …/outbound-webhook/deliveries/{deliveryId}/retry с новым Idempotency-Key.
Пауза прекращает capture новых событий; автоматический backfill периода паузы не обещается. Ротация заменяет secret немедленно, без overlap. Текущие endpoint и secret используются при попытке доставки, поэтому меняйте endpoint только на подконтрольный получатель. Журнал пока ограничен recent deliveries; полного replay journal и гарантии 72-часового восстановления нет.