@xrplrequest/sdk
v0.1.1
Published
TypeScript SDK for XRPL Request — wallet-agnostic XRPL transaction signing
Maintainers
Readme
@xrplrequest/sdk
TypeScript SDK for XRPL Request — wallet-agnostic XRPL transaction signing.
Installation
npm install @xrplrequest/sdkQuick start
import { XRPLRequest } from '@xrplrequest/sdk';
const client = new XRPLRequest({ apiKey: 'xrplr_live_...' });
const payload = await client.payloads.create({
type: 'signAndSubmit',
transaction: {
TransactionType: 'Payment',
Destination: 'rXXX...',
Amount: '1000000', // 1 XRP in drops
},
options: { expiresIn: 300 },
});
console.log(payload.signingUrl); // share with user
// https://xrplre.quest/sign/{uuid}
const result = await client.payloads.poll(payload.uuid, { timeout: 300_000 });
if (result.status === 'signed') {
console.log('tx hash:', result.txHash);
}API
new XRPLRequest(config)
| Option | Type | Required | Description |
|---|---|---|---|
| apiKey | string | ✓ | Your project API key (xrplr_live_...) |
| baseUrl | string | | Override the API base URL (default: https://xrplre.quest) |
client.payloads.create(options)
Create a new signing payload. Returns a Payload with a signingUrl to share with the user.
const payload = await client.payloads.create({
type: 'signAndSubmit', // 'connect' | 'sign' | 'signAndSubmit' | 'signMessage'
transaction: { ... }, // required for sign / signAndSubmit
message: 'Hello XRPL', // required for signMessage
options: {
expiresIn: 300, // seconds (default 300, max depends on plan)
returnUrl: 'https://...', // optional redirect after signing
customInstructions: '...', // shown to user on sign page (max 200 chars)
},
});
payload.uuid // unique identifier
payload.signingUrl // https://xrplre.quest/sign/{uuid}
payload.status // 'pending'
payload.expiresAt // ISO timestampclient.payloads.get(uuid)
Fetch the current status of a payload.
const payload = await client.payloads.get(uuid);
// payload.status: 'pending' | 'signed' | 'rejected' | 'expired'
// payload.txHash — populated when signed + submitted
// payload.signerAddress — populated when signed
// payload.walletAdapter — which wallet was usedclient.payloads.poll(uuid, options?)
Poll until the payload leaves pending state or the timeout is reached.
const result = await client.payloads.poll(uuid, {
timeout: 300_000, // max wait in ms (default 300,000)
interval: 2_000, // poll interval in ms (default 2,000; min 3s on Free plan)
onUpdate: (payload) => {
console.log('still waiting:', payload.status);
},
});client.payloads.cancel(uuid)
Cancel a pending payload.
client.payloads.list(options?)
List payloads for your project.
const { payloads } = await client.payloads.list({ limit: 20, offset: 0 });client.webhooks.create(options)
Register a webhook URL. The returned secret is used to verify signatures — save it, it won't be shown again.
const webhook = await client.webhooks.create({
url: 'https://yourapp.com/xrplrequest-webhook',
events: ['payload.signed', 'payload.rejected', 'payload.expired'],
});
console.log(webhook.secret); // save thisclient.webhooks.list()
List webhooks for your project.
client.webhooks.delete(id)
Remove a webhook.
verifyWebhookSignature(secret, rawBody, signature)
Standalone function to verify an incoming webhook request. Also available as WebhooksClient.verify.
import { verifyWebhookSignature } from '@xrplrequest/sdk';
// Express example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-xrpl-request-signature'] as string;
const valid = verifyWebhookSignature(process.env.WEBHOOK_SECRET!, req.body.toString(), sig);
if (!valid) return res.status(401).send('Invalid signature');
const { event, payload } = JSON.parse(req.body.toString());
console.log(event, payload.txHash);
res.sendStatus(200);
});Payload types
| Type | Description |
|---|---|
| connect | Request wallet address / identity verification. No transaction. |
| sign | Sign a transaction without submitting to the ledger. |
| signAndSubmit | Sign and broadcast to the XRP Ledger. Returns txHash. |
| signMessage | Sign an arbitrary UTF-8 message string. |
Webhook events
| Event | When |
|---|---|
| payload.signed | User signed (and optionally submitted) the transaction |
| payload.rejected | User rejected in their wallet |
| payload.expired | TTL elapsed without resolution |
Webhook POST body:
{
"event": "payload.signed",
"payload": {
"uuid": "...",
"type": "signAndSubmit",
"status": "signed",
"signerAddress": "rXXX...",
"txHash": "ABC123...",
"walletAdapter": "crossmark",
"createdAt": "2026-05-08T12:00:00.000Z",
"resolvedAt": "2026-05-08T12:01:23.000Z"
}
}Signature header: X-XRPL-Request-Signature: sha256=<hmac>
Discord bot example
import { Client, GatewayIntentBits } from 'discord.js';
import { XRPLRequest } from '@xrplrequest/sdk';
const xrpl = new XRPLRequest({ apiKey: process.env.XRPL_REQUEST_API_KEY! });
const discord = new Client({ intents: [GatewayIntentBits.Guilds] });
discord.on('interactionCreate', async (interaction) => {
if (!interaction.isChatInputCommand() || interaction.commandName !== 'tip') return;
const amount = interaction.options.getInteger('amount', true);
const destination = interaction.options.getString('destination', true);
const payload = await xrpl.payloads.create({
type: 'signAndSubmit',
transaction: {
TransactionType: 'Payment',
Destination: destination,
Amount: String(amount * 1_000_000), // XRP → drops
},
});
await interaction.reply({
content: `Sign this transaction to send ${amount} XRP:\n${payload.signingUrl}`,
ephemeral: true,
});
const result = await xrpl.payloads.poll(payload.uuid, { timeout: 300_000 });
if (result.status === 'signed') {
await interaction.editReply(`✅ Sent! Tx: \`${result.txHash}\``);
} else if (result.status === 'rejected') {
await interaction.editReply('❌ Transaction rejected.');
} else {
await interaction.editReply('⏱ Request expired.');
}
});License
MIT
