Cabinetry.Online has a number of webhook triggers where external URLs can be called on certain actions.
This acts as an additional check / validation step when submitting a job.
This can be used to implement:
This is a replacement for Cabinetry.Online's built-in pricing for manufacturers who have complicated pricing requirements or who already have systems in place to calculate pricing, tax, discounts, etc. on a job.
| Field | Example | Description |
|---|---|---|
| Enable webhook | On / Off | Whether the webhook is enabled |
| Authentication Type | Bearer Token | Type of authentication used by the webhook. See below |
| Export Details Format | JSON | Format to use for the job export in the payload |
| Field | Example | Description |
|---|---|---|
| Bearer Token | some-bearer-token |
Token to pass in the Authorization header when calling the webhook |
This uses the Client Credentials Flow to generate a token for the webhook request. This will be cached for the validity period returned by Auth0.
| Field | Example | Description |
|---|---|---|
| Tenant Domain | example.au.auth0.com |
Auth0 tenant domain name |
| Client ID | Client ID | |
| Client Secret | Client secret | |
| Audience | Audience |
The Client ID, Client Secret and Audience should be copied from Auth0's configuration.
Upon being triggered, the webhook will send a JSON payload and any authentication headers to the URL specified.
All webhooks use the same request structure.
POST https://example.com/webhook
Authorization: Bearer some-bearer-token
{
"export": {
"JobNumber": 348167,
"JobName": "Test Job",
"Status": "Processing",
"Description": "",
"CustomerName": "Test Account",
"Manufacturer Name": "Example",
"InvoiceTo": "Test Account",
"OrderDate": "26th Feb 2024 at 6:31pm",
"ExpectedDeliveryDate": "-",
"DispatchMethod": "Default Address",
"ContactNumber": "0412123123",
"CustomerId": 27465,
"JobRooms": [
{
"RoomNumber": 1,
"RoomName": "Room 1",
"RoomDescription": "",
"CompanyLabel": "Test Account 0412123123",
"InteriorMaterial": "Polytec SolidMatt MRMDF 16.5 Canterbury Grey",
"ExteriorMaterial": "Polytec SolidMatt MRMDF 16.5 Canterbury Grey",
"InteriorEdging": "Polytec SolidMatt 1 Mountain Pepper",
"ExteriorEdging": "Polytec SolidMatt 1 Mountain Pepper",
"DoorDwrProductName": "Flat Panel",
"HingeSeries": "None",
"DrawerSystem": "Blum Metabox",
"ToeKickHeight": "0",
"JobCabinets": [
{
"RoomProductNo": "1-1.00",
"Type": "Door",
"ProcessName": "DoorQFP",
"ProductDescription": "Door",
"Quantity": "1",
"ProductHeight": "900",
"ProductWidth": "550",
"ExteriorMaterialType": "Melamine",
"ExteriorMaterial": "Polytec SolidMatt MRMDF 16.5 Canterbury Grey",
"ExteriorEdging": "Polytec SolidMatt 1 Mountain Pepper",
"InteriorMaterialType": "Non Supply",
"DoorDwrEdgingLeft": "Yes",
"DoorDwrEdgingRight": "Yes",
"DoorDwrEdgingTop": "Yes",
"DoorDwrEdgingBottom": "Yes",
"ExcludeHardware": "Yes",
"SpecificHingeHeights": "None",
"DoorHangType": "Single",
"DoorHang": "None",
"HingeSeries": "None",
"DoorDwrProductName": "Flat Panel"
},
{
"RoomProductNo": "1-2.00",
"Type": "Drawer Face",
"ProcessName": "DrawerQFP",
"ProductDescription": "Drawer Pack",
"Quantity": "1",
"ProductHeight": "900",
"ProductWidth": "550",
"DrawerDetail": "298.66;298.66;298.66;",
"ExteriorMaterialType": "Melamine",
"ExteriorMaterial": "Polytec SolidMatt MRMDF 16.5 Canterbury Grey",
"ExteriorEdging": "Polytec SolidMatt 1 Mountain Pepper",
"InteriorMaterialType": "Non Supply",
"DoorDwrEdgingLeft": "Yes",
"DoorDwrEdgingRight": "Yes",
"DoorDwrEdgingTop": "Yes",
"DoorDwrEdgingBottom": "Yes",
"EdgeJoiningBorder": "Yes",
"ExcludeHardware": "Yes",
"DrawerAmount": "3",
"DoorDwrProductName": "Flat Panel",
"DrawerFaceType": "Individual Faces",
"ClearanceBetweenDrawers": "2"
},
{
"RoomProductNo": "1-3.00",
"Type": "Manual Panel",
"ProcessName": "PanelQFP",
"ProductDescription": "Flat Panel",
"Quantity": "1",
"ProductLength": "900",
"ProductWidth": "550",
"ExteriorMaterialType": "Melamine",
"ExteriorMaterial": "Polytec SolidMatt MRMDF 16.5 Canterbury Grey",
"ExteriorEdging": "Polytec SolidMatt 1 Mountain Pepper",
"InteriorMaterialType": "Non Supply",
"ExteriorEdgeFront": "Yes",
"ExteriorEdgeBack": "Yes",
"ExteriorEdgeLeft": "Yes",
"ExteriorEdgeRight": "Yes",
"ExcludeHardware": "Yes"
}
],
"RawJobBenchtops": []
}
],
"JobSundries": [],
"jobBenchtops": []
},
"job": {
"id": 338167,
"customerId": 27465,
"status": 0,
"name": "Test Job",
"description": "",
"dispatchMethod": 2,
"address": "1 Fake Street",
"suburb": "Example Suburb",
"state": "Victoria",
"postcode": "3220",
"variationCost": 0,
"freightCost": 50,
"dateEntered": "2024-02-26T18:31:56+00:00",
"dateUpdated": "2024-02-26T18:32:49+00:00",
"dateSubmitted": "0000-00-00T00:00:00+00:00",
"dateAccepted": "0000-00-00T00:00:00+00:00",
"dateDelivery": "0000-00-00T00:00:00+00:00",
"datePaid": "0000-00-00T00:00:00+00:00",
"accepted": 0,
"customerName": "Test Account",
"manufacturerName": "Example",
"variationsConfirmed": 0,
"taxRate": 10,
"currencyAbbreviation": "AUD",
"currencyType": "$",
"cost": 332.96,
"roomCount": "1",
"productCount": 3,
},
"customer": {
"id": 27465,
"manufacturer_id": 103,
"name": "Test Account",
"email": "example@cabinetsbycomputer.com",
"phone": "0412123123",
"mobile": "",
"cc_email": "",
"price_adjustment": 0,
"minimum_charge_amount": 0,
"minimum_freight_amount": 50,
"contact_address": {
"street": [
"1 Fake Street"
],
"suburb": "Example Suburb",
"state": "Victoria",
"state_code": "VIC",
"postcode": "1232",
"country": "Australia",
"country_code": "AU"
},
"delivery_address": {
"street": [
"1 Fake Street"
],
"suburb": "Example Suburb",
"state": "Victoria",
"state_code": "VIC",
"postcode": "3220",
"country": "Australia",
"country_code": "AU"
},
"customer_labels": [],
"settings": {
"is_beta": true,
"is_premium": false,
"is_assembly_enabled": false,
"is_suspended": false
}
},
"success": 1
}
This payload consists of three components:
{
"export": {...},
"job": {...},
"customer": {...}
}
Each component in the payload is the exact data returned by one of the other API endpoints present in the Cabinetry.Online API.
| Component | Source |
|---|---|
export |
Job Download |
job |
Job Listing |
customer |
Customer Management |
The data in each top-level component is identical to the data that would be returned by running an API request for each of those types of data for the job the webhook was triggered for and the customer who owns that job.
Note that depending on the size of the job, this object may be quite large.
This is the data returned by the job download API in the format selected in the webhook settings.
If the "JSON" format is selected, the JSON job export is embedded in the outer object returned like in the example above. If any other option is returned, this is a string containing the export.
This is the job data as returned from the job listing API.
Note that the columns documented in the job listing API are stable and other values may come and go depending on operational requirements.
This is the customer details as returned from the customer management API.
Each type of webhook has different response requirements based on their parituclar purpose.
However all responses will follow the same overall requirements:
success key is present in a response with a 200 status, this will be used to determine if the response is successful or not.message key is present, it will be used as a message or array of messages to be shown to the customerAny response that has either a non-200 HTTP status or a success key that isn't truthy will be assumed to be failed.
| Field | Example | Requried | Description |
|---|---|---|---|
success |
false |
No | Whether the request succeded, ignored if the HTTP status is not 200 |
errors |
["No widgets", "User blocked"] |
No | Message or array of messages describing why the request failed. Will be shown to customers |
If the errors field isn't present in a response, a generic "something went wrong" message will be shown to the customer.
{
"success": false,
"errors": [
"No Widgets",
"User Blocked",
]
}
Fields:
| Field | Example | Required | Description |
|---|---|---|---|
success |
true |
No | Whether the request succeeded, must be truthy |
message |
["Order #123456", "Please contact support for delivery"] |
No | Message(s) to store on the job and display to the customer, can be a string or array of strings |
id |
MEL-123456 |
No | External ID to store on the job |
{
"success": true,
"message": "Order #123456",
"id": "JPS-990495"
}
Fields:
| Field | Example | Required | Description |
|---|---|---|---|
cost |
1234.56 |
Yes | Total cost, including tax, of the job |
success |
true |
No | Whether the request succeeded, must be truthy |
message |
["10% first order discount applied", "Freight is for North Melbourne 3051"] |
No | Message(s) to store on the job and display to the customer, can be a string or array of strings |
breakdown |
[{"name": "Tax", "cost": 123.46}] |
No | Cost breakdown and any other cost lines to show to the customer |
The breakdown field is a tree structure containing a detailed breakdown of the pricing of the job.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Name of the line item |
cost |
number | No | Cost of the line item |
collapse |
boolean | No | Whether the child nodes of this one should be collapsed |
items |
Array of breakdown objects | No | Child items |
E.g.
[
{"name": "Subtotal", "cost": 123.45},
{"name": "Discounts", "collapse": true, "items": [
{"name": "Free freight", "cost": -100},
{"name": "10% coupon", "cost": -12.34}
]}
]
Would be displayed as this:
with the two discounts hidden by default.
Note:
cost values for items with child items will be calculated from the costs of the child items.{
"cost": 1234.56
}
{
"success": true,
"cost": 1234.56,
"message": "10% first order discount applied",
"breakdown": [
{
"name": "Discounts",
"items": [
{
"cost": 123.46,
"name": "10% First Order Discount"
}
]
}, {
"cost": 101.01,
"name": "Tax",
}
]
}