Overview

Webhooks allow your application to receive real-time notifications when events occur in Phoenix. When an order is created or its status changes, Phoenix can automatically send an HTTP POST request to your specified webhook endpoints.

Getting Started

1. Obtain Webhook API Key

Before setting up webhooks, you need to obtain a webhook API key from Phoenix:

  1. Contact our support team at main@phoenix.market
  2. Request webhook access for your integration

2. Define Your Webhook Endpoints

Your webhook endpoints should:

  • Accept POST requests
  • Return a 200 status code to acknowledge receipt
  • Process the webhook payload within 30 seconds
  • Validate the HMAC signature in the X-Webhook-Signature header
  • Handle duplicate events gracefully (use id for idempotency)

HMAC Signature

Phoenix provides HMAC signatures to help you verify the authenticity of webhook requests. The signature is calculated using your client_secret and the payload of the request, allowing you to validate that the webhook came from Phoenix.

The signature is sent in the X-Webhook-Signature header.

The signature’s timestamp is sent in the X-Webhook-Timestamp header.

The signature is calculated using the HMAC-SHA256 algorithm.

Signature Verification Example

Here’s how to verify the webhook signature in your endpoint:

const crypto = require('crypto');

// In your webhook endpoint
app.post('/webhook', (req, res) => {
  const signature = req.headers['x-webhook-signature']; // Or your custom header
  const timestamp  = req.headers['x-webhook-timestamp'];
  const rawBody = req.body; // Buffer, not parsed JSON!


const endpointSecret = 'Your-clientsecret-key'

  // Create the signed payload string
  const signedPayload = `${timestamp}.${JSON.stringify(rawBody)}`;

  // Compute the HMAC
  const expectedSignature = crypto
    .createHmac('sha256', endpointSecret)
    .update(signedPayload)
    .digest('hex');

    console.log(expectedSignature);
    console.log(signature);

  // Compare signatures (use timingSafeEqual in production)
  if (crypto.timingSafeEqual(
    Buffer.from(expectedSignature, 'hex'),
    Buffer.from(signature, 'hex')
  )) {
    // Signature is valid, process the event
    console.log('Webhook verified!');
    res.status(200).send('Webhook verified!');
  } else {
    console.log('Invalid signature');
    // Invalid signature
    res.status(400).send('Invalid signature');
  }
});

3. Share Your Webhook Endpoint URLs

Once you’ve defined your webhook endpoints, you need to share the exact URLs with Phoenix:

  1. Determine the full URL path where you want to receive webhooks (e.g., https://yourdomain.com/webhooks/phoenix-status-updates)
  2. Contact our support team at main@phoenix.market with your webhook endpoint URL
  3. Phoenix will configure the system to send webhook events to your specified URL

Webhook Events

Each webhook event contains a unique id field for deduplication. Implement idempotency using this id as Phoenix may retry events if your endpoint doesn’t return a 200 status code or if the request fails.

Current events:

  • payment_link_created
  • payment_link_status_updated
  • order_created
  • order_status_updated

Event Payload Structure

This structure may change over time, so do stay up to date with the docs.

{
  "event": "",
  "id": "xxx",
  "timestamp": "1970-01-01T00:00:00.001Z",
  "data": {
    "paymentLink" || "order": {
      "id": "xxx"
    }
  }
}

Querying Orders

You can query order details at any time using the Phoenix API. This is useful for getting the complete order information or checking the current status.

You can use this endpoint to test your connection to the Phoenix service before setting up webhooks.

Endpoint

GET https://prod-phoenix-api-kwr2.encr.app/orders/:orderId

Example Request

const orderId = "tpT9KpKVyHU1P24RjZcW1o";
const response = await fetch(`https://prod-phoenix-api-kwr2.encr.app/orders/${orderId}`);
const orderData = await response.json();

Example Response

Order Status Update Webhook

Endpoint Configuration

Endpoint: /phoenix-order-status-updated Event Type: order.status.change Triggered: When an order’s status changes

Common Status Values

  • fulfilled - Order has been completed successfully
  • expired - Order expired before completion
  • pending - Order is awaiting processing

Implementation Example

app.post('/phoenix-order-status-updated', (req, res) => {
  const { event, id, timestamp, data } = req.body;
  const { order, orderId, orderStatus } = data;
  
  // Validate required fields
  if (!event || !data || !data.orderId || !data.orderStatus) {
    return res.status(400).json({
      error: 'Missing required fields',
      required: ['event', 'data.orderId', 'data.orderStatus']
    });
  }
  
  // Validate authorization header
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({
      error: 'Missing or invalid authorization header'
    });
  }
  
  // Process the status update
  console.log(`Order ${data.orderId} status changed to: ${data.orderStatus}`);
  // Add your business logic here
  
  res.json({
    success: true,
    message: 'Order status webhook received',
    external_partner_id: "your-internal-order-123" // Optional: your own order ID
  });
});

Payload Structure

Webhook Response

Your webhook endpoint should return a JSON response with a 200 status code. You can optionally include an external_partner_id field to associate your own order ID with the Phoenix order:

// Basic response
res.json({
  success: true,
  message: 'Order status webhook received'
});

// Response with optional external partner ID
res.json({
  success: true,
  message: 'Order status webhook received',
  external_partner_id: "your-internal-order-123" // Optional: your own order ID
});

Optional Response Field:

  • external_partner_id (string): Your internal order ID or reference that you want to associate with the Phoenix order. This helps you link Phoenix orders to your own system’s order tracking.

Authentication

Phoenix uses Bearer token authentication for webhooks:

  • Header: Authorization: Bearer <webhook_secret>
  • Webhook Secret: Provided by Phoenix support team
  • Validation: Always verify the authorization header exists and contains a valid Bearer token

Best Practices

  1. Validate Authentication: Always check the Authorization header
  2. Validate Required Fields: Ensure all required fields are present
  3. Implement Idempotency: Use id to handle duplicate events
  4. Respond Quickly: Process webhooks within 30 seconds
  5. Log Events: Keep logs for debugging and monitoring
  6. Use HTTPS: Only accept webhooks over secure HTTPS connections
  7. Handle Errors Gracefully: Return appropriate HTTP status codes

Support

For webhook-related questions or to request webhook access: