WinFactor Docs

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

  1. Settings → Integrations → Live pricing events: toggle on.
  2. 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:

FieldPath
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:

SettingValue
MethodPUT
URL{{ trigger['body']['data']['callback']['url'] }}
HeadersContent-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

  1. Publish the flow.
  2. 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.
  3. Submit the configuration: the quote form's auto-fill now uses your items, and submission.created reports data.pricing.source: "external".

Troubleshooting

SymptomCheck
Configurator always falls back to base priceFlow 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 runsNormal during rapid editing — answer the newest event; consider whether your flow re-runs old executions.
403 window_expiredYou're answering an old event (e.g. from a retried run). Only answer fresh deliveries.
Nothing arrives at the flowDelivery 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 logicLog {{ trigger['body'] }} once and inspect basePricing.lineItems — the array you return REPLACES the whole breakdown.

On this page