Phoenix Wallet

Events

Event streaming and webhook integration

Phoenix Wallet provides real-time event streaming and webhook delivery for all ledger operations. Events enable you to build reactive systems that respond to balance changes, expirations, and other wallet activities.

Event Streaming

Subscribe to a real-time stream of events using Server-Sent Events (SSE).

GET /events/stream

Query Parameters

ParameterTypeDefaultDescription
typesstringallComma-separated event types to subscribe to
wallet_idstring-Filter to specific wallet
from_sequencestring-Resume from a specific sequence number

Example Request

curl -N "{{BASE_URL}}/events/stream?types=wallet.credited,wallet.debited" \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Accept: text/event-stream"

Example Stream Output

event: wallet.credited
id: seq_000000000001
data: {"id":"evt_abc123","type":"wallet.credited","wallet_id":"wal_abc123def456","data":{"amount":"100.00","asset":"POINTS","lot_id":"lot_xyz789"},"created_at":"2024-01-15T10:30:00Z"}

event: wallet.debited
id: seq_000000000002
data: {"id":"evt_def456","type":"wallet.debited","wallet_id":"wal_abc123def456","data":{"amount":"25.00","asset":"POINTS","transfer_id":"txf_lmn123"},"created_at":"2024-01-15T10:35:00Z"}

event: heartbeat
id: seq_000000000003
data: {"timestamp":"2024-01-15T10:40:00Z"}

Connection Notes

  • The stream sends a heartbeat every 30 seconds to keep the connection alive
  • If disconnected, use from_sequence to resume without missing events
  • Events are delivered in order within each wallet, but may be interleaved across wallets

Webhooks

Configure webhook endpoints to receive event notifications via HTTP POST.

Create Webhook

POST /webhooks

Request Body

FieldTypeRequiredDescription
urlstringYesHTTPS endpoint to receive events
eventsarrayYesEvent types to subscribe to (or ["*"] for all)
secretstringNoShared secret for signature verification (auto-generated if not provided)
metadataobjectNoCustom key-value pairs

Example Request

curl -X POST {{BASE_URL}}/webhooks \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: {{IDEMPOTENCY_KEY}}" \
  -d '{
    "url": "https://your-app.com/webhooks/phoenix-wallet",
    "events": [
      "wallet.credited",
      "wallet.debited",
      "reservation.created",
      "reservation.committed",
      "lot.expired"
    ],
    "metadata": {
      "environment": "production"
    }
  }'

Example Response

{
  "data": {
    "id": "whk_abc123def456",
    "url": "https://your-app.com/webhooks/phoenix-wallet",
    "events": [
      "wallet.credited",
      "wallet.debited",
      "reservation.created",
      "reservation.committed",
      "lot.expired"
    ],
    "secret": "whsec_aBcDeFgHiJkLmNoPqRsTuVwXyZ123456",
    "status": "active",
    "metadata": {
      "environment": "production"
    },
    "created_at": "2024-01-15T10:00:00Z"
  }
}

Webhook Payload

When an event occurs, Phoenix Wallet sends an HTTP POST to your endpoint.

Headers

HeaderDescription
Content-Typeapplication/json
X-Phoenix-SignatureHMAC-SHA256 signature of the payload
X-Phoenix-TimestampUnix timestamp of the request
X-Phoenix-Event-IDUnique event identifier
X-Phoenix-Event-TypeEvent type (e.g., wallet.credited)

Example Payload

{
  "id": "evt_abc123def456",
  "type": "wallet.credited",
  "api_version": "2024-01-01",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    "wallet_id": "wal_abc123def456",
    "amount": "100.00",
    "asset": "POINTS",
    "credit_id": "crd_xyz789abc123",
    "lot_id": "lot_mno456pqr789",
    "reference": "purchase_order_789",
    "balance_after": {
      "available": "1600.00",
      "reserved": "200.00",
      "total": "1800.00"
    }
  }
}

Verifying Webhook Signatures

