ドキュメント
料金GitHubダッシュボード

Quick start

Your first incoming-transfer webhook

Use the cabinet to receive a native-coin transfer on a test network. Open Notifications → Delivery settings and click Set up transfer alerts for checks against your current settings. You can complete this flow without an API key; SDK automation is available afterwards.

WebhooksTestnetAddress Watch

← すべてのガイド

手順

  1. Register and complete the email-code step. If your account has no address slots, activate the Starter trial from the cabinet banner when offered, or choose a plan with Address Watch. Free has no address slots, including for test deliveries.
  2. In Notifications → Delivery settings, click Set up transfer alerts, select a network, then choose one of its test environments. The wizard also supports mainnet. Use Ethereum Sepolia if offered; otherwise use another listed test network with a wallet and faucet you can access.
  3. Create a receiving account in a testnet wallet you control. Obtain test coins in a separate sending account from a faucet for the exact selected environment. Use a small native-coin transfer for this first test.
  4. Save the public HTTPS URL of your receiver in the wizard. For local development, first start a tunnel to port 3000 and append /webhook to its public URL. Copy the signing secret from the Test delivery step into RPCNODE_WEBHOOK_SECRET and start the receiver below.
  5. Click Send test webhook. Wait for status sent and check that the receiver prints type test. For pending or dead deliveries, inspect the delivery log and its response/error. A successful test verifies the delivery path.
  6. In the wizard, add the receiving address on the selected test network before sending funds. Continue to Enable incoming transfers and click Enable subscription.
  7. Send a small native-coin transfer from the funded sending account to the watched receiving address. Confirm that your wallet is using the selected testnet. Keep the transaction hash.
  8. After the transaction enters a scanned block, look for type payment, event detected, the expected network, address and tx_hash. Wait for event confirmed before treating it as final. Check the same delivery in the cabinet log. No fixed latency is promised; block production, scanner lag and confirmation settings affect the wait.

Expose your local receiver

After installing cloudflared, run this in a separate terminal. Save the printed HTTPS URL with /webhook appended. Keep the tunnel running.

cloudflared tunnel --url http://127.0.0.1:3000

Run the receiver

Node.js 20+; save the next example as receiver.mjs. No packages required.

RPCNODE_WEBHOOK_SECRET='your-webhook-signing-secret' node receiver.mjs

receiver.mjs

import { createServer } from 'node:http'
import { createHmac, timingSafeEqual } from 'node:crypto'

const secret = process.env.RPCNODE_WEBHOOK_SECRET
if (!secret) throw new Error('Set RPCNODE_WEBHOOK_SECRET from your Webhook settings')
const port = Number(process.env.PORT ?? 3000)
const seen = new Map()
const windowMs = 5 * 60 * 1000

createServer(async (req, res) => {
  if (req.method !== 'POST' || req.url !== '/webhook') {
    res.writeHead(404).end()
    return
  }
  try {
    const chunks = []
    let size = 0
    for await (const chunk of req) {
      size += chunk.length
      if (size > 1024 * 1024) {
        res.writeHead(413).end()
        return
      }
      chunks.push(chunk)
    }
    const body = Buffer.concat(chunks)
    const signature = req.headers['x-rpcnode-signature']
    const fields = Object.fromEntries(String(signature ?? '').split(',').map((part) => {
      const i = part.indexOf('=')
      return [part.slice(0, i).trim(), part.slice(i + 1).trim()]
    }))
    const timestamp = Number(fields.t)
    const validTime = Number.isInteger(timestamp) && Math.abs(Date.now() - timestamp * 1000) <= windowMs
    const expected = createHmac('sha256', secret).update(fields.t + '.').update(body).digest()
    const given = Buffer.from(fields.v1 ?? '', 'hex')
    if (!validTime || !/^[a-f0-9]{64}$/.test(fields.v1 ?? '') ||
        given.length !== expected.length || !timingSafeEqual(given, expected)) {
      res.writeHead(401).end()
      return
    }
    const event = JSON.parse(body.toString('utf8'))
    const id = req.headers['x-rpcnode-delivery']
    if (typeof id !== 'string' || !id) {
      res.writeHead(400).end()
      return
    }
    for (const [key, expires] of seen) {
      if (expires <= Date.now()) seen.delete(key)
    }
    if (!seen.has(id)) {
      console.log(JSON.stringify(event, null, 2))
      // For this demo, handling means printing the verified event.
      seen.set(id, Date.now() + 24 * 60 * 60 * 1000)
    }
    res.writeHead(200).end('ok')
  } catch (error) {
    console.error(error.message)
    if (!res.headersSent) res.writeHead(400).end()
  }
}).listen(port, '127.0.0.1', () => console.log('Receiver on http://127.0.0.1:' + port + '/webhook'))

ヒント

  • Use a public HTTPS tunnel or deploy the receiver on a public host. A hosted worker cannot reach your laptop at 127.0.0.1. Private IPs are refused by default. WEBHOOK_ALLOW_PRIVATE_URLS is a worker-side setting for a fully local backend.
  • Use the watch network key consistently: ethereum means mainnet, ethereum-sepolia means Sepolia. RPC availability alone does not mean that address events are available.
  • Address Watch calls incoming transfers payment. The separate Payments product emits payment_order; you do not need a Payments order for this guide.
  • The receiver is a logging demo with in-memory deduplication, lost on restart. For business processing, persist events and their delivery_id atomically before returning 2xx. Retries keep the same delivery_id; confirmation updates are separate events.
  • Enable only incoming transfers for the selected network. Block subscriptions deliver raw blocks and require a different receiver and a larger body limit.
  • Faucet eligibility and wallet setup depend on the network. Have test coins ready before spending time debugging webhook delivery.

If the transfer does not appear

First check the transaction hash in the selected testnet explorer. Confirm the destination equals the watched address and that the watch was active before the transfer.

If there is no delivery row, check the selected environment, incoming subscription, webhook switch, available credits and address activity. If address activity is also missing, contact support with the network, address and transaction hash so the scan can be checked.

If there is a delivery row, inspect its status and error. Check public URL reachability, tunnel lifetime, signature secret and receiver HTTP status. Pending deliveries retry automatically; a dead delivery needs investigation.