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
- Navigate to Developers from the main sidebar.
- Click
- Select a Trigger — the event type that should fire the webhook.
- Enter the URL of the endpoint that should receive the notification. The URL must use
http://orhttps://and point to a valid domain. - 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
- In the webhooks table, open the actions menu (⋯) for the webhook.
- 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
| Trigger | Description |
|---|---|
| Order Created | Fires when a new order is created or committed. |
| Order Updated | Fires when an order's details, status, payment status, or Kanban column changes. |
| Customer Organisation Created | Fires when a new customer organisation is created. |
| Customer Organisation Updated | Fires when a customer organisation's details are modified. |
| Part Specification Created | Fires 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
| Header | Description |
|---|---|
Content-Type | application/json |
X-Phasio-Signature | Hex-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:
- Verify the signature — compute HMAC-SHA256 over the raw
payloadstring, using the UTF-8 bytes of your secret as the HMAC key. Compare the hex-encoded result to theX-Phasio-Signatureheader. Reject the request if they don't match. - Decrypt the payload — Base64 URL-safe decode the
payloadstring 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
- In the webhooks table, open the actions menu (⋯) for the webhook.
- Click Delete to remove the webhook. Phasio will stop sending requests to that endpoint immediately.
Last updated on