Always verify webhook signatures to ensure requests are from Phoenix Wallet.

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, timestamp, secret) {
  const signedPayload = `${timestamp}.${payload}`;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  // Use timing-safe comparison
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// In your webhook handler
app.post('/webhooks/phoenix-wallet', (req, res) => {
  const signature = req.headers['x-phoenix-signature'];
  const timestamp = req.headers['x-phoenix-timestamp'];
  const payload = JSON.stringify(req.body);

  if (!verifyWebhookSignature(payload, signature, timestamp, WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Process the event
  const event = req.body;
  console.log(`Received event: ${event.type}`);

  res.status(200).json({ received: true });
});

Webhook Retry Policy

If your endpoint returns a non-2xx response, Phoenix Wallet retries with exponential backoff:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours
68 hours
724 hours

After 7 failed attempts, the webhook is marked as failed and the event is logged for manual review.


List Webhooks

GET /webhooks

Example Request

curl -X GET {{BASE_URL}}/webhooks \
  -H "Authorization: Bearer {{API_KEY}}"

Example Response

{
  "data": [
    {
      "id": "whk_abc123def456",
      "url": "https://your-app.com/webhooks/phoenix-wallet",
      "events": ["wallet.credited", "wallet.debited"],
      "status": "active",
      "created_at": "2024-01-15T10:00:00Z"
    }
  ]
}

Update Webhook

PATCH /webhooks/{{WEBHOOK_ID}}

Request Body

FieldTypeRequiredDescription
urlstringNoNew endpoint URL
eventsarrayNoReplace subscribed events
statusstringNoSet to active or disabled

Example Request

curl -X PATCH {{BASE_URL}}/webhooks/{{WEBHOOK_ID}} \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: {{IDEMPOTENCY_KEY}}" \
  -d '{
    "events": ["wallet.credited", "wallet.debited", "lot.expired"],
    "status": "active"
  }'

Delete Webhook

DELETE /webhooks/{{WEBHOOK_ID}}

Example Request

curl -X DELETE {{BASE_URL}}/webhooks/{{WEBHOOK_ID}} \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Idempotency-Key: {{IDEMPOTENCY_KEY}}"

Event Types

Wallet Events

Event TypeDescription
wallet.createdA new wallet was created
wallet.updatedWallet metadata or policies were updated
wallet.suspendedWallet was suspended
wallet.reactivatedSuspended wallet was reactivated
wallet.creditedFunds were added to the wallet
wallet.debitedFunds were removed from the wallet

Reservation Events

Event TypeDescription
reservation.createdA new reservation was created
reservation.committedReservation was committed (funds debited)
reservation.releasedReservation was released (funds returned)
reservation.expiredReservation expired automatically
reservation.extendedReservation expiration was extended

Transfer Events

Event TypeDescription
transfer.createdA transfer was completed

Lot Events

Event TypeDescription
lot.createdA new lot was created
lot.updatedLot attributes were updated
lot.expiredLot expired (automatic or manual)
lot.depletedLot balance reached zero

Get Event

Retrieve a specific event by ID.

GET /events/{{EVENT_ID}}

Example Request

curl -X GET {{BASE_URL}}/events/{{EVENT_ID}} \
  -H "Authorization: Bearer {{API_KEY}}"

Example Response

{
  "data": {
    "id": "evt_abc123def456",
    "type": "wallet.credited",
    "api_version": "2024-01-01",
    "created_at": "2024-01-15T10:30:00Z",
    "data": {
      "wallet_id": "wal_abc123def456",
      "amount": "100.00",
      "asset": "POINTS",
      "credit_id": "crd_xyz789abc123"
    },
    "delivery_attempts": [
      {
        "webhook_id": "whk_abc123def456",
        "status": "delivered",
        "response_code": 200,
        "attempted_at": "2024-01-15T10:30:01Z"
      }
    ]
  }
}

List Events

Retrieve a paginated list of events.

GET /events

Query Parameters

ParameterTypeDefaultDescription
limitinteger20Number of results (max 100)
cursorstring-Pagination cursor
typestring-Filter by event type
wallet_idstring-Filter by wallet
from_datestring-Events after this date (ISO 8601)
to_datestring-Events before this date (ISO 8601)

Example Request

curl -X GET "{{BASE_URL}}/events?type=wallet.credited&wallet_id={{WALLET_ID}}&limit=10" \
  -H "Authorization: Bearer {{API_KEY}}"

Example Response

{
  "data": [
    {
      "id": "evt_abc123def456",
      "type": "wallet.credited",
      "wallet_id": "wal_abc123def456",
      "created_at": "2024-01-15T10:30:00Z"
    },
    {
      "id": "evt_def456ghi789",
      "type": "wallet.credited",
      "wallet_id": "wal_abc123def456",
      "created_at": "2024-01-14T15:00:00Z"
    }
  ],
  "pagination": {
    "has_more": true,
    "next_cursor": "eyJpZCI6ImV2dF9kZWY0NTZnaGk3ODkifQ=="
  }
}