Skip to main content
How-To9 min read

How to Monitor XRPL Wallets in Real Time with Webhooks

Step-by-step guide to monitoring XRP Ledger wallets in real time using XRNotify webhooks, from account creation to signature verification and production scaling.

ByAli Morgan·

Monitoring XRP Ledger wallets in real time is essential for exchanges, payment processors, NFT marketplaces, and any application that needs to react instantly to on-chain activity. Rather than polling the XRPL every few seconds and burning through rate limits, you can use XRNotify to push normalized webhook events directly to your server the moment a transaction is validated. This guide walks you through the entire process, from creating your XRNotify account to scaling a production-grade monitoring pipeline across hundreds of wallets.

By the end of this tutorial you will have a fully working webhook integration that receives real-time notifications for payments, trust-line changes, escrow activity, and more, all verified with HMAC signatures and hardened against delivery failures. Let us get started.

Step 1: Create an XRNotify Account and Get Your API Key

Before you can create webhook subscriptions, you need an XRNotify account and an API key. Head to xrnotify.io/signup and create a free account. XRNotify offers a generous free tier that includes up to 10 webhook subscriptions and 10,000 deliveries per month, more than enough to get started.

Once you have signed in, navigate to Settings → API Keys in the XRNotify dashboard. Click Generate New Key. You will see your API key exactly once, so copy it to a secure location such as a password manager or an environment variable in your deployment pipeline. The key is a long, random string that looks like this:

xrn_live_k1_9f84a2c...your-secret-key

XRNotify also generates a webhook signing secret for each subscription you create. You will use this secret later to verify that incoming payloads genuinely originated from XRNotify and have not been tampered with in transit. Keep both the API key and the signing secret confidential; never commit them to version control.

Step 2: Create a Webhook Subscription with Account Filters

A webhook subscription tells XRNotify which events you care about and where to deliver them. Each subscription includes a destination URL (your server endpoint), one or more event types, and optional account filters that narrow delivery to specific XRPL addresses.

Use the XRNotify REST API to create a subscription. The following curl command creates a webhook that fires whenever a payment or trustline_change event touches either of the two specified XRPL wallets:

curl -X POST https://api.xrnotify.io/v1/webhooks \
  -H "Authorization: Bearer xrn_live_k1_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.example.com/webhooks/xrpl",
    "event_types": ["payment", "trustline_change"],
    "account_filters": [
      "rN7n3473SaZBCG4dFL83w7p1W9cgZw6im3",
      "rLdinLq5CJood9wdjY9ZCdgycK8KGEvkUj"
    ],
    "description": "Production wallet monitor"
  }'

XRNotify responds with a JSON object that contains the subscription ID and your unique signing secret. Store the signing_secret securely; you will need it in Step 4 to verify the HMAC signature on every incoming webhook delivery:

{
  "id": "wh_3kTm8vQpZr1x",
  "url": "https://your-app.example.com/webhooks/xrpl",
  "event_types": ["payment", "trustline_change"],
  "account_filters": [
    "rN7n3473SaZBCG4dFL83w7p1W9cgZw6im3",
    "rLdinLq5CJood9wdjY9ZCdgycK8KGEvkUj"
  ],
  "signing_secret": "whsec_a8b3f...your-signing-secret",
  "status": "active",
  "created_at": "2026-04-04T12:00:00Z"
}

You can also create subscriptions through the XRNotify dashboard if you prefer a graphical interface. Navigate to Webhooks → New Subscription, fill in the same fields, and click Create. The dashboard displays the signing secret in a one-time modal immediately after creation.

Step 3: Set Up Your Endpoint to Receive POST Requests

XRNotify delivers each event as an HTTP POST request to the URL you specified. Your endpoint needs to accept JSON bodies and respond with a 2xx status code within 15 seconds. Any response outside the 200-299 range, or a timeout, is treated as a delivery failure and triggers XRNotify's automatic retry logic.

Here is a minimal Express.js handler that receives XRNotify webhook payloads and acknowledges them:

const express = require('express');
const app = express();

