GuidesWebhook Integration

Webhook Integration

Webhooks let your server receive real-time events instead of polling. Recommended for production.

Events

EventWhen fired
payload.signedUser signed (and optionally submitted) the transaction
payload.rejectedUser explicitly rejected in their wallet
payload.expiredPayload TTL elapsed without resolution

Payload body

Every webhook POST has this shape:

{
  "event": "payload.signed",
  "payload": {
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "type": "signAndSubmit",
    "status": "signed",
    "signerAddress": "rXXX...",
    "txHash": "ABC123...",
    "txBlob": "1200002200000000...",
    "walletAdapter": "crossmark",
    "createdAt": "2026-05-08T12:00:00.000Z",
    "resolvedAt": "2026-05-08T12:01:23.000Z"
  }
}

Headers

HeaderValue
Content-Typeapplication/json
X-XRPL-Request-Signaturesha256=<hmac>
X-XRPL-Request-Eventpayload.signed | payload.rejected | payload.expired
X-XRPL-Request-DeliveryDelivery attempt ID

HMAC signatures are a Pro plan feature. On the Free tier, the signature header is omitted.

Setting up a webhook

Register the webhook

const webhook = await client.webhooks.create({
  url: 'https://yourapp.com/xrplrequest-webhook',
  events: ['payload.signed', 'payload.rejected', 'payload.expired'],
});
 
// ⚠ Save webhook.secret — it's shown once
process.env.WEBHOOK_SECRET = webhook.secret;

Handle incoming requests

import express from 'express';
import { verifyWebhookSignature } from '@xrplrequest/sdk';
 
const app = express();
 
// ⚠ Must use raw body — JSON middleware will break signature verification
app.post('/xrplrequest-webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-xrpl-request-signature'] as string;
  const rawBody = req.body.toString('utf-8');
 
  if (!verifyWebhookSignature(process.env.WEBHOOK_SECRET!, rawBody, sig)) {
    return res.status(401).send('Invalid signature');
  }
 
  const { event, payload } = JSON.parse(rawBody);
 
  switch (event) {
    case 'payload.signed':
      console.log('Signed:', payload.txHash);
      break;
    case 'payload.rejected':
      console.log('Rejected by user');
      break;
    case 'payload.expired':
      console.log('Request expired');
      break;
  }
 
  res.sendStatus(200); // respond quickly — process async if needed
});

Retries

If your endpoint returns anything other than 2xx, XRPL Request will retry with exponential backoff:

AttemptDelay
1immediate
22s
34s
48s
516s

After 5 failed attempts the delivery is marked failed. You can see delivery history in the dashboard.

Best practices

  • Always verify the signature before processing (Pro plan)
  • Respond with 200 immediately and process the event asynchronously to avoid timeouts
  • Use idempotency — a uuid uniquely identifies a payload; your handler may be called more than once if a retry occurs
  • Deduplicate by X-XRPL-Request-Delivery if you need strict once-only processing