Payload status updates

You can get, or wait, for payload status updates using websockets, webhooks and API calls to the xumm API. Your application should always use a server side API call to the HTTP GET /payload endpoint to verify the authenticity of the data received.

For Javascript/Typescript developers the ready to use SDK offers subscribe methods & type definitions. All information below still applies to the events (responses) the SDK emits.
npm version


All payloads get assigned a payload specific websocket URL. You can connect to this socket from the client side of your application to receive live status updates (if the user opened the payload on their device, and the user resolved the payload).

The pushed information contains minimal information; it is meant to trigger your application to fetch more information from your application's backend.

If a payload doesn't exist, the websocket connection will receive a {"message":"..."} update and the socket will be closed. If the payload has expired, the connection will receive an {"expired":true"} message.

If the payload exists and did not expire, the connection will start with a message containing the remaining seconds until expiration: {"expires_in_seconds":51}. This message will be sent every 15 seconds for keepalive reasons. If the connection is still active when the payload expired, an {"expired":true"} message will be sent. The connection will be kept alive though, leaving it up to the client to disconnect.



The expiration should be handled as a OPEN/SCAN BEFORE, not a RESOLVE BEFORE. If the end user opens the payload (from the push notification, deeplink or QR scanning) but it didn't resolve before expiration, they will not receive a notification, and will still be able to resolve as if the payload hadn't expired. When the user resolves after the expiration moment, the webhook and callback will handle the response as they would when the payload wasn't expired.

WebSocket events to expect

Event (when does the websocket emit the message)
Connected{"message":"Welcome <payload-uuid>"}
After connecting and every 15 seconds while the conncetion is alive. A negative value means the transaction expired that many seconds ago.{"expires_in_seconds":54}
When the user received the payload, eg. push notification, deeplink or QR scan{"opened":true}
When the xumm API fetched the payload details{"devapp_fetched":true}
When the payload is resolved{"payload_uuidv4":"..."...} (Sample below)
When the payload is expired{"expired":true}

The WebSocket payload resolve message looks like:

  "payload_uuidv4": "<some-uuid>",
  "reference_call_uuidv4": "<some-uuid>",
    "app": "<some-url>",
    "web": "<some-url>",
  "signed": true,
  "opened_by_deeplink": false,
  "user_token": true,
    "identifier": "<some-identifier>",
    "blob": {},
    "instruction": "<some-instruction>"
  "txid": "<some-tx-hash>"


If you entered a valid Webhook URL in your Application Settings at the xumm Developer Dashboard your application will receive a HTTP POST containing a JSON body with limited details for the given payload. A sample webhook looks like this:

  "meta": {
    "url": "<your-webhook-endpoint>",
    "application_uuidv4": "<some-uuid>",
    "payload_uuidv4": "<some-uuid>",
    "opened_by_deeplink": true
  "custom_meta": {
    "identifier": "some_identifier_1337",
    "blob": {},
    "instruction": "Hey ❤️ ..."
  "payloadResponse": {
    "payload_uuidv4": "<some-uuid>",
    "reference_call_uuidv4": "<some-uuid>",
    "signed": true,
    "user_token": true,
    "return_url": {
      "app": "<your-url>",
      "web": "<your-url>"
    "txid": "<some-tx-hash>"
  "userToken": {
    "user_token": "<some-token>",
    "token_issued": 1635000000,
    "token_expiration": 1637500000

If a user token (userToken.user_token) is issued and you plan on payload delivery using push notifications in the future, your application could/should store the token. See: Pushing sign requests.

Use the payloadResponse.payload_uuidv4 to confirm and get payload outcome data from the xumm platform API using an API call:


Expected result / retry

If your backend does not respond within 15 seconds, or responds with a non 200 HTTP status code, the Xumm platform will retry the webhook four more times (bringing the max. total attempts at five).

The first retry will take place after 10 seconds. The next after 60 seconds. Then two more retries will take place at a 600 second (10 minute) interval.

Webhook verification

Every webhook call contains a signature to verify the authenticity. It sends a HMAC using your application secret as key. Sample verification in NodeJS:

import crypto from 'crypto'

// Xumm App secret (Xumm developer console)
const secret = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'

const timestamp = req.headers?.['x-xumm-request-timestamp'] || ''
const json = req.body

const hmac = crypto.createHmac('sha1', secret.replace('-', ''))
  .update(timestamp + JSON.stringify(json))

console.log(hmac, hmac === req.headers?.['x-xumm-request-signature'])

API call

By calling the HTTP GET /payload endpoint using the unique payload ID, you receive all payload information and the information added due to the user resolving the payload (rejecting or signing). A sample response body (JSON) looks like the one displayed below.


Meta boolean scenarios

Please check the meta boolean scenarios for different resolve statuses and their expected true/false value.

  "meta": {
    "exists": true,
    "uuid": "<some-uuid>",
    "multisign": false,
    "submit": true,
    "destination": "XRP Tip Bot",
    "resolved": true,
    "signed": true,
    "expired": false,
    "pushed": true,
    "app_opened": true,
    "opened_by_deeplink": null,
    "return_url_app": "...",
    "return_url_web": "...",
    "is_xapp": false,
    "pathfinding": false
  "custom_meta": {
    "identifier": "some_identifier_1337",
    "blob": {},
    "instruction": "Hey ❤️ ..."
  "application": {
    "name": "...",
    "description": "...",
    "disabled": 0,
    "uuidv4": "<some-uuid>",
    "icon_url": "<some-uuid>.png",
    "issued_user_token": "<some-token>"
  "payload": {
    "tx_type": "Payment",
    "tx_destination": "...",
    "tx_destination_tag": 1234,
    "request_json": {
      "TransactionType": "Payment",
      "Destination": "...",
      "Amount": "500000",
      "DestinationTag": 1234,
      "LastLedgerSequence": 20,
      "Fee": "12"
    "origintype": "QR",
    "signmethod": "TANGEM",
    "created_at": "2020-01-20T15:41:18Z",
    "expires_at": "2020-01-30T15:41:18Z",
    "expires_in_seconds": 1201
  "response": {
    "hex": "<signed-tx-blob-hex>",
    "txid": "<tx-hash>",
    "resolved_at": "2020-01-20T15:41:31.000Z",
    "dispatched_to": "wss://",
    "dispatched_nodetype": "MAINNET",
    "dispatched_result": "tesSUCCESS",
    "multisign_account": "",
    "account": "<user-account-used-to-sign>"


You can use the response.txid value to fetch the transaction from a node connected to the XRPL. If you rely on the transaction outcome (eg. credit user balance), please always verify the on ledger transaction results. Please familiarize yourself fully on partial payments and be aware of the implications of not setting this correctly!

Verifying the signature

If a (non-multisigned) transaction has been signed, you can verify the signature and signer (response.hex) in NodeJS (Javascript) with the verify-xrpl-signature package.