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": "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": [] }
}
}
}
FieldDescription
typeThe agent type that triggered the request (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. 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"
}
}
FieldRequiredDescription
conversationIdNoEchoed back to link the response to the conversation
responseMessageYesText displayed to the user in the UI
responseMetadataNoFreeform 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 × 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.

Try it yourself

Download the Margin Validator n8n workflow

To get the example running:

  1. In n8n, go to Settings → Import from File and select the downloaded JSON
  2. Open the Webhook node and create a new Basic Auth credential with a username and password of your choice
  3. Activate the workflow and copy the webhook URL that n8n generates
  4. 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
  5. 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:

  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