Налаштування блокчейн-вебхуків: підписи, дедуплікація та повторне відтворення
Створюйте підписки на адреси через HTTP, перевіряйте підписи сирого тіла запиту, дедуплікуйте ID подій та відновлюйте збережені збіги або пропущені блоки.
Відстежуйте EVM-адресу гаманця та отримуйте її нативні перекази, перекази токенів та відповідні логи смарт-контрактів на ваш ендпоінт HTTPS для сповіщень про активність гаманця або моніторингу подій смарт-контрактів. Розробники та AI-агенти використовують той самий HTTP API підписок. Для сповіщень про платежі ERC-20 USDT / USDC дотримуйтесь інструкцій з отримувача платежів у стейблкоїнах.
- Перший крок: Розгорніть отримувач, що перевіряє підписи сирого тіла запиту, використовуючи наведений нижче приклад верифікації.
- Готово, коли: Після
applied_version >= change_versionвідповідна ончейн-активність надходить на ваш отримувач, проходить перевірку підпису та зберігається заidподії; створення підписки не надсилає тестове повідомлення.
Завдання, які допомагає виконати цей посібник
- Отримувати активність адрес гаманців шляхом створення автентифікованої підписки, додавання адрес для відстеження та перевірки вхідних подій.
- Моніторити відповідні логи контрактів шляхом перевірки подій
logдля відстежуваних адрес та фільтраціїaddress,topicsіdataу вашому отримувачі. - Відновлювати перервану доставку шляхом перевірки прогресу підписки та повторного відтворення збережених збігів із подальшим заповненням прогалин за межами вікна збереження.
Підписка поєднує один HTTPS-URL для прийому, секрет для підпису, відстежувані EVM-адреси та обов'язковий об'єкт chains. Адреси застосовуються до кожної мережі в цьому об'єкті. Використовуйте API із заголовком x-api-key; будь-який активний ключ у вашому акаунті може керувати всіма його підписками. Отримайте API key перед початком. Push OpenAPI містить список усіх операцій та схем вебхуків.
Підключення активності адрес гаманців
- Розгорніть отримувач, який перевіряє оригінальне тіло запиту, зберігає події за
idта підтверджує їх протягом 10 секунд. - Прочитайте
GET /v1/push/chains, потім створіть підписку з вашим HTTPS-URL та обраними мережами. Збережіть отриманіidтаsecret. - Додайте адреси гаманців. Зачекайте, поки
applied_version >= change_version, і зафіксуйтеapplied_from_blockдля кожної мережі; зіставлення починається з нього. - Обробляйте перекази та логи, а також відновлюйте прогалини або замінені блоки. Фільтруйте контракти токенів, отримувачів та цілочисельні суми перед використанням сповіщень в обробці платежів.
Вибір Webhook, WebSocket або опитування
- Webhook надсилає події відстежуваних адрес на HTTPS-отримувач із повторними спробами доставки та відтворенням збережених збігів.
- WebSocket транслює
newHeadsта відфільтрованіlogsчерез постійне з'єднання. Перепідключайтеся, повторно підписуйтеся та запитуйте пропущені блоки після розриву з'єднання. - Опитування запитує
eth_getLogsв обмежених діапазонах блоків за допомогою вашого власного курсора; використовуйте його для моніторингу платежів або заповнення пропущених логів.
Перевірте ws та subscriptions у GET /v1/chains щодо підтримки WebSocket. Якщо ws має значення false, вебхуки для адрес усе одно доступні, якщо ця мережа присутня в автентифікованому списку GET /v1/push/chains. Сама лише підтримка RPC не гарантує підтримку Push.
Ліміти адрес
Самообслуговування підтримує до 1,000,000 адрес на підписку та доступне під час реєстрації. Підписка охоплює кілька мереж з однією URL-адресою прийому. Ліміт для підприємств підтримує 10,000,000 / 100,000,000 адрес на підписку; зв'яжіться з нами для його підключення. Розробники та AI-агенти мають однакові варіанти місткості та ціни. Обидва рівні використовують однакові тарифи за адресо-день та доставлену подію, зазначені на сторінці цін.
Створення підписки
Прочитайте GET /v1/push/chains, щоб дізнатися про доступні мережі та їхню мінімальну, стандартну й максимальну кількість підтверджень. Блок вважається підтвердженим, коли head - block + 1 >= confirmations. Кожна мережа може використовувати значення за замовчуванням, якщо передати {}. Потрібно вказати щонайменше одну мережу; нові мережі не додаються автоматично до наявних підписок.
Збережіть наступний приклад як create.json, замінивши URL на адресу вашого отримувача та обравши мережі зі списку мереж. URL має використовувати HTTPS на порту 443, ім'я хоста замість IP-адреси, і не містити інформації про користувача чи фрагментів.
{
"url": "https://hooks.example.com/push",
"chains": {
"bsc_mainnet": {
"confirmations": 1
},
"base_mainnet": {}
}
}Встановіть BLOCKVECTRA_API_KEY у вашому середовищі, потім виконайте:
PUSH_URL='https://api.blockvectra.com/v1/push'
curl --fail-with-body -sS "$PUSH_URL/chains" \
-H "x-api-key: $BLOCKVECTRA_API_KEY"
curl --fail-with-body -sS "$PUSH_URL/subscriptions" \
-H "x-api-key: $BLOCKVECTRA_API_KEY" \
-H 'Content-Type: application/json' \
-d @create.json > subscription.jsonУспішне створення повертає HTTP 201 та підписку зі статусом online без адрес. Надійно збережіть її числовий id та secret. Секрет повертається лише під час створення або виклику POST /subscriptions/{subscription_id}/rotate-secret; ротація набуває чинності негайно для всіх мереж без перехідного періоду. Тестове повідомлення не надсилається.
Додавання та перегляд списку адрес
Збережіть пакет адрес як addresses.json, замінивши приклади адрес на ті, за якими ви спостерігаєте:
{
"addresses": [
"0x9725db73f2cd8657f3e1841e5689f210ee54a92d",
"0x99d47bB552ae095159C251836De6A5d524076872"
]
}Встановіть SUBSCRIPTION_ID у значення отриманого ID підписки:
curl --fail-with-body -sS "$PUSH_URL/subscriptions/$SUBSCRIPTION_ID/addresses/add" \
-H "x-api-key: $BLOCKVECTRA_API_KEY" \
-H 'Content-Type: application/json' -d @addresses.json
curl --fail-with-body -sS "$PUSH_URL/subscriptions/$SUBSCRIPTION_ID/addresses" \
-H "x-api-key: $BLOCKVECTRA_API_KEY"
curl --fail-with-body -sS "$PUSH_URL/subscriptions/$SUBSCRIPTION_ID" \
-H "x-api-key: $BLOCKVECTRA_API_KEY"Кожен виклик додавання приймає не більше ніж 10,000 адрес. Вхідні адреси мають бути в нижньому регістрі або валідному змішаному регістрі згідно з EIP-55; невалідні вхідні дані призводять до відхилення всього пакета. Повторювані адреси вважаються unchanged, тому повторне надсилання того самого запиту на додавання є безпечним. Списки адрес використовують limit та page_token; next_page_token: null позначає останню сторінку.
Додавання адрес повертає change_version. Опитуйте або перевіряйте GET /subscriptions/{subscription_id}, доки applied_version >= change_version; застосування змін зазвичай триває близько 1 секунди. Значення applied_from_block кожної мережі визначає фактичний блок, починаючи з якого зіставляються ончейн-транзакції та логи. Для нових адрес зіставлення заднім числом не проводиться.
Створення підписки повертає HTTP 201 на підтвердження створення ресурсу підписки; HTTP 201 не означає, що ваш отримувач прийняв хоча б один пуш вебхука. Платформа не надсилає перевірочних або тестових повідомлень під час створення підписки чи реєстрації адрес. Ви повинні дочекатися виникнення відповідної ончейн-активності на відстежуваних адресах і мережах, щоб перевірити доставку на вашому отримувачі.
Формат подій
Кожен запит POST містить type: push.events, created_at та data. data містить subscription_id, одну мережу chain, complete_through_block та масив events. Фіксуйте прогрес окремо для кожної мережі: блок може охоплювати кілька повідомлень, тому номери блоків окремих подій не є маркером завершення. Кожне повідомлення містить максимум 1,000 подій, 1 MiB та 50 блоків.
{
"type": "push.events",
"created_at": "2026-10-02T03:00:05Z",
"data": {
"subscription_id": 48213,
"chain": "bsc_mainnet",
"complete_through_block": 64000121,
"events": [
{
"id": "evt_payvsqb6ogymhmehrs2wl5xcky",
"type": "native.transfer",
"ref": "eip155:56:0x7b19944dc683c33e8dedba259cb6939f7271f70f2eeb6ca5e456cccb57cc1eff:tx",
"from": "0xe0a2100d7dad33f70c4bb765323cb96b2400c844",
"to": "0x9725db73f2cd8657f3e1841e5689f210ee54a92d",
"amount": "150000000000000000",
"block_number": 64000120,
"block_hash": "0x327892a3e5699a43981f0fbcc5e490628641d92c040eb0429fb550ba3a73c3bf",
"block_timestamp": "2026-10-02T03:00:00Z",
"tx_hash": "0x7b19944dc683c33e8dedba259cb6939f7271f70f2eeb6ca5e456cccb57cc1eff",
"tx_index": 3,
"matched": [
{
"address": "0x9725db73f2cd8657f3e1841e5689f210ee54a92d",
"role": "to"
}
]
},
{
"id": "evt_lgcdattb6l2k3ejuhe4mtdljkm",
"type": "token.transfer",
"ref": "eip155:56:0x3be3448e6f54ebfa928d5997a0cb2d5e9d3186c5ebfa293ad45b7edc72483a76:7",
"standard": "erc20",
"token": "0x55d398326f99059ff775485246999027b3197955",
"from": "0x0f94e5283c41c29a8f4dff8c17f68bdfb59f07df",
"to": "0x99d47bb552ae095159c251836de6a5d524076872",
"token_id": null,
"amount": "25000000000000000000",
"batch_index": null,
"block_number": 64000121,
"block_hash": "0x6a8146159162f182c091d17eac7d03e95dc92ce80de704c958ca2528306aff15",
"block_timestamp": "2026-10-02T03:00:00Z",
"tx_hash": "0x3be3448e6f54ebfa928d5997a0cb2d5e9d3186c5ebfa293ad45b7edc72483a76",
"tx_index": 5,
"log_index": 7,
"matched": [
{
"address": "0x99d47bb552ae095159c251836de6a5d524076872",
"role": "to"
}
]
},
{
"id": "evt_sgliw3ficdf6gaa6zzx4ew6vni",
"type": "log",
"ref": "eip155:56:0x3be3448e6f54ebfa928d5997a0cb2d5e9d3186c5ebfa293ad45b7edc72483a76:8",
"address": "0xb54ffbe723264b84cf74947127a6914cf87fc593",
"topics": [
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
"0x00000000000000000000000099d47bb552ae095159c251836de6a5d524076872",
"0x000000000000000000000000b54ffbe723264b84cf74947127a6914cf87fc593"
],
"data": "0x0000000000000000000000000000000000000000000000000000000000000000",
"block_number": 64000121,
"block_hash": "0x6a8146159162f182c091d17eac7d03e95dc92ce80de704c958ca2528306aff15",
"block_timestamp": "2026-10-02T03:00:00Z",
"tx_hash": "0x3be3448e6f54ebfa928d5997a0cb2d5e9d3186c5ebfa293ad45b7edc72483a76",
"tx_index": 5,
"log_index": 8,
"matched": [
{
"address": "0x99d47bb552ae095159c251836de6a5d524076872",
"role": "topic1"
}
]
}
]
}
}| Тип події | Що обробляти |
|---|---|
native.transfer | Успішні перекази нативного значення верхнього рівня за участю відстежуваної адреси; amount є цілочисельним десятковим рядком. Внутрішні нативні перекази виключені. |
token.transfer | Перекази ERC-20, ERC-721 та ERC-1155 за участю відстежуваних адрес; перевіряйте standard, token, token_id, amount та batch_index. Пакетні перекази ERC-1155 генерують одну подію на кожен елемент. |
log | Інші логи, де відстежувана адреса зазначена як контракт-емітент або в темах 1–3; перевіряйте address, topics, data та matched. |
subscription.gap | Діапазон від from_block до to_block недоступний для доставки, з reason: retention_expired; відновлюйте дані за допомогою Data API або eth_getLogs. |
chain.reorg | Безкоштовне сповіщення про реорганізацію: доставлені блоки від from_block до to_block були замінені. Позначте або видаліть їхні старі події за ref, потім збережіть автоматично повторно доставлені канонічні події та дедуплікуйте за id. |
У межах однієї підписки дедуплікуйте за id події; між різними підписками використовуйте ref та type. Ігноруйте невідомі поля та типи подій. Перевіряйте ончейн-факти перед виконанням фінансових операцій.
Перевірка підписів
Заголовки містять webhook-id, webhook-timestamp, webhook-signature та bv-subscription-id. Вибирайте секрет лише з підписок, створених вами; відхиляйте невідомі ID. Перевіряйте HMAC-SHA256 для рядка webhook-id.webhook-timestamp.raw-body, використовуючи байти оригінального тіла запиту перед парсингом JSON. Підпис має формат v1,<base64>; дозволяйте відхилення мітки часу приблизно на п'ять хвилин і порівнюйте за постійний час.
Ця функція Node.js приймає сире тіло у вигляді Buffer, заголовки запиту та мапу збережених секретів для ID підписок:
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyPush(rawBody, headers, secrets) {
const subscriptionId = headers['bv-subscription-id'];
const id = headers['webhook-id'];
const timestamp = headers['webhook-timestamp'];
const signature = headers['webhook-signature'];
if ([subscriptionId, id, timestamp, signature].some(v => typeof v !== 'string')) return false;
const secret = secrets.get(subscriptionId);
if (typeof secret !== 'string' || !secret.startsWith('whsec_')) return false;
if (!/^\d{10}$/.test(timestamp) || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const match = /^v1,([A-Za-z0-9+/]{43}=)$/.exec(signature);
if (!match) return false;
const received = Buffer.from(match[1], 'base64');
const expected = createHmac('sha256', Buffer.from(secret.slice(6), 'base64'))
.update(`${id}.${timestamp}.`).update(rawBody).digest();
return received.length === expected.length && timingSafeEqual(received, expected);
}Після перевірки розпарсіть тіло, збережіть результат обробки та поверніть 2xx протягом 10 секунд. Заголовок ID підписки вважається ненадійним, доки підпис не буде перевірено.
Перевірка вашої першої події
Зберігайте підписку в статусі online. Після того як зміни в адресах будуть застосовані, дочекайтеся відповідної ончейн-активності та переконайтеся, що ваш отримувач верифікує та надійно зберігає подію.
Припинення прослуховування після перевірки
Щоб припинити спостереження за адресами, збережіть адреси для видалення у файлі addresses.json та викличте POST /subscriptions/{subscription_id}/addresses/remove:
curl --fail-with-body -sS "$PUSH_URL/subscriptions/$SUBSCRIPTION_ID/addresses/remove" \
-H "x-api-key: $BLOCKVECTRA_API_KEY" \
-H 'Content-Type: application/json' -d @addresses.jsonКожен виклик видалення приймає не більше ніж 10,000 адрес. Адреса, за якою наразі не ведеться спостереження, вважається unchanged. Виклик повертає change_version. Як тільки applied_version >= change_version, блоки, починаючи з цього фактичного блоку, більше не зіставляються з видаленими адресами. Раніше зіставлені події (у процесі доставки, повторних спроб або в черзі) все одно будуть доставлені; вже доставлені події не відкликаються.
Щоб тимчасово призупинити прослуховування без видалення конфігурації чи адрес, встановіть status у значення offline:
curl --fail-with-body -sS -X PATCH "$PUSH_URL/subscriptions/$SUBSCRIPTION_ID" \
-H "x-api-key: $BLOCKVECTRA_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"status":"offline"}'Підписка зі статусом offline зупиняє прослуховування та доставку, вивантажує адреси з індексу зіставлення та не тарифікується за адреси за кожен повний день за UTC, протягом якого вона залишається офлайн. Уся конфігурація (URL, секрет, адреси, мережі та підтвердження) зберігається. Оновлення через PATCH на {"status":"online"} відновлює прослуховування з поточного фактичного блоку та не заповнює період перебування офлайн.
Використовуйте JSON Merge Patch у PATCH /subscriptions/{subscription_id}, щоб змінити url, key_id, status або chains: об'єкт мережі додає або оновлює її, а null — видаляє. Щонайменше одна мережа має залишатися. Щоб остаточно видалити підписку:
curl --fail-with-body -sS -X DELETE "$PUSH_URL/subscriptions/$SUBSCRIPTION_ID" \
-H "x-api-key: $BLOCKVECTRA_API_KEY"DELETE назавжди видаляє підписку, негайно зупиняє доставку в усіх мережах і знищує секрет та адреси.
Доставка, повторні спроби та повторне відтворення
Доставка здійснюється за принципом «щонайменше один раз». Мережа кожної підписки впорядкована за блоками та позицією в блоці; невдалі пакети блокують наступні події в цій мережі. Різні мережі мають незалежний прогрес і можуть надсилати POST-запити паралельно. Повторна спроба відправлення ідентичного пакета зберігає webhook-id, але змінений пакет може отримати новий ID: дедуплікуйте події, а не пакети.
Будь-який статус 2xx протягом 10 секунд підтверджує надійну обробку. Перенаправлення не виконуються; статуси 3xx та 410 вважаються помилками. Після помилки повторні спроби виконуються з інтервалами: негайно, через 5 секунд, 30 секунд, 2 хвилини, 10 хвилин, 30 хвилин та 1 годину, а потім щогодини. Заголовок 429 Retry-After може подовжити очікування до однієї години. Перевіряйте condition, last_error та next_attempt_at кожної мережі, якщо доставка припинилася. Можливі значення стану: receiver_failing, insufficient_balance та key_revoked; останній вимагає оновлення key_id на інший активний ключ акаунта.
Недоставлені події втрачають актуальність після закінчення вікна збереження та генерують subscription.gap. POST /subscriptions/{subscription_id}/replay приймає chain та from_block; перевірте replayable_from_block у GET /push/chains та прогрес підписки. Повторне відтворення доставляє наявні збіги та не може відновити події, що відбулися до додавання адреси або мережі.
chain.reorg сповіщає про те, що вже доставлені блоки були замінені; це не свідчить про прогалину в доставці. Реорганізації, глибина яких менша за вашу кількість підтверджень, непомітні. Для реорганізацій, що зачіпають доставлені блоки на глибину до 1,024 блоків, канонічні події доставляються автоматично з новими id. Позначте або відкиньте замінені події за ref, збережіть канонічні події та дедуплікуйте за id; для платіжних записів проводьте звірку за ref та tx_hash. Глибша реорганізація зупиняє мережу: перевірте поле halted у GET /push/chains; канонічна повторна доставка розпочнеться після відновлення мережі. Керуюча подія не просуває значення complete_through_block.
Запитуйте доставлені події даних за допомогою GET /subscriptions/{subscription_id}/events?chain=..., за потреби додаючи from_block, to_block, limit та page_token. Рядки історії містять event, replay_epoch, orphaned та delivered_at; orphaned: true позначає блок, який згодом було замінено. Запит до історії може повернути 402 insufficient_balance (data.reason: balance_exhausted або free_grant_exhausted), 403 key_cap_exhausted (data.cu_cap) або 429 rate_limited (key_rate_limit або free_plan_call_limit). Помилка 429 cost_exceeds_burst містить причину request_exceeds_burst та data.max: збільште пікову пропускну здатність перед повторною спробою. Інструкції щодо невалідних діапазонів та повторних спроб див. у розділі Обробка помилок.
Тарифікація та приклад
Вага розраховується згідно з GET /v1/plans. Доставлені події даних, успішні запити до історії та адресо-дні мають окрему вагу; виклики керування, окрім історії, керуючі події, невдалі доставки та автоматичні повторні спроби є безкоштовними. За кожну доставлену подію плата стягується один раз; повторне відтворення за запитом клієнта та повторна доставка канонічних подій тарифікуються як нові доставки.
Тарифікація адрес використовує найбільшу кількість адрес у кожній підписці протягом її перебування в онлайні за добу UTC після вирахування безкоштовної квоти адрес акаунта, яка розподіляється між підписками (спочатку для старіших підписок). Одна й та сама адреса у двох підписках враховується двічі; додавання мереж змінює плату за події, а не за адреси. Підписка, що перебувала офлайн протягом усієї доби за UTC, не тарифікується за адреси.
| Використання | Розрахункова одиниця | CU |
|---|---|---|
push.address_day | Оплачувана адреса-день | 33 |
push.history | Успішний запит історії | 25 |
push.log | Доставлена подія даних | 150 |
push.native_transfer | Доставлена подія даних | 150 |
push.token_transfer | Доставлена подія даних | 150 |
Безкоштовних адрес на акаунт за день UTC: 1000
Ліміт безкоштовних адрес на акаунт за день UTC, спільний для всіх груп підписки незалежно від плану. Для кожної групи враховується максимальна кількість адрес під час її перебування онлайн протягом цього дня; виділення ліміту здійснюється за зростанням ID групи. Одна й та сама адреса у двох групах враховується двічі; кількість мереж у групі не примножує кількість адрес. Група, яка була офлайн або видалена протягом усього дня, нічого не додає. Для кожної групи кількість адрес, що залишилася після використання її частки ліміту, множиться на вагу CU `push.address_day` у `method_weights`. Поточний налаштований ліміт походить із тієї самої політики ціноутворення, що й плата за адресу-день; він не є лімітом місткості акаунта чи окремим лімітом для кожної групи.
Приклад: 10 доставлених подій native.transfer, 2 успішні запити історії та 10 оплачуваних адрес-днів коштують 10 × 150 + 2 × 25 + 10 × 33 = 1880 CU. Оплачувані адреси-дні враховуються після вичерпання ліміту безкоштовних адрес акаунта.
Правила обліку та конвертації CU див. у правилах тарифікації та на сторінці цін.
Пов'язані ресурси
- Порівняйте підтримувані події, покриття мереж та ціни в огляді Blockchain Webhook API.
Востаннє оновлено:
Один ключ, багато мереж
Один і той самий API key працює в усіх підтримуваних мережах. Дізнайтеся, як структуровані URL, як програмно знаходити мережі та як об'єднуються баланси й ліміти.
Правила тарифікації
Детальний розбір правил тарифікації для кодів стану HTTP, помилок JSON-RPC та Data API з рекомендованими діями для розробників.