// XRNotify sends JSON payloads, so parse the raw body for
// signature verification and the JSON body for processing.
app.post(
  '/webhooks/xrpl',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const rawBody = req.body; // Buffer for HMAC verification
    const event = JSON.parse(rawBody.toString());

    console.log('Received XRNotify event:', event.event_type);

    // TODO: Verify signature (Step 4)
    // TODO: Process event (Step 5)

    // Return 200 to acknowledge receipt
    res.status(200).json({ received: true });
  }
);

app.listen(3000, () => {
  console.log('Webhook server listening on port 3000');
});

A few important considerations for your endpoint. First, always parse the body as raw bytes before verifying the signature, then parse it as JSON. If you let a middleware parse the JSON first, the re-serialized bytes may differ from the original payload, causing the HMAC check to fail. Second, keep your handler fast. Offload heavy processing to a background job queue (e.g., BullMQ, SQS, or a database row) and return 200 immediately. XRNotify expects a response within 15 seconds, and spending too long processing in the request cycle risks a timeout and unnecessary retries.

Step 4: Verify the HMAC Signature

Every webhook delivery from XRNotify includes an X-XRNotify-Signature header containing an HMAC-SHA256 digest of the raw request body, signed with your subscription's signing secret. Verifying this signature is critical for security; it proves the payload was sent by XRNotify and has not been altered by a man-in-the-middle or replayed by an attacker.

The following Node.js function performs constant-time signature verification using the built-in crypto module:

const crypto = require('crypto');

/**
 * Verify that an incoming XRNotify webhook payload is authentic.
 *
 * @param {Buffer} rawBody       - The raw request body bytes
 * @param {string} signatureHeader - Value of X-XRNotify-Signature header
 * @param {string} secret         - Your webhook signing secret (whsec_...)
 * @returns {boolean} true if the signature is valid
 */
function verifyXRNotifySignature(rawBody, signatureHeader, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  const expectedBuffer = Buffer.from(expected, 'utf-8');
  const receivedBuffer = Buffer.from(signatureHeader, 'utf-8');

  if (expectedBuffer.length !== receivedBuffer.length) {
    return false;
  }

  return crypto.timingSafeEqual(expectedBuffer, receivedBuffer);
}

Integrate this check into your Express handler from Step 3. If the signature does not match, reject the request with a 401 Unauthorized response and log the attempt for monitoring:

app.post(
  '/webhooks/xrpl',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const rawBody = req.body;
    const signature = req.headers['x-xrnotify-signature'];
    const secret = process.env.XRNOTIFY_SIGNING_SECRET;

    if (!signature || !verifyXRNotifySignature(rawBody, signature, secret)) {
      console.warn('Invalid XRNotify signature — rejecting payload');
      return res.status(401).json({ error: 'Invalid signature' });
    }

    const event = JSON.parse(rawBody.toString());
    console.log('Verified XRNotify event:', event.event_type, event.id);

    // Hand off to your processing pipeline
    processEventAsync(event);

    res.status(200).json({ received: true });
  }
);

Using crypto.timingSafeEqual is important because a naive string comparison (===) is vulnerable to timing attacks, where an attacker can deduce your secret byte by byte based on how long the comparison takes. XRNotify strongly recommends constant-time comparison for all signature checks.

Step 5: Parse the Normalized Event Payload

One of XRNotify's most valuable features is its normalized event format. Raw XRPL transaction data from rippled is deeply nested and varies significantly between transaction types. XRNotify flattens this into a consistent, predictable JSON structure so you do not have to write brittle parsing code for every transaction variant.

Here is an example of a normalized payment event delivered by XRNotify:

{
  "id": "evt_9xKm3pQr7vZw",
  "event_type": "payment",
  "created_at": "2026-04-04T14:32:10.483Z",
  "ledger_index": 92481537,
  "tx_hash": "A1B2C3D4E5F6...",
  "account": "rN7n3473SaZBCG4dFL83w7p1W9cgZw6im3",
  "data": {
    "source": "rN7n3473SaZBCG4dFL83w7p1W9cgZw6im3",
    "destination": "rLdinLq5CJood9wdjY9ZCdgycK8KGEvkUj",
    "amount": {
      "currency": "XRP",
      "value": "125.500000"
    },
    "destination_tag": 12345,
    "fee": "0.000012",
    "result": "tesSUCCESS"
  },
  "webhook_id": "wh_3kTm8vQpZr1x"
}

