---
title: "Webhooks"
description: "Referencia de la API del sistema de webhooks de Fystack."
url: https://docs.fystack.io/es/webhooks
last_updated: 2026-11-13
---

> ## Documentation index
> Fetch the complete documentation index at: https://docs.fystack.io/llms.txt
> Use it to discover all available pages before exploring further.

# Webhooks

Los webhooks te permiten recibir notificaciones en tiempo real cuando ocurren eventos específicos en tu cuenta de Fystack.

---

## Obtener la clave pública de webhook

**Endpoint:** `GET /workspaces/:workspace_id/webhook-verification-key`

Obtén la clave pública que se usa para verificar las firmas de webhook de tu espacio de trabajo. Cada solicitud de webhook se firma con Ed25519, y debes verificar esta firma antes de procesar los datos del webhook.

### Respuesta

- `success` (boolean): Indica si la solicitud se realizó correctamente.
- `message` (string): Un mensaje que describe el resultado de la operación.
- `code` (integer): Un código numérico que indica el estado del resultado (0 si es exitoso).
- `data` (object): Contiene los detalles de la clave pública.
  - `public_key` (string): La clave pública Ed25519 para verificar las firmas de webhook.

> **Warning.** Guarda esta clave pública, la necesitarás para verificar las firmas de webhook.

**Request** `GET /workspaces/:workspace_id/webhook-verification-key`

```typescript
import { FystackSDK, Environment } from "@fystack/sdk"

const apiCredentials = {
  apiKey: process.env.API_KEY,
  apiSecret: process.env.API_SECRET
}

const sdk = new FystackSDK({
  credentials: apiCredentials,
  environment: Environment.Sandbox,
  logger: true
})

const response = await sdk.getWebhookPublicKey(
  "workspace_id"
)
```

```bash
curl -X GET https://api.fystack.io/api/v1/workspaces/workspace_id/webhook-verification-key \
  -H "ACCESS-API-KEY: your_api_key" \
  -H "ACCESS-TIMESTAMP: 1667836889" \
  -H "ACCESS-SIGN: YourBase64EncodedSignature=="
```

```json
{
  "success": true,
  "message": "success",
  "code": 0,
  "data": {
    "public_key": "503d18d8375e60667a4cdb879e72c249b3dc054e7d6443c5ccd93aef7f6547fb"
  }
}
```

---

## Eventos de webhook

Fystack envía notificaciones de webhook para varios eventos. Cada tipo de evento representa un cambio de estado específico en tu cuenta.

### Eventos de depósito

- `deposit.pending`: Se detectó un nuevo depósito, pero aún no se ha confirmado en la blockchain.
- `deposit.confirmed`: Un depósito se confirmó en la blockchain.

