Developers

Webhooks

Set up webhooks to receive real-time notifications when events occur in your Phasio account

Webhooks allow you to receive automated HTTP notifications when specific events happen in your Phasio account. When an event is triggered, Phasio sends an encrypted POST request to the URL you provide, letting you integrate with external systems such as ERPs, CRMs, or custom automation.

Setting up a webhook

  1. Navigate to Developers from the main sidebar.
  2. Click
  3. Select a Trigger — the event type that should fire the webhook.
  4. Enter the URL of the endpoint that should receive the notification. The URL must use http:// or https:// and point to a valid domain.
  5. Click Add to save the webhook.

Once created, Phasio generates a unique secret for the webhook — a Base64 URL-safe encoded AES-256 encryption key. This secret is used to both encrypt the webhook payload and sign the request so your endpoint can verify authenticity and decrypt the data.

Copying the secret

  1. In the webhooks table, open the actions menu (⋯) for the webhook.
  2. Click Copy Secret to copy the secret to your clipboard.

Important: Treat the secret like a password. Store it securely and never expose it in client-side code or public repositories.

Available triggers

TriggerDescription
Order CreatedFires when a new order is created or committed.
Order UpdatedFires when an order's details, status, payment status, or Kanban column changes.
Customer Organisation CreatedFires when a new customer organisation is created.
Customer Organisation UpdatedFires when a customer organisation's details are modified.
Part Specification CreatedFires when a new part specification is created.

Request format

When a webhook fires, Phasio sends an HTTP POST request to your endpoint with the following structure:

Headers

HeaderDescription
Content-Typeapplication/json
X-Phasio-SignatureHex-encoded HMAC-SHA256 signature of the encrypted payload string, computed using the UTF-8 bytes of your secret as the HMAC key.

Body

{
  "payload": "<Base64-encoded encrypted string>"
}

The payload value is the JSON event data encrypted with AES-256-GCM, then Base64 URL-safe encoded. Your secret (a Base64 URL-safe encoded AES-256 key) is required to both verify the signature and decrypt the payload.

Verifying and decrypting the payload

To read the event data, your endpoint must:

  1. Verify the signature — compute HMAC-SHA256 over the raw payload string, using the UTF-8 bytes of your secret as the HMAC key. Compare the hex-encoded result to the X-Phasio-Signature header. Reject the request if they don't match.
  2. Decrypt the payload — Base64 URL-safe decode the payload string to get the raw bytes. The first 12 bytes are the initialization vector (IV), followed by the AES-256-GCM ciphertext and authentication tag (128-bit). Decrypt using the Base64-decoded secret as the AES key.

Note the distinction: the HMAC key is the secret string's raw UTF-8 bytes, while the AES key is the Base64-decoded bytes of the secret.

Example (Node.js)

import { createDecipheriv, createHmac, timingSafeEqual } from 'node:crypto'

function verify(payload, signature, secret) {
  const expected = createHmac('sha256', secret).update(payload).digest()
  return timingSafeEqual(expected, Buffer.from(signature, 'hex'))
}

function decrypt(payload, secret) {
  const key = Buffer.from(secret, 'base64url')
  const data = Buffer.from(payload, 'base64url')

  const decipher = createDecipheriv('aes-256-gcm', key, data.subarray(0, 12))
  decipher.setAuthTag(data.subarray(-16))

  return Buffer.concat([
    decipher.update(data.subarray(12, -16)),
    decipher.final(),
  ]).toString()
}

Usage in a request handler:

const { payload } = req.body
const signature = req.headers['x-phasio-signature']

if (!verify(payload, signature, process.env.WEBHOOK_SECRET)) {
  return res.status(401).end()
}

const event = JSON.parse(decrypt(payload, process.env.WEBHOOK_SECRET))

Payload schemas

After decrypting the payload, you'll receive a JSON object whose structure depends on the trigger type.

{
  "orderId": 12345,
  "quoteNumber": "Q-001",
  "orderNumber": "ORD-001",
  "customerOrganisationId": 678,
  "customerOrganisationName": "Acme Manufacturing",
  "createdDate": "2025-06-15T10:30:00",
  "kanbanColumnName": "In Progress",
  "kanbanColumnSequence": 2,
  "paymentStatus": "PAID",
  "parts": [
    {
      "id": 1,
      "specificationId": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Bracket Assembly",
      "technology": "CNC Machining",
      "material": "Aluminum 6061",
      "quantity": 100,
      "pricePerPart": 12.50,
      "color": null,
      "infill": null,
      "precision": "Standard",
      "postProcessings": [
        {
          "name": "Anodizing",
          "price": 2.00
        }
      ]
    }
  ],
  "expenses": [
    {
      "id": 1,
      "name": "Setup Fee",
      "price": 50.00
    }
  ],
  "pricing": {
    "currency": "USD",
    "shippingFee": 25.00,
    "taxPercentage": 10.00,
    "taxAmount": 125.00,
    "applyTaxToShipping": true,
    "discountPercentage": 5.00,
    "paymentDueDate": "2025-07-15",
    "topUpAmount": null
  },
  "shipping": {
    "shippingMethod": "FedEx Ground",
    "address": {
      "id": "addr-001",
      "street1": "123 Industrial Ave",
      "street2": "Suite 400",
      "city": "Austin",
      "state": "TX",
      "country": "US",
      "zip": "73301",
      "name": "Acme Manufacturing",
      "phone": "+1-555-0100",
      "email": "receiving@acme.com",
      "residential": false
    }
  }
}
{
  "id": 678,
  "name": "Acme Manufacturing",
  "taxNumber": "US123456789",
  "language": "en",
  "customers": [
    {
      "id": 1,
      "email": "john@acme.com",
      "phoneNumber": "+1-555-0100",
      "firstName": "John",
      "lastName": "Doe",
      "notificationPreference": "EMAIL"
    }
  ],
  "addresses": [
    {
      "id": "addr-001",
      "street1": "123 Industrial Ave",
      "street2": null,
      "city": "Austin",
      "state": "TX",
      "country": "US",
      "zip": "73301",
      "name": "Headquarters",
      "phone": "+1-555-0100",
      "email": "info@acme.com",
      "residential": false
    }
  ],
  "isTaxExempt": false,
  "isApproved": true,
  "createdAt": "2025-01-10T08:00:00",
  "lastUpdatedOn": "2025-06-15T14:20:00"
}
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "revisionId": "660e8400-e29b-41d4-a716-446655440000",
  "name": "Bracket Assembly",
  "units": "mm",
  "technology": "CNC Machining",
  "material": "Aluminum 6061",
  "infill": null,
  "precision": "Standard",
  "color": null,
  "postProcessings": ["Anodizing", "Bead Blasting"]
}

Deleting a webhook

  1. In the webhooks table, open the actions menu (⋯) for the webhook.
  2. Click Delete to remove the webhook. Phasio will stop sending requests to that endpoint immediately.

Last updated on