Every XRNotify event follows this top-level schema: a unique id, the event_type string, the originating account, the ledger_index and tx_hash for on-chain reference, and a data object whose shape varies by event type. For payments the data includes source, destination, amount, and destination_tag. For trust-line changes it includes currency, issuer, and limit. XRNotify documents every event type in the API reference.

In your processing code, switch on the event_type field to handle each category appropriately:

async function processEventAsync(event) {
  switch (event.event_type) {
    case 'payment':
      await handlePayment(event.data);
      break;
    case 'trustline_change':
      await handleTrustlineChange(event.data);
      break;
    case 'escrow_create':
    case 'escrow_finish':
    case 'escrow_cancel':
      await handleEscrow(event.event_type, event.data);
      break;
    case 'offer_create':
    case 'offer_cancel':
      await handleDexOffer(event.event_type, event.data);
      break;
    default:
      console.log('Unhandled XRNotify event type:', event.event_type);
  }
}

Because XRNotify normalizes every payload, adding support for a new event type is as simple as adding a new case branch. There is no need to dig through raw rippled metadata or handle edge cases like partial payments or amended transactions; XRNotify does that for you.

Step 6: Handle Delivery Failures and Retries

Network blips happen. Your server might be deploying, a load balancer might timeout, or a downstream database might be momentarily unavailable. XRNotify is built for reliability and automatically retries failed deliveries using an exponential backoff schedule.

A delivery is considered successful when your endpoint returns any HTTP status code in the 200-299 range. Anything else, including 3xx redirects, 4xx client errors, 5xx server errors, connection timeouts, and DNS failures, triggers the retry sequence. XRNotify retries up to 8 times with the following approximate schedule:

AttemptDelay after failureCumulative wait
1st retry10 seconds10 seconds
2nd retry30 seconds40 seconds
3rd retry2 minutes~2.5 minutes
4th retry10 minutes~12.5 minutes
5th retry30 minutes~42.5 minutes
6th retry1 hour~1 hour 42 min
7th retry3 hours~4 hours 42 min
8th retry6 hours~10 hours 42 min

After all retry attempts are exhausted, XRNotify marks the delivery as failed and records it in your delivery log. You can manually replay any failed delivery from the XRNotify dashboard, or use the API to trigger a replay programmatically.

To minimize unnecessary retries, make sure your endpoint is idempotent. Use the event.id field as a deduplication key. Before processing an event, check whether you have already seen that ID. This way, if XRNotify retries a delivery that your server actually processed but failed to acknowledge, you will not double-count the transaction:

async function handlePayment(event) {
  // Idempotency check using the XRNotify event ID
  const existing = await db.events.findOne({ xrnotify_id: event.id });
  if (existing) {
    console.log('Duplicate XRNotify event, skipping:', event.id);
    return;
  }

  await db.events.insertOne({
    xrnotify_id: event.id,
    event_type: event.event_type,
    tx_hash: event.tx_hash,
    data: event.data,
    processed_at: new Date(),
  });

  // Continue with business logic...
}

Step 7: Use the Dashboard to Monitor Delivery Health

XRNotify provides a comprehensive dashboard that gives you full visibility into your webhook pipeline. After your integration is live, check the dashboard regularly to ensure everything is running smoothly.

The Delivery Logs section shows every delivery attempt for each subscription. You can filter by status (success, failed, pending retry), event type, and time range. Each log entry includes the HTTP status code your endpoint returned, the response time in milliseconds, the full request payload, and the response body. This is invaluable for debugging integration issues.

Key metrics available on the XRNotify dashboard include:

  • Delivery success rate — the percentage of deliveries that received a 2xx response on the first attempt. A healthy integration should maintain a success rate above 99%. If you see this number dropping, check your server logs for errors or timeouts.
  • Average latency — the time between when XRNotify sends the POST request and when your server responds. Keep this under 1 second for best results. If latency creeps up, consider offloading processing to a background queue.
  • Retry rate — the percentage of deliveries that required at least one retry. A rising retry rate is an early warning sign that your endpoint is struggling under load or experiencing intermittent failures.
  • Event volume — a time-series chart showing the number of events delivered per hour. Use this to understand traffic patterns and plan capacity accordingly.