> **Warning.** Los payloads de los eventos de depósito incluyen un campo `idempotency_key`. Úsalo para deduplicar de forma segura las entregas de webhook, consulta [Clave de idempotencia](#idempotency-key) más abajo.

### Eventos de retiro

- `withdrawal.pending`: Una solicitud de retiro está pendiente de aprobación.
- `withdrawal.executed`: La transacción de retiro se envió a la blockchain.
- `withdrawal.confirmed`: La transacción de retiro se confirmó en la blockchain.
- `withdrawal.failed`: El retiro falló. El payload es el objeto de retiro con `status: "FAILED"`. El objeto anidado `transaction` es opcional y puede ser `null`.

> **Info.** Cuando un retiro falla, los fondos retenidos se liberan de vuelta al saldo de la billetera. Puedes crear un nuevo retiro de forma segura.

---

## Verificación de webhooks

Cada solicitud de webhook incluye una firma en el encabezado `x-webhook-signature`. Debes verificar esta firma para asegurarte de que el webhook proviene de Fystack.

### Encabezados

- `x-webhook-signature` (string): Firma Ed25519 del payload JSON canonicalizado.
- `x-webhook-event` (string): El tipo de evento que activó este webhook.

### Payload del webhook

- `webhook_id` (string): El UUID de la configuración del webhook.
- `resource_id` (string): El UUID del recurso que activó el webhook.
- `url` (string): La URL a la que se envió el webhook.
- `event` (string): El tipo de evento (por ejemplo, `deposit.confirmed`).
- `payload` (object): Los datos específicos del evento.

### Clave de idempotencia

> **Warning.** Los webhooks de depósito (`deposit.pending` y `deposit.confirmed`) pueden entregarse más de una vez para el mismo depósito, por ejemplo tras un reintento después de un timeout o una interrupción temporal de tu endpoint. Usa siempre `idempotency_key` para deduplicar el procesamiento, en lugar de solo `resource_id` o `id`.

Para los eventos de depósito, el campo `payload.idempotency_key` identifica de forma única un evento de depósito subyacente. Como las condiciones de red o la caída de tu endpoint pueden hacer que Fystack vuelva a entregar el mismo webhook, debes tratar dos entregas con la misma `idempotency_key` como el mismo evento y procesarlas solo una vez.

- `idempotency_key` (string): Una clave única que identifica este evento de depósito. Guarda las claves que ya procesaste (por ejemplo, en una base de datos con una restricción de unicidad) y omite el procesamiento si la `idempotency_key` de un webhook entrante ya se registró.

### Verificación de firmas

Para verificar las firmas de webhook, necesitas:

1. Canonicalizar el payload JSON (garantizar un orden consistente de las claves del objeto)
2. Verificar la firma Ed25519 con tu clave pública

> **Warning.** Canonicaliza siempre el payload del evento antes de verificarlo. Distintos serializadores JSON pueden producir representaciones de texto diferentes para la misma estructura de datos.

```typescript
import * as ed from '@noble/ed25519'

// Example public key from Fystack
const FYSTACK_PUBLIC_KEY = '503d18d8375e60667a4cdb879e72c249b3dc054e7d6443c5ccd93aef7f6547fb'

// Canonicalize JSON by sorting keys recursively
function canonicalizeJSON(inputObject: Record<string, any>): string {
  if (typeof inputObject !== 'object' || inputObject === null) {
    throw new Error('Input must be a non-null object.')
  }

  const sortKeys = (value: any): any => {
    if (Array.isArray(value)) {
      return value.map(sortKeys)
    }
    if (value && typeof value === 'object' && value.constructor === Object) {
      return Object.keys(value)
        .sort()
        .reduce((sortedObj: Record<string, any>, key: string) => {
          sortedObj[key] = sortKeys(value[key])
          return sortedObj
        }, {})
    }
    return value
  }

  return JSON.stringify(sortKeys(inputObject))
}

// Verify Ed25519 signature
async function verifyWebhook(payload: object, signatureHex: string): Promise<boolean> {
  const canonical = canonicalizeJSON(payload)
  const msg = new TextEncoder().encode(canonical)
  return ed.verify(signatureHex, msg, FYSTACK_PUBLIC_KEY)
}

// Example usage
app.post('/webhook', async (req, res) => {
  const signature = req.headers['x-webhook-signature']
  const event = req.headers['x-webhook-event']
  const payload = req.body

  // Verify signature using your stored public key
  const isValid = await verifyWebhook(
    payload,
    signature
  )

  if (!isValid) {
    return res.status(400).json({ error: 'Invalid signature' })
  }

  // Handle different event types
  switch (event) {
    case 'deposit.confirmed':
      // Handle confirmed deposit
      break
    case 'withdrawal.confirmed':
      // Handle confirmed withdrawal
      break
    // ... handle other events
  }

  res.json({ received: true })
})
```

```go
package main

import (
  "crypto/ed25519"
  "encoding/hex"
  "encoding/json"
  "fmt"
)

// Example public key from Fystack
const FystackPublicKey = "503d18d8375e60667a4cdb879e72c249b3dc054e7d6443c5ccd93aef7f6547fb"

// CanonicalizeJSON ensures consistent JSON representation
func CanonicalizeJSON(data []byte) ([]byte, error) {
  var temp map[string]interface{}
  err := json.Unmarshal(data, &temp)
  if err != nil {
    return nil, fmt.Errorf("invalid JSON: %w", err)
  }

  canonicalJSON, err := json.Marshal(temp)
  if err != nil {
    return nil, fmt.Errorf("failed to marshal JSON: %w", err)
  }
  return canonicalJSON, nil
}

// VerifyWebhook verifies the webhook signature
func VerifyWebhook(payload []byte, signatureHex string) (bool, error) {
  // Decode hex strings
  publicKey, err := hex.DecodeString(FystackPublicKey)
  if err != nil {
    return false, fmt.Errorf("invalid public key: %w", err)
  }

  signature, err := hex.DecodeString(signatureHex)
  if err != nil {
    return false, fmt.Errorf("invalid signature: %w", err)
  }

  // Canonicalize payload
  canonical, err := CanonicalizeJSON(payload)
  if err != nil {
    return false, fmt.Errorf("failed to canonicalize JSON: %w", err)
  }

  // Verify signature
  return ed25519.Verify(publicKey, canonical, signature), nil
}

// Example usage in HTTP handler
func handleWebhook(w http.ResponseWriter, r *http.Request) {
  signature := r.Header.Get("x-webhook-signature")
  event := r.Header.Get("x-webhook-event")

  body, err := io.ReadAll(r.Body)
  if err != nil {
    http.Error(w, "Failed to read body", http.StatusBadRequest)
    return
  }

  isValid, err := VerifyWebhook(
    body,
    signature,
  )
  if err != nil || !isValid {
    http.Error(w, "Invalid signature", http.StatusBadRequest)
    return
  }

  // Handle webhook...
}
```

```php
<?php

// Example public key from Fystack
const FYSTACK_PUBLIC_KEY = '503d18d8375e60667a4cdb879e72c249b3dc054e7d6443c5ccd93aef7f6547fb';

/**
 * Canonicalize JSON by decoding and re-encoding with sorted keys
 */
function canonicalizeJSON($data) {
    $decoded = json_decode($data, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        throw new Exception('Invalid JSON: ' . json_last_error_msg());
    }

    // Sort all arrays and objects recursively
    array_walk_recursive($decoded, function(&$item, $key) {
        if (is_array($item)) {
            ksort($item);
        }
    });
    ksort($decoded);

    return json_encode($decoded);
}

/**
 * Verify webhook signature using Ed25519
 */
function verifyWebhook($payload, $signatureHex) {
    try {
        // Convert hex to binary
        $publicKey = hex2bin(FYSTACK_PUBLIC_KEY);
        $signature = hex2bin($signatureHex);

        // Canonicalize payload
        $canonical = canonicalizeJSON($payload);

        // Verify using sodium
        return sodium_crypto_sign_verify_detached(
            $signature,
            $canonical,
            $publicKey
        );
    } catch (Exception $e) {
        error_log('Webhook verification failed: ' . $e->getMessage());
        return false;
    }
}

// Example usage in a webhook handler
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$event = $_SERVER['HTTP_X_WEBHOOK_EVENT'] ?? '';
$payload = file_get_contents('php://input');

if (!verifyWebhook(
    $payload,
    $signature
)) {
    http_response_code(400);
    echo json_encode(['error' => 'Invalid signature']);
    exit;
}

// Handle different event types
switch ($event) {
    case 'deposit.confirmed':
        // Handle confirmed deposit
        break;
    case 'withdrawal.confirmed':
        // Handle confirmed withdrawal
        break;
}

echo json_encode(['received' => true]);
```

```python
import json
import ed25519
from typing import Dict, Any
from binascii import unhexlify

# Example public key from Fystack
FYSTACK_PUBLIC_KEY = '503d18d8375e60667a4cdb879e72c249b3dc054e7d6443c5ccd93aef7f6547fb'

def canonicalize_json(data: Dict[Any, Any]) -> str:
    """Ensure consistent JSON representation."""
    return json.dumps(data, sort_keys=True, separators=(',', ':'))

def verify_webhook(payload: Dict[Any, Any], signature_hex: str) -> bool:
    """Verify webhook signature."""
    try:
        # Convert hex strings to bytes
        public_key = unhexlify(FYSTACK_PUBLIC_KEY)
        signature = unhexlify(signature_hex)

        # Canonicalize payload
        canonical = canonicalize_json(payload).encode('utf-8')

        # Create verifier
        verifier = ed25519.VerifyingKey(public_key)
        return verifier.verify(signature, canonical)
    except Exception as e:
        print(f"Verification failed: {e}")
        return False

# Example Flask route
@app.route('/webhook', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('x-webhook-signature')
    event = request.headers.get('x-webhook-event')
    payload = request.json

    if not verify_webhook(
        payload,
        signature
    ):
        return jsonify({'error': 'Invalid signature'}), 400

    # Handle webhook...
    return jsonify({'received': True})
```

```java
import com.fasterxml.jackson.databind.ObjectMapper;
import org.bouncycastle.crypto.signers.Ed25519Signer;
import org.bouncycastle.util.encoders.Hex;

public class WebhookVerifier {
    private static final ObjectMapper MAPPER = new ObjectMapper();

    // Example public key from Fystack
    private static final String FYSTACK_PUBLIC_KEY = "503d18d8375e60667a4cdb879e72c249b3dc054e7d6443c5ccd93aef7f6547fb";

    public static String canonicalizeJSON(Object data) throws JsonProcessingException {
        // Use Jackson's sorted map feature for consistent ordering
        return MAPPER.writer().writeValueAsString(data);
    }

    public static boolean verifyWebhook(Object payload, String signatureHex) {
        try {
            byte[] publicKey = Hex.decode(FYSTACK_PUBLIC_KEY);
            byte[] signature = Hex.decode(signatureHex);

            // Canonicalize payload
            String canonical = canonicalizeJSON(payload);
            byte[] message = canonical.getBytes(StandardCharsets.UTF_8);

            Ed25519Signer verifier = new Ed25519Signer();
            verifier.init(false, new Ed25519PublicKeyParameters(publicKey, 0));
            verifier.update(message, 0, message.length);

            return verifier.verifySignature(signature);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    // Example Spring controller
    @PostMapping("/webhook")
    public ResponseEntity<?> handleWebhook(
        @RequestHeader("x-webhook-signature") String signature,
        @RequestHeader("x-webhook-event") String event,
        @RequestBody Map<String, Object> payload
    ) {
        if (!verifyWebhook(
            payload,
            signature
        )) {
            return ResponseEntity.badRequest().body("Invalid signature");
        }

        // Handle webhook...
        return ResponseEntity.ok().build();
    }
}
```

> **Info.** El paso de canonicalización es fundamental: garantiza que los mismos datos JSON siempre produzcan la misma representación de texto, sin importar cómo se formatearon u ordenaron originalmente.

### Ejemplos de payloads de eventos

**Event Examples**

```json
{
  "webhook_id": "73893532-8e3d-4bc7-b3f9-148c9b82b811",
  "resource_id": "62ef8383-e897-449f-b9d8-78fffaa26a61",
  "url": "https://webhook.site/56e92433-823d-4b28-9cc9-9298076af671",
  "payload": {
    "amount": "1000",
    "asset": {
      "address": "0xDD75BEb521f4513D65fFbbE91ab7131f3cD92080",
      "address_type": "evm",
      "created_at": "2024-04-12T22:58:36.133934+07:00",
      "decimals": 6,
      "id": "f1be472d-1338-43ff-b547-06faba7ecce7",
      "is_native": false,
      "is_whitelisted": true,
      "logo_url": "https://assets-cdn.trustwallet.com/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
      "name": "USDC Sepolia test",
      "network_id": "23d13c03-1f3c-4e2a-9aef-1771a244b7f6",
      "symbol": "USDC",
      "updated_at": "2024-04-12T22:58:36.133934+07:00"
    },
    "asset_hold": null,
    "asset_hold_id": null,
    "asset_id": "f1be472d-1338-43ff-b547-06faba7ecce7",
    "block_number": 8960514,
    "created_at": "2025-08-11T17:31:24+07:00",
    "description": "",
    "direction": "in",
    "from_address": "0xe6EBF81E9C225BbCEa9b5399E0D0d0f29f30f119",
    "id": "62ef8383-e897-449f-b9d8-78fffaa26a61",
    "idempotency_key": "bfa74b5d5b591794031e0f78a556c80b7b89875378baac4df75b8302cf80b72c",
    "method": "transfer",
    "network": null,
    "network_id": "23d13c03-1f3c-4e2a-9aef-1771a244b7f6",
    "price_native_token": "4260.480000022453",
    "price_token": "0.9997516371944777",
    "status": "pending",
    "to_address": "0xA118fa3d039BA6ad3BB0bA8ac8F243F727B2F99F",
    "tx_fee": "0.000051521446618",
    "tx_hash": "0x21c201ff8e02abdcd4395c71639266d953cb18cb159bdcd28b7556c482b4881a",
    "type": "token_transfer",
    "updated_at": "2025-08-11T17:31:26.815540431+07:00",
    "user": null,
    "user_id": "075243c1-962e-4803-83da-d73c78b8b060",
    "wallet": {
      "created_at": "2025-08-11T10:35:54.174577+07:00",
      "deleted_at": null,
      "disabled": false,
      "id": "e0851607-417a-475a-b399-5f3aa262b81d",
      "name": "hello1",
      "threshold": 1,
      "updated_at": "2025-08-11T10:35:54.174577+07:00",
      "user_id": "075243c1-962e-4803-83da-d73c78b8b060",
      "wallet_purpose": "general",
      "wallet_type": "standard",
      "workspace_id": "6bcfe6c7-281b-4773-a188-e78f5cad3336"
    },
    "wallet_id": "e0851607-417a-475a-b399-5f3aa262b81d",
    "workspace": null,
    "workspace_id": "6bcfe6c7-281b-4773-a188-e78f5cad3336"
  },
  "event": "deposit.pending"
}
```

```json
{
  "webhook_id": "73893532-8e3d-4bc7-b3f9-148c9b82b811",
  "resource_id": "c6702df0-b8ef-4a38-882d-dabf0d56127d",
  "url": "https://webhook.site/56e92433-823d-4b28-9cc9-9298076af671",
  "event": "deposit.confirmed",
  "payload": {
    "id": "c6702df0-b8ef-4a38-882d-dabf0d56127d",
    "idempotency_key": "a1c92e7f4d3b8619524fbd0b3e9c15a6ed172b8d31f645a998e2c07b1a5f6d3e",
    "amount": "1000",
    "status": "confirmed",
    "tx_hash": "0x75789cbbe4d55328e6be7b7155b4028ea8afa384ffe13a1146d8bdb3eed6ba22",
    "from_address": "0xe6EBF81E9C225BbCEa9b5399E0D0d0f29f30f119",
    "to_address": "0xA793457d84CFD255Ed68295984300C4f799E759F",
    "asset_id": "f1be472d-1338-43ff-b547-06faba7ecce7",
    "wallet_id": "e0851607-417a-475a-b399-5f3aa262b81d",
    "block_number": 8960538,
    "type": "token_transfer",
    "direction": "in"
  }
}
```

```json
{
  "webhook_id": "73893532-8e3d-4bc7-b3f9-148c9b82b811",
  "resource_id": "147d4808-2e98-4a86-9ead-c227e7e80cf6",
  "url": "https://webhook.site/56e92433-823d-4b28-9cc9-9298076af671",
  "event": "withdrawal.pending",
  "payload": {
    "id": "0de79230-a820-4bd4-b020-883d395faa87",
    "amount": "1000",
    "status": "PENDING_APPROVAL",
    "recipient_address": "0x7c2BbE2e02af09F1cf9aFd4A79EC201DC841675a",
    "asset_id": "f1be472d-1338-43ff-b547-06faba7ecce7",
    "wallet_id": "e0851607-417a-475a-b399-5f3aa262b81d",
    "creator_id": "075243c1-962e-4803-83da-d73c78b8b060",
    "asset_hold_id": "2595ac0b-deb6-4c58-8466-290a248e5d2f",
    "created_at": "2025-08-11T17:37:29.815148+07:00",
    "updated_at": "2025-08-11T17:37:29.815148+07:00"
  }
}
```

```json
{
  "webhook_id": "73893532-8e3d-4bc7-b3f9-148c9b82b811",
  "resource_id": "0de79230-a820-4bd4-b020-883d395faa87",
  "url": "https://webhook.site/56e92433-823d-4b28-9cc9-9298076af671",
  "payload": {
    "amount": "1000",
    "asset": null,
    "asset_hold_id": "2595ac0b-deb6-4c58-8466-290a248e5d2f",
    "asset_id": "f1be472d-1338-43ff-b547-06faba7ecce7",
    "created_at": "2025-08-11T17:37:29.815148+07:00",
    "creator": null,
    "creator_id": "075243c1-962e-4803-83da-d73c78b8b060",
    "error_reason": null,
    "id": "0de79230-a820-4bd4-b020-883d395faa87",
    "recipient_address": "0x7c2BbE2e02af09F1cf9aFd4A79EC201DC841675a",
    "status": "EXECUTED",
    "transaction": {
      "amount": "1000",
      "asset": null,
      "asset_hold": null,
      "asset_hold_id": "2595ac0b-deb6-4c58-8466-290a248e5d2f",
      "asset_id": "f1be472d-1338-43ff-b547-06faba7ecce7",
      "block_number": 8960546,
      "created_at": "2025-08-11T17:37:45.828992+07:00",
      "description": "",
      "direction": "out",
      "from_address": "0xA118fa3d039BA6ad3BB0bA8ac8F243F727B2F99F",
      "id": "147d4808-2e98-4a86-9ead-c227e7e80cf6",
      "method": "transfer",
      "network": null,
      "network_id": "23d13c03-1f3c-4e2a-9aef-1771a244b7f6",
      "price_native_token": "4260.480000022453",
      "price_token": "0.9997516371944777",
      "status": "completed",
      "to_address": "0x7c2BbE2e02af09F1cf9aFd4A79EC201DC841675a",
      "tx_fee": "0.0000001851509817",
      "tx_hash": "0x619dea0c5eb5206a35ea22113d6b4d9e5f0ce226ae3b2ca0089ec9c341b3a210",
      "type": "withdrawal",
      "updated_at": "2025-08-11T17:37:50.765281+07:00",
      "user": null,
      "user_id": "075243c1-962e-4803-83da-d73c78b8b060",
      "wallet": null,
      "wallet_id": "e0851607-417a-475a-b399-5f3aa262b81d",
      "workspace": null,
      "workspace_id": "6bcfe6c7-281b-4773-a188-e78f5cad3336"
    },
    "transaction_id": "147d4808-2e98-4a86-9ead-c227e7e80cf6",
    "updated_at": "2025-08-11T17:37:46.069818+07:00",
    "wallet_id": "e0851607-417a-475a-b399-5f3aa262b81d",
    "withdrawal_approvals": null
  },
  "event": "withdrawal.executed"
}
```

```json
{
  "webhook_id": "73893532-8e3d-4bc7-b3f9-148c9b82b811",
  "resource_id": "147d4808-2e98-4a86-9ead-c227e7e80cf6",
  "url": "https://webhook.site/56e92433-823d-4b28-9cc9-9298076af671",
  "payload": {
    "amount": "1000",
    "asset": null,
    "asset_hold_id": "2595ac0b-deb6-4c58-8466-290a248e5d2f",
    "asset_id": "f1be472d-1338-43ff-b547-06faba7ecce7",
    "created_at": "2025-08-11T17:37:29.815148+07:00",
    "creator": null,
    "creator_id": "075243c1-962e-4803-83da-d73c78b8b060",
    "error_reason": null,
    "id": "0de79230-a820-4bd4-b020-883d395faa87",
    "recipient_address": "0x7c2BbE2e02af09F1cf9aFd4A79EC201DC841675a",
    "status": "SUCCESS",
    "transaction": {
      "amount": "1000",
      "asset": null,
      "asset_hold": null,
      "asset_hold_id": "2595ac0b-deb6-4c58-8466-290a248e5d2f",
      "asset_id": "f1be472d-1338-43ff-b547-06faba7ecce7",
      "block_number": 8960546,
      "created_at": "2025-08-11T17:37:45.828992+07:00",
      "description": "",
      "direction": "out",
      "from_address": "0xA118fa3d039BA6ad3BB0bA8ac8F243F727B2F99F",
      "id": "147d4808-2e98-4a86-9ead-c227e7e80cf6",
      "method": "transfer",
      "network": null,
      "network_id": "23d13c03-1f3c-4e2a-9aef-1771a244b7f6",
      "price_native_token": "4260.480000022453",
      "price_token": "0.9997516371944777",
      "status": "confirmed",
      "to_address": "0x7c2BbE2e02af09F1cf9aFd4A79EC201DC841675a",
      "tx_fee": "0.0000001851509817",
      "tx_hash": "0x619dea0c5eb5206a35ea22113d6b4d9e5f0ce226ae3b2ca0089ec9c341b3a210",
      "type": "withdrawal",
      "updated_at": "2025-08-11T17:38:05.607714+07:00",
      "user": null,
      "user_id": "075243c1-962e-4803-83da-d73c78b8b060",
      "wallet": null,
      "wallet_id": "e0851607-417a-475a-b399-5f3aa262b81d",
      "workspace": null,
      "workspace_id": "6bcfe6c7-281b-4773-a188-e78f5cad3336"
    },
    "transaction_id": "147d4808-2e98-4a86-9ead-c227e7e80cf6",
    "updated_at": "2025-08-11T17:38:06.057452+07:00",
    "wallet_id": "e0851607-417a-475a-b399-5f3aa262b81d",
    "withdrawal_approvals": null
  },
  "event": "withdrawal.confirmed"
}
```

```json
{
  "webhook_id": "73893532-8e3d-4bc7-b3f9-148c9b82b811",
  "resource_id": "3e8a6d21-7f4c-4b19-a0d5-2c9e1b7f8a36",
  "url": "https://webhook.site/56e92433-823d-4b28-9cc9-9298076af671",
  "payload": {
    "id": "3e8a6d21-7f4c-4b19-a0d5-2c9e1b7f8a36",
    "amount": "1000",
    "status": "FAILED",
    "recipient_address": "0x7c2BbE2e02af09F1cf9aFd4A79EC201DC841675a",
    "asset": null,
    "asset_id": "f1be472d-1338-43ff-b547-06faba7ecce7",
    "asset_hold_id": "8f4b2a90-1c6d-4e57-b3a8-0d9e6f1c2b75",
    "wallet_id": "e0851607-417a-475a-b399-5f3aa262b81d",
    "creator": null,
    "creator_id": "075243c1-962e-4803-83da-d73c78b8b060",
    "error_reason": null,
    "fee_priority": "standard",
    "approval_threshold": 0,
    "withdrawal_approvals": null,
    "transaction_id": "d47c1e93-5a2b-4f80-9e6d-1b3a8c7f0e52",
    "transaction": {
      "amount": "1000",
      "asset": null,
      "asset_hold": null,
      "asset_hold_id": "8f4b2a90-1c6d-4e57-b3a8-0d9e6f1c2b75",
      "asset_id": "f1be472d-1338-43ff-b547-06faba7ecce7",
      "block_number": 0,
      "created_at": "2025-08-11T18:10:02.118254+07:00",
      "description": "",
      "direction": "out",
      "from_address": "0xA118fa3d039BA6ad3BB0bA8ac8F243F727B2F99F",
      "id": "d47c1e93-5a2b-4f80-9e6d-1b3a8c7f0e52",
      "method": "transfer",
      "network": null,
      "network_id": "23d13c03-1f3c-4e2a-9aef-1771a244b7f6",
      "failed_reason": "Signing timeout",
      "price_native_token": "0",
      "price_token": "0",
      "status": "failed",
      "to_address": "0x7c2BbE2e02af09F1cf9aFd4A79EC201DC841675a",
      "tx_fee": "0",
      "tx_hash": null,
      "type": "withdrawal",
      "updated_at": "2025-08-11T18:15:02.540917+07:00",
      "user": null,
      "user_id": "075243c1-962e-4803-83da-d73c78b8b060",
      "wallet": null,
      "wallet_id": "e0851607-417a-475a-b399-5f3aa262b81d",
      "workspace": null,
      "workspace_id": "6bcfe6c7-281b-4773-a188-e78f5cad3336"
    },
    "created_at": "2025-08-11T18:09:58.774102+07:00",
    "updated_at": "2025-08-11T18:15:02.611385+07:00"
  },
  "event": "withdrawal.failed"
}
```

> **Info.** Registra tus endpoints de webhook en el portal de Fystack y selecciona qué eventos quieres recibir.
