Webhook integration
The webhook URL on an agent points to an external workflow engine. Any system that can receive HTTP POST requests and return JSON responses will work. These examples use n8n but the same principles apply to any workflow engine or custom API.
What the webhook receives
Propeller sends a POST request to the configured webhookUrl with the following structure.
Headers
Content-Type: application/json(always included)Authorization: Basic ...(included when basic auth credentials are configured on the agent)X-Propeller-Signature: sha256=<hex-digest>(included when a signature salt is configured on the agent)
Body
{
"type": "SALES_HUB_ORDER_EDITOR",
"adminUserId": 205,
"requestMessage": "give me advice on how to sell more",
"conversationId": "4306",
"requestMetadata": {
"payload": {
"orderId": 3043,
"status": "VALIDATED",
"contact": { "..." : "..." },
"company": { "..." : "..." },
"items": [ "..." ],
"total": { "..." : "..." },
"carriers": [ "..." ],
"payMethods": [ "..." ],
"revisions": { "..." : "..." }
}
}
}
| Field | Description |
|---|---|
type | The UI context that triggered the agent (for example SALES_HUB_ORDER_EDITOR) |
adminUserId | The backoffice user who triggered the agent |
requestMessage | The user's message (present for chat agents, absent for button agents) |
conversationId | Present for follow-up messages in multi-turn conversations |
requestMetadata.payload | The full page context including order/quote data with all line items, pricing, margins, customer info, addresses, carriers and payment methods |
What the webhook must return
The webhook responds with a JSON object containing:
{
"conversationId": "4308",
"responseMessage": "Quote #3043 is approved.\n\nMargin: 28.34%",
"responseMetadata": {
"processed": true,
"marginPercentage": "28.34",
"marginAmount": "1770.13",
"totalRevenue": "6246.94",
"totalCost": "4476.81",
"threshold": 20,
"timestamp": "2026-03-18T19:29:15.105Z"
}
}
| Field | Description |
|---|---|
conversationId | Echoed back to link the response to the conversation |
responseMessage | Text displayed to the user in the UI |
responseMetadata | Optional structured data that the UI or downstream systems can use for conditional rendering or further automation |
Example: Margin Validator n8n workflow
This n8n workflow powers the Margin Validator agent. It receives quote data from Propeller, calculates the overall margin and returns an approval or warning.
Workflow nodes
Webhook → Code (Process Quote Data) → Respond to Webhook
1. Webhook node listens for POST requests on the agent's webhook path. Configured with basic auth credentials matching the agent's webhookBasicAuthUsername and webhookBasicAuthPassword. Uses "response node" mode so the response is sent by a later node.
2. Code node contains the business logic. It extracts the quote payload from body.requestMetadata.payload and:
- Sums
costPrice × quantityfor all main items and their child items to get total cost - Calculates revenue as
subTotalGross - discountGrossfrom the totals - Computes margin percentage as
(revenue - totalCost) / revenue × 100 - Compares against a 20% threshold
- Returns an approval message if the margin is at or above threshold, or a warning if below
Core logic:
const quoteData = inputData.body?.requestMetadata?.payload || {};
const quoteItems = quoteData.items || [];
// Sum cost prices across all items including children
let totalCost = 0;
quoteItems.forEach(item => {
totalCost += (item.costPrice || 0) * (item.quantity || 1);
if (item.childItems) {
item.childItems.forEach(child => {
totalCost += (child.costPrice || 0) * (child.quantity || 1);
});
}
});
// Calculate margin
const totalRevenue = (quoteData.total?.subTotalGross || 0)
- (quoteData.total?.discountGross || 0);
const marginAmount = totalRevenue - totalCost;
const marginPercentage = totalRevenue > 0
? (marginAmount / totalRevenue) * 100
: 0;
const MARGIN_THRESHOLD = 20;
The response follows the required format:
return [{
json: {
conversationId: conversationId,
responseMessage: responseText,
responseMetadata: {
processed: true,
marginPercentage: marginPercentage.toFixed(2),
marginAmount: marginAmount.toFixed(2),
totalRevenue: totalRevenue.toFixed(2),
totalCost: totalCost.toFixed(2),
threshold: MARGIN_THRESHOLD,
timestamp: new Date().toISOString()
}
}
}];
3. Respond to Webhook node sends the code node's output back to Propeller as a JSON response.
Building your own workflow
Any workflow engine or custom API can serve as the webhook backend. The requirements are:
- Accept a POST request with a JSON body
- Optionally validate the
Authorizationheader (Basic Auth) and/orX-Propeller-Signatureheader (HMAC-SHA256) - Read the context from
body.requestMetadata.payload - Return a JSON response with
responseMessageand optionallyresponseMetadata
Since the webhook is fully external, tenants and partners can build any logic they need: call LLMs, query databases, integrate with CRMs, run business rules or chain multiple steps together.
See also
- Agent configuration for setting up webhook credentials
- Conversations for the conversation lifecycle