XRNotify also supports email and Slack alerts for delivery anomalies. You can configure alerts to fire when your success rate drops below a threshold or when a subscription accumulates too many consecutive failures. Navigate to Settings → Alerts in the XRNotify dashboard to set these up.

Step 8: Scale with Multiple Webhooks

As your application grows, you will likely need to monitor more wallets and react to more event types. XRNotify is designed to scale with you. Here are several patterns for structuring your webhook subscriptions at scale.

Separate Webhooks by Event Type

Instead of funneling all event types into a single endpoint, create dedicated subscriptions for each category. For example, route payment events to a payments microservice and trust-line events to a compliance service:

# Payment events -> payments service
curl -X POST https://api.xrnotify.io/v1/webhooks \
  -H "Authorization: Bearer xrn_live_k1_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://payments.internal.example.com/webhooks/xrpl",
    "event_types": ["payment"],
    "account_filters": ["rN7n3473SaZBCG4dFL83w7p1W9cgZw6im3"]
  }'

# Trust-line events -> compliance service
curl -X POST https://api.xrnotify.io/v1/webhooks \
  -H "Authorization: Bearer xrn_live_k1_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://compliance.internal.example.com/webhooks/xrpl",
    "event_types": ["trustline_change"],
    "account_filters": ["rN7n3473SaZBCG4dFL83w7p1W9cgZw6im3"]
  }'

This pattern keeps each service focused and prevents a surge in one event type from creating backpressure on unrelated handlers. Each XRNotify subscription has its own retry queue and delivery metrics, so a struggling endpoint only affects its own events.

Partition by Account Set

If you manage hundreds of XRPL wallets, split them across multiple XRNotify subscriptions grouped by business function or risk level. For instance, keep hot-wallet monitoring on a high-priority subscription with a fast, dedicated endpoint, while cold-storage wallets go to a lower-priority subscription that writes to a batch processing queue.

Use the XRNotify API to Manage Subscriptions Programmatically

For dynamic wallet monitoring (for example, when a new user deposits to a unique XRPL address), use the XRNotify API to add account filters to an existing subscription or create new subscriptions on the fly. The PATCH endpoint lets you update a subscription's account filters without recreating it:

curl -X PATCH https://api.xrnotify.io/v1/webhooks/wh_3kTm8vQpZr1x \
  -H "Authorization: Bearer xrn_live_k1_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "account_filters": [
      "rN7n3473SaZBCG4dFL83w7p1W9cgZw6im3",
      "rLdinLq5CJood9wdjY9ZCdgycK8KGEvkUj",
      "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe"
    ]
  }'

This is particularly useful for exchanges and custodial platforms where new deposit addresses are generated frequently. Automate account-filter updates in your onboarding flow so that every new wallet is covered by XRNotify within seconds of creation.

Plan Limits and Upgrades

The XRNotify free tier supports up to 10 webhook subscriptions and 10,000 deliveries per month. If your volume exceeds this, upgrade to the Pro or Enterprise tier for higher limits, priority support, and dedicated infrastructure. Visit the XRNotify pricing page for current plan details.

Summary

You now have a complete, production-ready pipeline for monitoring XRPL wallets in real time with XRNotify. To recap the eight steps:

  1. Create an XRNotify account and generate your API key.
  2. Create a webhook subscription with event types and account filters via the XRNotify API.
  3. Build an endpoint that accepts POST requests and responds with a 2xx status code quickly.
  4. Verify every delivery using the HMAC-SHA256 signature in the X-XRNotify-Signature header.
  5. Parse XRNotify's normalized event payload and route by event type.
  6. Handle retries gracefully with idempotent processing keyed on the event ID.
  7. Monitor delivery health, latency, and success rates in the XRNotify dashboard.
  8. Scale by splitting webhooks across event types, account sets, and microservices.

XRNotify handles the complex infrastructure of subscribing to XRPL nodes, normalizing transaction data, and ensuring reliable delivery so that you can focus on building your application logic. Whether you are tracking a single wallet or thousands, XRNotify gives you the real-time visibility you need to build responsive, reliable XRPL applications.

Start monitoring XRPL events

Create your free XRNotify account and receive real-time webhook notifications in minutes.

Get Started Free

Related Articles