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": "can you give me advice how to increase the order value?",
"requestMetadata": {
"payload": {
"tenderId": "019d026b-99ce-7b94-b5cd-f5d436d23f78",
"orderId": 3043,
"status": "VALIDATED",
"channelId": 1,
"source": "Sales Portal",
"contact": { "contactId": 14780, "firstName": "Wouter", "lastName": "Smrkovsky" },
"company": { "companyId": 308, "name": "M&G Group" },
"items": [ { "name": "La Marzocco Linea Mini – Yellow", "quantity": 1, "price": "4796.55", "costPrice": 3650, "childItems": [] } ],
"total": { "subTotalGross": 6246.94, "totalGross": 6291.94, "totalTax": 1321.31 },
"carriers": [ { "name": "DHL", "id": 8, "amount": 2.36 } ],
"payMethods": [ { "code": "IDEAL", "description": "Ideal | Wero", "allowed": true } ],
"paymentData": { "method": "", "overruled": "N", "price": 35 },
"postageData": { "carrier": null, "overruled": "N", "price": 10 },
"revisions": { "itemsFound": 4, "items": [] }
}
}
}
| Field | Description |
|---|---|
type | The agent type that triggered the request (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. Structure varies by agent type. See Agent payloads for the complete field reference |
What the webhook must return
The webhook responds with a JSON object. Only responseMessage is required. The structure of responseMetadata is entirely up to you and depends on what your workflow does.
{
"conversationId": "4308",
"responseMessage": "The result of your workflow, displayed to the user.",
"responseMetadata": {
"anyKey": "anyValue"
}
}
| Field | Required | Description |
|---|---|---|
conversationId | No | Echoed back to link the response to the conversation |
responseMessage | Yes | Text displayed to the user in the UI |
responseMetadata | No | Freeform object. Use it to pass structured data back to the UI or downstream systems. The contents are entirely defined by your workflow |
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.
Try it yourself
Download the Margin Validator n8n workflowTo get the example running:
- In n8n, go to Settings → Import from File and select the downloaded JSON
- Open the Webhook node and create a new Basic Auth credential with a username and password of your choice
- Activate the workflow and copy the webhook URL that n8n generates
- In the Propeller Backoffice, create an agent with:
- Webhook URL set to the n8n webhook URL
- Basic auth credentials matching what you configured in step 2
- Type set to
SALES_HUB_QUOTE_EDITOR - Trigger set to
BUTTON
- Open a quote in the Sales Hub and click the agent button to validate the margin
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
- Agent payloads for the complete field reference per agent type
- Conversations for the conversation lifecycle