Skip to main content

Prerequisites

  • A WalletSuite API key, passed as the x-api-key header on every management request (same scheme as the rest of the API).
  • A publicly reachable HTTPS URL for delivery. Private, internal, and link-local addresses are rejected with WEBHOOK_URL_NOT_ALLOWED.
export WALLETSUITE_API_KEY="your-api-key"

Steps

1

Provision your signing secret

Every delivery is signed with a per-account secret using the Standard Webhooks scheme. Provision it once:
Provision signing secret
curl -X POST "https://api.walletsuite.io/api/notifications/signing-secret" \
  -H "x-api-key: $WALLETSUITE_API_KEY"
The response returns the secret inside the standard {ok, code, message, data} envelope:
Response
{
  "ok": true,
  "code": "OK",
  "message": "ok",
  "data": {
    "secret": "whsec_MfKQ9r8g...base64...=="
  }
}
The secret is shown once and never returned again. Copy it now and store it in your secrets manager. If you lose it, you must rotate it - there is no read-back endpoint.
Calling this endpoint again when a secret already exists returns 409 SIGNING_SECRET_EXISTS. To replace a lost or compromised secret, use POST /api/notifications/signing-secret/rotate instead.
export WALLETSUITE_WEBHOOK_SECRET="whsec_MfKQ9r8gMfKQ9r8g..."
2

Stand up a verifying receiver

Your endpoint receives a POST, verifies the signature, and acknowledges with a 2xx. With the official standardwebhooks library that is a few lines of code: pass it the raw request body and the Webhook-* headers, and it returns the verified, parsed event.
Install
npm install express standardwebhooks
server.js
const express = require("express");
const { Webhook } = require("standardwebhooks");

const app = express();
const wh = new Webhook(process.env.WALLETSUITE_WEBHOOK_SECRET);

app.post(
  "/webhooks/walletsuite",
  express.raw({ type: "application/json" }),
  (req, res) => {
    let event;
    try {
      event = wh.verify(req.body, {
        "webhook-id": req.header("webhook-id"),
        "webhook-timestamp": req.header("webhook-timestamp"),
        "webhook-signature": req.header("webhook-signature"),
      });
    } catch (err) {
      return res.status(400).send("invalid signature");
    }

    res.status(204).end();

    handleEvent(event).catch((e) =>
      console.error("processing failed", event.event_id, e)
    );
  }
);

async function handleEvent(event) {
  console.log(event.event_type, event.event_id, event.data);
}

app.listen(3000, () => console.log("listening on :3000"));
Run
WALLETSUITE_WEBHOOK_SECRET="$WALLETSUITE_WEBHOOK_SECRET" node server.js
Want the full verification contract - constant-time comparison, replay defense, and a from-scratch reference implementation - see Verify Signatures.
3

Create the subscription

This quickstart subscribes to incoming transfers (transfer.received) as a worked example - the event catalog lists every event type and its payload. Use an address you control so you can trigger one in the next step. Set chain, the address, both asset-scope filters in eventKinds, and your public webhookUrl:
Create subscription
curl -X POST "https://api.walletsuite.io/api/notifications/subscriptions" \
  -H "x-api-key: $WALLETSUITE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chain": "ethereum",
    "address": "0xYourWatchedAddressHere",
    "eventKinds": ["INCOMING_NATIVE", "INCOMING_FUNGIBLE"],
    "webhookUrl": "https://yourapp.com/webhooks/walletsuite"
  }'
The API responds 202 Accepted with the subscription. It registers and flips from pending to active within moments:
Response
{
  "ok": true,
  "code": "OK",
  "message": "accepted",
  "data": {
    "id": "9b7c1e2a-4f6d-4c3b-8a1e-2d5f7a9c0b13",
    "chain": "ethereum",
    "address": "0xYourWatchedAddressHere",
    "eventKinds": ["INCOMING_NATIVE", "INCOMING_FUNGIBLE"],
    "webhookUrl": "https://yourapp.com/webhooks/walletsuite",
    "status": "pending",
    "createdAt": "2026-05-26T14:22:48.123456Z"
  }
}
Confirm it is active:
Check status
curl "https://api.walletsuite.io/api/notifications/subscriptions/9b7c1e2a-4f6d-4c3b-8a1e-2d5f7a9c0b13" \
  -H "x-api-key: $WALLETSUITE_API_KEY"
If the subscription does not go active, see Troubleshooting.
4

Trigger and observe

Trigger a real event by sending a small transfer to the watched address on the same chain. On the first confirmation of an incoming transfer, WalletSuite delivers a transfer.received event to your endpoint.The delivered request looks like this:
Delivered request
POST /webhooks/walletsuite HTTP/1.1
Host: yourapp.com
Content-Type: application/json; charset=utf-8
User-Agent: WalletSuite-Webhooks/1.0
Webhook-Id: 3f0a8d52-7e14-4b9c-a6d2-c8e1f4b09a7d
Webhook-Timestamp: 1779805371
Webhook-Signature: v1,g0hM9SsE+OTbJCwQ8...base64...=

{
  "schema_version": "1.0",
  "event_id": "3f0a8d52-7e14-4b9c-a6d2-c8e1f4b09a7d",
  "event_type": "transfer.received",
  "subscription_id": "9b7c1e2a-4f6d-4c3b-8a1e-2d5f7a9c0b13",
  "occurred_at": "2026-05-26T14:22:48.123Z",
  "data": {
    "chain": "ethereum",
    "direction": "incoming",
    "tx_hash": "0x9f3...",
    "block_number": 18573245,
    "from": "0x123...",
    "to": "0xabc...",
    "amount": "1.5",
    "asset_type": "erc20",
    "asset_contract": "0xa0b8..."
  }
}

Next steps

Verify Signatures

The full Standard Webhooks verification contract - raw-body handling, replay protection, and from-scratch code.

Delivery & Retries

The 18-second ack budget, the retry schedule, at-least-once semantics, and failure handling.

Events & Payloads

Field-by-field payload reference, transfer.received, Tron address encoding, and payload versioning.

Best Practices

Idempotency, async processing, secret rotation, and production hardening for your receiver.