# Webhooks overview

## What are webhooks?

Wallet-as-a-Service (Palisade) sends automated messages called webhooks to your back-end systems whenever specific events occur on the platform. The system sends the message, or payload, to a unique URL on your server that you designate to receive webhook events.

## Why are webhooks beneficial?

The automated nature of webhooks allows organizations to receive guaranteed, real-time updates about any interactions with their wallets (including creation of new wallets, deposits and withdrawals). This push-based approach eliminates the need for API/UI polling and ensures customers are immediately notified of important updates.

## Webhook payload format

Wallet-as-a-Service (Palisade) delivers webhook messages as POST requests containing JSON objects with a base64-encoded payload. For example:


```json
{
    "domain": "WALLET",
    "timestamp": "2025-05-08T14:15:37Z",
    "payload": "eyJpZCI6IjAxOTYxNTNiLTY3YmMtN2NkMy1iNTExLTM1NDM3M2QyODEzMCIsICJ2YXVsdElkIjoiMDE5NjE1MmQtNTI5NC03MzlkLTk5NDUtMmE0OTlmOWUzOTg5IiwgIm9yZ2FuaXphdGlvbklkIjoiMjFjODEzMTktNWI4My00NWY5LWI2NDgtNDIwNTUwODRhZjE1IiwgInF1b3J1bUlkIjoiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAwIiwgImNyZWF0ZWRCeSI6ImE1NWRmOWUwLWUxNGEtNDQxMC1hOTgzLTEyYWZhZTQ2NjYyZiIsICJjcmVhdGVkQXQiOiIyMDI1LTA0LTA4VDExOjQ4OjU2Ljg5OTA4NloiLCAidXBkYXRlZEF0IjoiMjAyNS0wNC0wOFQxMTo0ODo1Ni45MDA0OTBaIiwgIm5hbWUiOiJld3FmZXciLCAiZGVzY3JpcHRpb24iOiIiLCAiYWRkcmVzcyI6IjB4NDY0NDMyMGYzMUQ2OTU4RWQ4NjAzRWI2NjYwNDQ5NTBBYTY5NzAyZSIsICJwdWJsaWNLZXkiOiIwNDgxY2IwMmU4MjFlNTVhMmM2MTk2ZmZkZTM4MGJhM2I0OTgxN2JkYmYwMmYzMTVkYzI4YjU2ZjcwNzkyZDU2ZTZhZGFjOTE3ZTdhZGFmODdmMTBjMDg4NDA4YThjNmEwZTZkMTgwNzkzYzA1MGMzZDBjODc3MWNiOWRkZjgyNThkIiwgImtleXN0b3JlIjoiSFNNIiwgImJsb2NrY2hhaW4iOiJBUkJJVFJVTSIsICJzZXR0aW5ncyI6e30sICJzdGF0dXMiOiJQUk9WSVNJT05FRCJ9",
}
```

When decoded, the payload contains detailed information about the blockchain event. For example:


```json
{
  "id": "0196153b-67bc-7cd3-b511-354373d28130",
  "vaultId": "0196152d-5294-739d-9945-2a499f9e3989",
  "organizationId": "21c81319-5b83-45f9-b648-42055084af15",
  "createdBy": "a55df9e0-e14a-4410-a983-12afae46662f",
  "createdAt": "2025-04-08T11:48:56.899086Z",
  "updatedAt": "2025-04-08T11:48:56.900490Z",
  "name": "My Wallet",
  "address": "0x4644320f31D6958Ed8603Eb666044950Aa69702e",
  "publicKey": "0481cb02e821e55a2c6196ffde380ba3b49817bdbf02f315dc28b56f70792d56e6adac917e7adaf87f10c088408a8c6a0e6d180793c050c3d0c8771cb9ddf8258d",
  "keystore": "HSM",
  "blockchain": "ARBITRUM",
  "settings": {},
  "status": "PROVISIONED"
}
```

The webhook contains a `signature` header you can use to verify that the payload is authentic. For example: `MEQCIGjBwNKzzfqK9/Rb3Q2OQCyCuUiOOQz7vZwQ9iqInz76AiB/bvRn5iNUAkeVT80/pwhQ2LUajE6Mb2JtGt2mRmJMpg==`

## Retry Behaviour

Your server must return an HTTP-200 (OK) response to confirm successful receipt of a webhook notification.

If your server does not respond, Wallet-as-a-Service (Palisade) automatically retries the request several times based on the following schedule (in seconds): 0, 60, 120, 180, 320, 480, 840, 1500, 2820, 5400.

The system does not retry for client-side errors (HTTP 4xx) except for the following cases:

429 - Too Many Requests: Indicates rate limits have been exceeded.
408 - Request Timeout: Indicates the server took too long to respond.

After 10 failed attempts, the system marks the notification as failed and stops retrying.

## Event Ordering

Wallet-as-a-Service (Palisade) strives to send notifications in order (per resource), but we cannot guarantee delivery sequence. Design your endpoint to process events independently without relying on their delivery order.

## Subscription Domains

When you create a webhook, you subscribe to one or more domains. Each domain sends notifications when the corresponding resource changes state.

