# การตั้งค่า Blockchain Webhook: ลายเซ็น การขจัดข้อมูลซ้ำ และการ Replay

> Source: https://docs.blockvectra.com/th/guides/webhook-push/

ติดตามแอดเดรสกระเป๋าเงิน EVM และรับข้อมูลการโอนสินทรัพย์เนทีฟ การโอนโทเคน และ contract log ที่ตรงกัน ณ HTTPS endpoint ของคุณสำหรับการแจ้งเตือนกิจกรรมของกระเป๋าเงินหรือการตรวจสอบเหตุการณ์สมาร์ตคอนแทร็กต์ นักพัฒนาและ AI Agent ใช้ HTTP subscription API เดียวกัน สำหรับการแจ้งเตือนการชำระเงิน ERC-20 USDT / USDC โปรดทำตาม [ตัวรับการชำระเงินสเตเบิลคอยน์](https://docs.blockvectra.com/en/guides/stablecoin-payments/#receive-payments-with-webhooks)

* **ขั้นตอนแรก:** [ปรับใช้ตัวรับที่ตรวจสอบลายเซ็น raw-body](#verify-signatures) โดยใช้ตัวอย่างการตรวจสอบด้านล่าง
* **สำเร็จเมื่อ:** หลังจาก `applied_version >= change_version` กิจกรรมบนเชนที่ตรงกันจะส่งไปยังตัวรับของคุณ ผ่านการตรวจสอบลายเซ็น และได้รับการบันทึกตาม `id` ของเหตุการณ์; การสร้างการติดตามจะไม่ส่งข้อความทดสอบ

[ตัวเลือกการเข้าถึง Webhook](https://blockvectra.com/en/webhooks/)

## งานที่คู่มือนี้ช่วยให้คุณทำสำเร็จ

* [รับกิจกรรมของแอดเดรสกระเป๋าเงิน](#connect-wallet-address-activity) โดยการสร้างการติดตามที่ผ่านการยืนยันตัวตน, เพิ่มแอดเดรสที่ต้องการติดตาม และตรวจสอบเหตุการณ์ที่เข้ามา
* [ตรวจสอบ contract log ที่ตรงกัน](#event-format) โดยการตรวจสอบเหตุการณ์ `log` สำหรับแอดเดรสที่ติดตาม และกรอง `address`, `topics` และ `data` ในตัวรับของคุณ
* [กู้คืนการนำส่งที่หยุดชะงัก](#delivery-retries-and-replay) โดยการตรวจสอบความคืบหน้าของการติดตามและ replay ข้อมูลที่ตรงกันซึ่งเก็บรักษาไว้ จากนั้นดึงข้อมูลย้อนหลังสำหรับช่วงที่ขาดหายไปซึ่งอยู่นอกหน้าต่าง replay

การติดตามรวม HTTPS receiving URL หนึ่งรายการ, signing secret หนึ่งรายการ, แอดเดรส EVM ที่ติดตาม และออบเจกต์ `chains` ที่จำเป็นเข้าด้วยกัน แอดเดรสจะถูกนำไปใช้กับทุกเชนในออบเจกต์นั้น ใช้งาน API ด้วยส่วนหัว `x-api-key`; คีย์ที่ใช้งานอยู่ใดๆ ในบัญชีของคุณสามารถจัดการการติดตามทั้งหมดของบัญชีได้ [รับ API key](https://blockvectra.com/en/get-api-key/) ก่อนเริ่มต้น [Push OpenAPI](https://docs.blockvectra.com/openapi/push.yaml) แสดงรายการทุกการดำเนินการและสคีมา webhook

## เชื่อมต่อกิจกรรมของแอดเดรสกระเป๋าเงิน

1. ปรับใช้ตัวรับที่[ตรวจสอบเนื้อหาคำขอเดิม](#verify-signatures), บันทึกเหตุการณ์ตาม `id` และตอบรับภายใน 10 วินาที
2. อ่าน `GET /v1/push/chains` จากนั้น[สร้างการติดตาม](#create-a-subscription)ด้วย HTTPS URL ของคุณและเชนที่เลือก บันทึก `id` และ `secret` ที่ส่งกลับมาอย่างปลอดภัย
3. [เพิ่มแอดเดรสกระเป๋าเงิน](#add-and-list-addresses) รอจนกระทั่ง `applied_version >= change_version` และบันทึก `applied_from_block` ของแต่ละเชน; การจับคู่จะเริ่มต้นจากจุดนั้น
4. จัดการการโอนและ log และ[กู้คืนช่องว่างหรือบล็อกที่ถูกแทนที่](#delivery-retries-and-replay) กรองสัญญาโทเคน ผู้รับ และจำนวนเต็มก่อนนำการแจ้งเตือนไปใช้ในการประมวลผลการชำระเงิน

## เลือกระหว่าง Webhook, WebSocket หรือการโพลล์

* **Webhook** ส่งเหตุการณ์ของแอดเดรสที่ติดตามไปยังตัวรับ HTTPS พร้อมการลองส่งใหม่และการ replay ข้อมูลที่ตรงกันซึ่งเก็บรักษาไว้
* **[WebSocket](https://docs.blockvectra.com/en/guides/websocket-subscriptions/)** สตรีม `newHeads` และ `logs` ที่กรองแล้วผ่านการเชื่อมต่อแบบต่อเนื่อง เชื่อมต่อใหม่ สมัครรับข้อมูลใหม่ และคิวรีบล็อกที่ขาดหายไปหลังจากการตัดการเชื่อมต่อ
* **[การโพลล์](https://docs.blockvectra.com/en/guides/stablecoin-payments/)** คิวรี `eth_getLogs` ในช่วงบล็อกที่จำกัดขอบเขตด้วยเคอร์เซอร์ของคุณเอง; ใช้เพื่อตรวจสอบการชำระเงินหรือดึงข้อมูล log ที่ขาดหายไปย้อนหลัง

ตรวจสอบ `ws` และ `subscriptions` ใน `GET /v1/chains` สำหรับการรองรับ WebSocket หาก `ws` เป็น false ตัวเลือก Webhook สำหรับแอดเดรสยังคงใช้งานได้เมื่อเชนนั้นปรากฏในรายการ `GET /v1/push/chains` ที่ผ่านการยืนยันตัวตนแล้ว การรองรับ RPC เพียงอย่างเดียวไม่ได้หมายความว่ารองรับ Push

## ความจุของแอดเดรส

แบบบริการตนเองรองรับสูงสุด 1,000,000 แอดเดรสต่อการติดตาม และพร้อมใช้งานทันทีเมื่อลงทะเบียน การติดตามหนึ่งรายการครอบคลุมหลายเชนด้วย receiving URL เดียว ความจุระดับ Enterprise รองรับ 10,000,000 / 100,000,000 แอดเดรสต่อการติดตาม; [ติดต่อเราเพื่อเปิดใช้งาน](https://blockvectra.com/en/contact/) นักพัฒนาและ AI Agent มีตัวเลือกความจุและราคาเดียวกัน ทั้งสองระดับใช้อัตราแอดเดรส-วันและอัตราเหตุการณ์ที่นำส่งเดียวกันตามที่แสดงใน [หน้าราคา](https://blockvectra.com/en/pricing/)

## สร้างการติดตาม

อ่าน `GET /v1/push/chains` สำหรับเชนที่พร้อมใช้งานและจำนวนการยืนยันขั้นต่ำ ค่าเริ่มต้น และสูงสุด บล็อกจะถูกปล่อยเมื่อ `head - block + 1 >= confirmations` แต่ละเชนสามารถใช้ค่าเริ่มต้นได้โดยระบุ `{}` จำเป็นต้องระบุอย่างน้อยหนึ่งเชน; เชนใหม่จะไม่เข้าร่วมการติดตามที่มีอยู่โดยอัตโนมัติ

บันทึกตัวอย่างต่อไปนี้เป็น `create.json` โดยแทนที่ URL ด้วยตัวรับของคุณ และเลือกเชนจากรายการเชน URL ต้องใช้ HTTPS บนพอร์ต 443 ใช้ชื่อโฮสต์แทน IP literal และต้องไม่มีข้อมูลผู้ใช้หรือ fragment

```json
{
  "url": "https://hooks.example.com/push",
  "chains": {
    "bsc_mainnet": {
      "confirmations": 1
    },
    "base_mainnet": {}
  }
}
```

ตั้งค่า `BLOCKVECTRA_API_KEY` ในสภาพแวดล้อมของคุณ แล้วรัน:

```bash
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` ไว้อย่างปลอดภัย secret จะถูกส่งกลับมาเฉพาะตอนสร้างหรือเมื่อเรียก `POST /subscriptions/{subscription_id}/rotate-secret` เท่านั้น; การสลับคีย์จะมีผลทันทีในทุกเชนโดยไม่มีช่วงทับซ้อน จะไม่มีการส่งข้อความทดสอบ

## เพิ่มและแสดงรายการแอดเดรส

บันทึกชุดแอดเดรสเป็น `addresses.json` โดยแทนที่แอดเดรสต้วอย่างด้วยแอดเดรสที่คุณต้องการติดตาม:

```json
{
  "addresses": [
    "0x9725db73f2cd8657f3e1841e5689f210ee54a92d",
    "0x99d47bB552ae095159C251836De6A5d524076872"
  ]
}
```

ตั้งค่า `SUBSCRIPTION_ID` เป็น subscription ID ที่ส่งกลับมา:

```bash
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"
```

การเรียก add แต่ละครั้งรับได้สูงสุด 10,000 แอดเดรส แอดเดรสขาเข้าต้องเป็นตัวพิมพ์เล็กหรือตัวพิมพ์ผสมตามมาตรฐาน EIP-55 ที่ถูกต้อง; ข้อมูลขาเข้าที่ไม่ถูกต้องจะปฏิเสธทั้งชุด แอดเดรสที่ซ้ำจะถูกนับเป็น `unchanged` ดังนั้นการส่งคำขอ add เดิมซ้ำจึงปลอดภัย รายการแอดเดรสใช้ `limit` และ `page_token`; `next_page_token: null` ระบุว่าเป็นหน้าสุดท้าย

การเพิ่มแอดเดรสจะส่งกลับ `change_version` โพลล์หรือตรวจสอบ `GET /subscriptions/{subscription_id}` จนกระทั่ง `applied_version >= change_version`; โดยปกติการเปลี่ยนแปลงจะใช้เวลาประมาณ 1 วินาทีในการมีผล `applied_from_block` ของแต่ละเชนจะระบุบล็อกที่มีผลซึ่งธุรกรรมและ log บนเชนจะเริ่มถูกจับคู่ แอดเดรสใหม่จะไม่ถูกจับคู่ย้อนหลัง

การสร้างการติดตามจะส่งกลับ HTTP 201 เพื่อยืนยันว่ารีซอร์สการติดตามถูกสร้างขึ้นแล้ว; HTTP 201 ไม่ได้หมายความว่าตัวรับของคุณได้รับ webhook push ใดๆ แล้ว แพลตฟอร์มไม่ได้ส่งข้อความยืนยันหรือข้อความทดสอบเมื่อสร้างหรือลงทะเบียนแอดเดรส คุณต้องรอจนกว่าจะมีกิจกรรมบนเชนที่ตรงกันเกิดขึ้นบนแอดเดรสและเชนที่ติดตามเพื่อยืนยันการนำส่งที่ตัวรับของคุณ

## รูปแบบเหตุการณ์

ทุก POST จะมี `type: push.events`, `created_at` และ `data` โดย `data` ประกอบด้วย `subscription_id`, `chain` หนึ่งเชน, `complete_through_block` และ `events` บันทึกความคืบหน้ารายเชน: บล็อกหนึ่งบล็อกสามารถข้ามหลายข้อความได้ ดังนั้นหมายเลขบล็อกของแต่ละเหตุการณ์จึงไม่ใช่เครื่องหมายระบุความสมบูรณ์ แต่ละข้อความประกอบด้วยเหตุการณ์ได้สูงสุด 1,000 รายการ ขนาด 1 MiB และ 50 บล็อก

```json
{
  "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"
          }
        ]
      }
    ]
  }
}
```

| Event type         | What to handle                                                                                                                                                                                              |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `native.transfer`  | การโอนมูลค่าเนทีฟระดับบนสุดที่สำเร็จซึ่งเกี่ยวข้องกับแอดเดรสที่ติดตาม; `amount` เป็นสตริงทศนิยมจำนวนเต็ม ไม่รวมการโอนเนทีฟภายใน                                                                             |
| `token.transfer`   | การโอน ERC-20, ERC-721 และ ERC-1155 ที่เกี่ยวข้องกับแอดเดรสที่ติดตาม; ตรวจสอบ `standard`, `token`, `token_id`, `amount` และ `batch_index` การโอนแบบกลุ่ม ERC-1155 จะสร้างหนึ่งเหตุการณ์ต่อหนึ่งรายการ       |
| `log`              | log อื่นๆ ที่ระบุชื่อแอดเดรสที่ติดตามเป็นสัญญาผู้ส่งออกหรือใน topics 1–3; ตรวจสอบ `address`, `topics`, `data` และ `matched`                                                                                 |
| `subscription.gap` | ช่วงตั้งแต่ `from_block` ถึง `to_block` ไม่พร้อมสำหรับการนำส่ง โดยมี `reason: retention_expired`; ดึงข้อมูลย้อนหลังด้วย Data API หรือ `eth_getLogs`                                                         |
| `chain.reorg`      | การแจ้งเตือน reorg ฟรี: บล็อกที่นำส่งแล้วใน `from_block`–`to_block` ถูกแทนที่ ให้ทำเครื่องหมายหรือละทิ้งเหตุการณ์เก่าตาม `ref` จากนั้นเก็บเหตุการณ์ canonical ที่ส่งซ้ำโดยอัตโนมัติและขจัดข้อมูลซ้ำตาม `id` |

ภายในการติดตามเดียวกัน ให้ขจัดข้อมูลซ้ำตาม `id` ของเหตุการณ์; ข้ามการติดตามให้ใช้ `ref` และ `type` ละเว้นฟิลด์และประเภทเหตุการณ์ที่ไม่รู้จัก ตรวจสอบข้อเท็จจริงบนเชนก่อนดำเนินการทางการเงิน

## ตรวจสอบลายเซ็น

ส่วนหัวคือ `webhook-id`, `webhook-timestamp`, `webhook-signature` และ `bv-subscription-id` เลือก secret เฉพาะจากการติดตามที่คุณสร้างขึ้นเท่านั้น; ปฏิเสธ ID ที่ไม่รู้จัก ตรวจสอบ HMAC-SHA256 บน `webhook-id.webhook-timestamp.raw-body` โดยใช้ไบต์ของเนื้อหาคำขอเดิมก่อนที่จะแปลง JSON ลายเซ็นคือ `v1,<base64>`; อนุญาตให้เวลาคลาดเคลื่อนได้ประมาณห้านาที และเปรียบเทียบในแบบเวลาคงที่

ฟังก์ชัน Node.js นี้รับ raw body เป็น `Buffer`, ส่วนหัวคำขอ และแมปของ subscription ID กับ secret ที่จัดเก็บไว้:

```js
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 วินาที ส่วนหัว subscription ID จะยังไม่ได้รับความไว้วางใจจนกว่าจะตรวจสอบลายเซ็นแล้ว

## ตรวจสอบเหตุการณ์แรกของคุณ

รักษาการติดตามให้อยู่ในสถานะ online หลังจากที่การเปลี่ยนแปลงแอดเดรสมีผลบังคับใช้แล้ว ให้รอกิจกรรมบนเชนที่ตรงกันและตรวจสอบว่าตัวรับของคุณตรวจสอบและจัดเก็บเหตุการณ์ไว้อย่างถาวร

## หยุดการรับฟังหลังจากการตรวจสอบ

<a id="stop-listening-and-clean-up" />

หากต้องการหยุดติดตามแอดเดรส ให้บันทึกแอดเดรสที่จะลบออกใน `addresses.json` แล้วเรียก `POST /subscriptions/{subscription_id}/addresses/remove`:

```bash
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
```

การเรียก remove แต่ละครั้งรับได้สูงสุด 10,000 แอดเดรส แอดเดรสที่ไม่ได้ติดตามอยู่ในปัจจุบันจะนับเป็น `unchanged` การเรียกจะส่งกลับ `change_version` เมื่อ `applied_version >= change_version` บล็อกตั้งแต่บล็อกที่มีผลนั้นเป็นต้นไปจะไม่จับคู่กับแอดเดรสที่ถูกลบออกอีกต่อไป เหตุการณ์ที่จับคู่ไปก่อนหน้านี้ (กำลังส่ง ลองส่งใหม่ หรืออยู่ในคิว) จะยังคงถูกนำส่ง; เหตุการณ์ที่นำส่งไปแล้วจะไม่ถูกเพิกถอน

หากต้องการหยุดรับฟังชั่วคราวโดยไม่ลบการกำหนดค่าหรือแอดเดรส ให้ตั้งค่า `status` เป็น `offline`:

```bash
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, secret, แอดเดรส, เชน และ confirmations) จะยังคงได้รับการเก็บรักษาไว้ การ patch ด้วย `{"status":"online"}` จะกลับมาเริ่มรับฟังต่อจากบล็อกที่มีผลในปัจจุบัน และจะไม่ดึงข้อมูลย้อนหลังสำหรับช่วงเวลาที่ออฟไลน์

ใช้ JSON Merge Patch บน `PATCH /subscriptions/{subscription_id}` เพื่อเปลี่ยน `url`, `key_id`, `status` หรือ `chains`: ออบเจกต์เชนจะเพิ่มหรืออัปเดตเชน และ `null` จะลบเชนนั้นออก ต้องคงเหลือไว้อย่างน้อยหนึ่งเชน หากต้องการลบการติดตามอย่างถาวร:

```bash
curl --fail-with-body -sS -X DELETE "$PUSH_URL/subscriptions/$SUBSCRIPTION_ID" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY"
```

`DELETE` จะลบการติดตามอย่างถาวร หยุดการนำส่งข้ามทุกเชนในทันที และทำลาย secret ตลอดจนแอดเดรส

## การนำส่ง การลองส่งใหม่ และการ Replay

การนำส่งเป็นแบบอย่างน้อยหนึ่งครั้ง แต่ละเชนของการติดตามจะเรียงลำดับตามบล็อกและตำแหน่งในบล็อก; แบทช์ที่ล้มเหลวจะบล็อกเหตุการณ์ที่ตามมาบนเชนนั้น เชนที่ต่างกันจะมีความคืบหน้าที่เป็นอิสระต่อกันและสามารถ 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`; โดยเงื่อนไขสุดท้ายกำหนดให้ต้อง patch `key_id` ไปยังคีย์อื่นของบัญชีที่ยังใช้งานอยู่

เหตุการณ์ที่ไม่ได้นำส่งจะหมดอายุเมื่ออยู่นอกหน้าต่างการเก็บรักษา และทำให้เกิด `subscription.gap` เมธอด `POST /subscriptions/{subscription_id}/replay` รับ `chain` และ `from_block`; โปรดตรวจสอบ `replayable_from_block` ใน `GET /push/chains` และความคืบหน้าของการติดตาม การ replay จะนำส่งข้อมูลที่ตรงกันที่มีอยู่เดิมและไม่สามารถกู้คืนเหตุการณ์ที่เกิดขึ้นก่อนที่จะเพิ่มแอดเดรสหรือเชนได้

`chain.reorg` แจ้งเตือนให้คุณทราบว่าบล็อกที่นำส่งไปแล้วถูกแทนที่; ไม่ได้บ่งชี้ถึงช่องว่างในการนำส่ง reorg ที่ตื้นกว่าจำนวน confirmation ของคุณจะไม่ปรากฏให้เห็น สำหรับ reorg ที่ส่งผลกระทบต่อบล็อกที่นำส่งแล้วลึกไม่เกิน 1,024 บล็อก เหตุการณ์ canonical จะถูกนำส่งใหม่อัตโนมัติด้วย `id` ใหม่ ให้ทำเครื่องหมายหรือละทิ้งเหตุการณ์ที่ถูกแทนที่ตาม `ref` เก็บเหตุการณ์ canonical ไว้ และขจัดข้อมูลซ้ำตาม `id`; สำหรับบันทึกการชำระเงิน ให้กระทบยอดตาม `ref` และ `tx_hash` ส่วน reorg ที่ลึกกว่านั้นจะระงับการทำงานของเชน: ตรวจสอบ `halted` ใน `GET /push/chains`; การนำส่ง canonical ใหม่อัตโนมัติจะตามมาหลังจากกู้คืนเชนแล้ว เหตุการณ์ควบคุมจะไม่เลื่อน `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` มี reason เป็น `request_exceeds_burst` และ `data.max`: ให้เพิ่มความจุ burst ก่อนลองใหม่ ดู [การจัดการข้อผิดพลาด](https://docs.blockvectra.com/en/errors/) สำหรับช่วงที่ไม่ถูกต้องและคำแนะนำในการลองใหม่

## การเรียกเก็บเงินและตัวอย่าง

น้ำหนักมาจาก `GET /v1/plans` เหตุการณ์ข้อมูลที่นำส่งแล้ว, คำขอประวัติที่สำเร็จ และแอดเดรส-วัน จะมีน้ำหนักแยกจากกัน; การเรียกการจัดการนอกเหนือจากประวัติ, เหตุการณ์ควบคุม, การนำส่งที่ล้มเหลว และการลองใหม่อัตโนมัติจะไม่มีค่าบริการ เหตุการณ์ที่นำส่งแต่ละรายการจะถูกคิดค่าบริการครั้งเดียว; การ replay ของลูกค้าและการนำส่งเหตุการณ์ canonical ซ้ำจะมีค่าบริการการนำส่งใหม่

การเรียกเก็บเงินของแอดเดรสจะใช้จำนวนแอดเดรสสูงสุดของการติดตามแต่ละรายการระหว่างช่วงเวลา online ของวัน UTC หลังจากหักโควตาแอดเดรสฟรีของบัญชีที่แชร์ข้ามการติดตาม (การติดตามที่เก่ากว่าได้รับสิทธิ์ก่อน) แอดเดรสเดียวกันในการติดตามสองรายการจะถูกนับสองครั้ง; การเพิ่มเชนจะเปลี่ยนค่าบริการเหตุการณ์ แต่ไม่เปลี่ยนค่าบริการแอดเดรส การติดตามที่ offline ตลอดทั้งวัน 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` โควตาที่กำหนดค่าไว้ในปัจจุบันมาจากนโยบายราคาเดียวกันกับที่ใช้สำหรับค่าบริการที่อยู่-วัน ซึ่งไม่ใช่ขีดจำกัดความจุของบัญชีหรือโควตาแยกต่างหากต่อกลุ่ม

ตัวอย่าง: เหตุการณ์ native.transfer ที่ส่งมอบแล้ว 10 เหตุการณ์, คำขอประวัติที่สำเร็จ 2 คำขอ และที่อยู่-วันที่คิดค่าบริการ 10 ที่อยู่-วัน มีค่าใช้จ่าย 10 × 150 + 2 × 25 + 10 × 33 = 1880 CU ที่อยู่-วันที่คิดค่าบริการจะถูกนับหลังจากหักโควตาที่อยู่ฟรีของบัญชีแล้ว

ดู [กฎการเรียกเก็บเงิน](https://docs.blockvectra.com/en/guides/billing-rules/) และ [หน้าราคา](https://blockvectra.com/en/pricing/) สำหรับการวัดปริมาณและการแปลง CU

## แหล่งข้อมูลที่เกี่ยวข้อง

* เปรียบเทียบเหตุการณ์ที่รองรับ ความครอบคลุมของเชน และราคาใน [ภาพรวม Blockchain Webhook API](https://blockvectra.com/en/webhooks/)
