ActivePieces Recipe: Live Price Adjustments
A copy-paste flow that adjusts customer-visible pricing in real time — no server required
Build the full live-pricing loop inside your ActivePieces project: webhook trigger → code step with your pricing logic → one HTTP call back. About ten minutes; no infrastructure.
Before you start
- Settings → Integrations → Live pricing events: toggle on.
- Create a dedicated flow for pricing (recommended). Pricing events fire on every meaningful configuration change — far more often than business events — and the customer is literally watching the spinner while your flow runs. Keep it out of your notification flow:
- In ActivePieces, create a new flow with a Catch Webhook trigger and copy its URL.
- Paste that URL into Dedicated pricing webhook URL in the same settings section.
- Your existing notification flow keeps receiving only the nine business events.
Step 1 — Trigger: Catch Webhook
Nothing to configure beyond creating it. Each pricing.calculated event arrives as the trigger body; the fields you'll use:
| Field | Path |
|---|---|
| Callback URL | {{ trigger['body']['data']['callback']['url'] }} |
| Config hash | {{ trigger['body']['data']['configHash'] }} |
| Base line items | {{ trigger['body']['data']['basePricing']['lineItems'] }} |
| Configuration | {{ trigger['body']['data']['configuration'] }} |
| Context | {{ trigger['body']['data']['context'] }} |
Step 2 — Code step: your pricing logic
Add a Code step. Pick a snippet and adapt — each one receives the trigger body and returns the PUT payload.
A. Percentage markup on every item:
export const code = async (inputs) => {
const data = inputs.event.data;
const MARKUP = 1.12; // +12%
const lineItems = data.basePricing.lineItems.map((item) => ({
...item,
unitPrice: Math.round(item.unitPrice * MARKUP * 100) / 100,
}));
return { configHash: data.configHash, lineItems };
};B. Reprice specific items by itemKey (e.g. your own glass prices):
export const code = async (inputs) => {
const data = inputs.event.data;
// itemKey format: "level:strategyId:sourceId" — discover yours via
// GET /api/v1/templates/{id}/catalog or by logging one event.
const MY_PRICES = {
"option:k72abc:k88def": 165.0, // HR++ glass, your price
};
const lineItems = data.basePricing.lineItems.map((item) =>
MY_PRICES[item.itemKey] !== undefined
? { ...item, unitPrice: MY_PRICES[item.itemKey] }
: item
);
return { configHash: data.configHash, lineItems };
};C. Append a fixed fee (keep base items untouched):
export const code = async (inputs) => {
const data = inputs.event.data;
const lineItems = [
...data.basePricing.lineItems,
{ description: "Mounting & delivery", quantity: 1,
unitPrice: 250.0, itemType: "labor", sortOffset: 99 },
];
return { configHash: data.configHash, lineItems };
};Configure the step's input: event → {{ trigger['body'] }}.
Need data from your ERP? Add an HTTP step before the code step to fetch it — just keep total flow time inside the configured timeout (default 10s) so the customer sees your price without the fallback note.
Step 3 — HTTP step: send the adjustment
Add an HTTP step:
| Setting | Value |
|---|---|
| Method | PUT |
| URL | {{ trigger['body']['data']['callback']['url'] }} |
| Headers | Content-Type: application/json |
| Body (JSON) | {{ step_2 }} (the code step's output) |
No Authorization header — the callback URL itself is the credential (capability URLs).
A 200 with {"applied": true, "isCurrentConfiguration": true} means the customer's screen just updated. A 409 stale_config_hash means the customer kept clicking — ignore it; you already received a fresh event for the new configuration.
Publish & verify
- Publish the flow.
- Open one of your template links, set dimensions, and watch the price button: amber dot ("confirming final price…") → your adjusted price. Open the price breakdown to see your line items and the "Pricing confirmed by …" note.
- Submit the configuration: the quote form's auto-fill now uses your items, and
submission.createdreportsdata.pricing.source: "external".
Troubleshooting
| Symptom | Check |
|---|---|
| Configurator always falls back to base price | Flow run history: is the flow failing or slower than the timeout? Raise the timeout (Settings, up to 30s) or trim slow steps. |
409 stale_config_hash on most runs | Normal during rapid editing — answer the newest event; consider whether your flow re-runs old executions. |
403 window_expired | You're answering an old event (e.g. from a retried run). Only answer fresh deliveries. |
| Nothing arrives at the flow | Delivery log: GET /api/v1/events?type=pricing.calculated shows status + last error per delivery; redeliver with POST /api/v1/events/{id}/redeliver. |
| Wrong/missing items in your logic | Log {{ trigger['body'] }} once and inspect basePricing.lineItems — the array you return REPLACES the whole breakdown. |