| Domain | Description | Payload |
|  --- | --- | --- |
| **TRANSACTION** | Transaction status changes (transfers, deposits, withdrawals) | [Transaction object](https://docs.ripple.com/products/wallet/api-docs/palisade-api/palisade-api/transactions/transactionsservice_gettransaction#transactions/transactionsservice_gettransaction/response&c=200/body) |
| **WALLET** | Wallet status changes (creation, provisioning) | [Wallet object](https://docs.ripple.com/products/wallet/api-docs/palisade-api/palisade-api/wallets/vaultservice_getwallet#wallets/vaultservice_getwallet/response&c=200/body) |
| **APPROVAL** | Approval request updates | [Approval object](https://docs.ripple.com/products/wallet/api-docs/palisade-api/palisade-api/approvals/approvalservice_getapprovalsummary#approvals/approvalservice_getapprovalsummary/t=response&c=200&path=approval) |


### Transaction Domain

You receive a webhook notification whenever a transaction transitions to a new status. The payload contains the full transaction object with its current state.

| Status | Description | Terminal? |
|  --- | --- | --- |
| `REQUESTED` | Transaction created | No |
| `POLICY_CHECK_PENDING` | Validating against policies | No |
| `POLICY_CHECK_PASSED` | Policy checks passed | No |
| `APPROVAL_CHECK_PENDING` | Waiting for approvals | No |
| `APPROVAL_CHECK_PASSED` | Approvals complete | No |
| `COMPILATION_PENDING` | Preparing for blockchain | No |
| `COMPILED` | Ready for signing | No |
| `SIGNATURE_PENDING` | Waiting for signature | No |
| `SIGNED` | Signed successfully | No |
| `PUBLISH_PENDING` | Submitting to network | No |
| `PUBLISHED` | Submitted to blockchain | No |
| `CONFIRMATION_PENDING` | Awaiting block confirmation | No |
| `CONFIRMED` | Confirmed on blockchain | ✅ Yes |
| `REJECTED` | Rejected by policy or approval | ✅ Yes |
| `FAILED` | Technical failure | ✅ Yes |


For more details on transaction status flow, see [Transactions overview](/products/wallet/user-interface/transactions/overview).

#### Transaction Payload Example


```json
{
  "domain": "TRANSACTION",
  "timestamp": "2025-05-08T14:30:00Z",
  "payload": "base64-encoded-transaction-object"
}
```

When decoded, the payload contains the transaction object. Key fields include:


```json
{
  "id": "019c94f1-cc8c-79b4-9c0f-bc7edaabc267",
  "vaultId": "0196152d-5294-739d-9945-2a499f9e3989",
  "walletId": "0196153b-67bc-7cd3-b511-354373d28130",
  "organizationId": "21c81319-5b83-45f9-b648-42055084af15",
  "status": "CONFIRMED",
  "blockchain": "ETHEREUM",
  "hash": "0x8a4c5e9f2b1d7c3a6e8f0b2d4c6a8e0f2b4d6c8a0e2f4b6d8c0a2e4f6b8d0c2e",
  "destination": {
    "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2E8Ba"
  },
  "asset": {
    "symbol": "ETH"
  },
  "qty": "1.500000000000000000",
  "createdAt": "2025-05-08T14:25:00Z",
  "updatedAt": "2025-05-08T14:30:00Z"
}
```

For the complete transaction schema, see the [Transaction API reference](https://docs.ripple.com/products/wallet/api-docs/palisade-api/palisade-api/transactions/transactionsservice_gettransaction#transactions/transactionsservice_gettransaction/response&c=200/body).

Inbound transactions and compliance
For inbound transactions, the payload may include a `freezeInfo` field indicating whether funds are frozen for compliance review. See [Transaction Freeze and Compliance Controls](/products/wallet/changelogs/transaction-freeze-and-compliance-controls) for details.

### Wallet Domain

You receive a webhook notification whenever a wallet transitions to a new status.

| Status | Description | Terminal? |
|  --- | --- | --- |
| `CREATED` | Wallet record created | No |
| `PROVISIONING` | Wallet being set up | No |
| `PROVISIONED` | Wallet ready for use | ✅ Yes |
| `PROVISIONING_FAILED` | Setup failed | ✅ Yes |


For the complete wallet schema, see the [Wallet API reference](https://docs.ripple.com/products/wallet/api-docs/palisade-api/palisade-api/wallets/vaultservice_getwallet#wallets/vaultservice_getwallet/response&c=200/body).

### Approval Domain

You receive a webhook notification whenever an approval request is created or updated. This includes approvals for transactions, policy changes, and user management actions.

#### Approval Set Status

The overall approval request status:

| Status | Description | Terminal? |
|  --- | --- | --- |
| `PENDING` | Waiting for approvers to respond | No |
| `MET` | Required approvals received | ✅ Yes |
| `NOT_MET` | Approval rejected | ✅ Yes |
| `EXPIRED` | Timed out before requirements met | ✅ Yes |
| `FAILED` | Technical failure during processing | ✅ Yes |


#### Individual Approval Status

Each approver's response within an approval set:

| Status | Description |
|  --- | --- |
| `PROCESSING` | Approver is reviewing the request |
| `APPROVED` | Approver approved the request |
| `REJECTED` | Approver rejected the request |


For the complete approval schema, see the [Approval API reference](https://docs.ripple.com/products/wallet/api-docs/palisade-api/palisade-api/approvals/approvalservice_getapprovalsummary#approvals/approvalservice_getapprovalsummary/t=response&c=200&path=approval).

## Best Practices

### Idempotent Processing

Design your webhook handler to be idempotent. Use the resource `id` and `updatedAt` timestamp to detect and safely handle duplicate deliveries.

### Quick Acknowledgment

We recommend returning a `200 OK` response within 5 seconds. Perform any heavy processing asynchronously after acknowledging receipt to avoid timeouts.

### Signature Verification

Always verify the webhook signature before processing to ensure the payload is authentic. See [Manage webhooks](/products/wallet/user-interface/integrations/manage-webhooks) for verification details.