Skip to main content

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": { "..." : "..." }
}
}
}
FieldDescription
typeThe UI context that triggered the agent (for example SALES_HUB_ORDER_EDITOR)
adminUserIdThe backoffice user who triggered the agent
requestMessageThe user's message (present for chat agents, absent for button agents)
conversationIdPresent for follow-up messages in multi-turn conversations
requestMetadata.payloadThe 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"
}
}
FieldDescription
conversationIdEchoed back to link the response to the conversation
responseMessageText displayed to the user in the UI
responseMetadataOptional 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 × quantity for all main items and their child items to get total cost
  • Calculates revenue as subTotalGross - discountGross from 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:

  1. Accept a POST request with a JSON body
  2. Optionally validate the Authorization header (Basic Auth) and/or X-Propeller-Signature header (HMAC-SHA256)
  3. Read the context from body.requestMetadata.payload
  4. Return a JSON response with responseMessage and optionally responseMetadata

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