Skip to main content

Build Forecast

POST 

/api/inventory-forecasting/build

Build a replenishment forecast for one or more suppliers and return recommended order quantities per supplier, with a full per-line breakdown of how each number was derived.

Authorization

Any valid API token can call this endpoint — no specific scope required. Manage tokens.

Authentication: Requires Bearer token.

FORECAST TYPES (forecast_type)

  • with_sales_history: derive demand from historical sales velocity over the chosen window. The most common mode.
  • target_stock_level: order up to a fixed target quantity per product (target_quantity), or up to each product's minimum stock level when use_min_stock_level_as_target is true. Velocity is not used to set the target.
  • fill_backorders: order only what is needed to clear outstanding backorders. Baseline and trend settings do not apply.

BASELINE METHODS (baseline_method) - how the daily velocity baseline is derived from sales history (applies to with_sales_history)

  • flat (default): plain mean over the window (total units / days covered). No trend. Legacy behaviour - stable and predictable. Use when demand is roughly level.
  • recency_weighted: weights recent history more heavily than older history, so a recent shift in demand is reflected faster without projecting a hard trend line. Use for choppy demand that has recently stepped up or down. Tune with trend_half_life_days.
  • damped_trend: fits the recent growth (or decline) trend and projects it forward, attenuated by trend_damping_factor so a short surge does not overshoot into over-ordering. Use for a SKU on a sustained growth or decline curve.

DAMPED-TREND CONTROLS

  • trend_damping_factor (0..1, default 0.85): the damping factor phi. Each period further out keeps only phi of the previous period's growth, so the projected trend flattens instead of compounding forever. Lower = more damping (closer to flat mean); 1.0 = undamped.
  • trend_max_growth_pct (>= 0, default 100): caps how far the trend may lift the baseline above the flat mean. 100 means the trend may at most double the flat mean; 0 disables any lift. When the cap binds, the line reports trend_capped: true.
  • trend_half_life_days (>= 1, nullable, default 21): reserved tuning for recency_weighted - the age at which a day's weight halves.

SPARSE-HISTORY FALLBACK A trend needs enough signal to be trustworthy. If a product has fewer than roughly 8 weeks of history or fewer than 8 orders in the window, the trend is skipped and the baseline falls back to the flat mean for that product (trend_factor 1.0), regardless of baseline_method. This stops a couple of early sales from projecting a runaway trend.

ANOMALY CLEANSING (exclude_detected_anomalies, default false) When true, days already resolved as excluded or smoothed anomalies for a product are dropped (excluded) or replaced with their expected value (smoothed) before the baseline is computed. This de-contaminates one-off promo spikes and stockout gaps so they do not distort ordinary velocity. Detect and resolve anomalies first (see the Anomalies folder). Each line reports how many days were affected via inputs.anomaly_days_excluded and inputs.anomaly_days_smoothed.

DEMAND MODIFIERS (demand_modifiers[]) An array of adjustments layered onto the baseline. Each entry has a type:

  • percentage: scale demand by (1 + value/100), e.g. value 10 = +10%.
  • seasonal / event: a bounded uplift or reduction over a date range (start_date, end_date) for a known seasonal peak or one-time event; value is the percent change.
  • override: replace the daily average outright with value.
  • promo_lift: re-apply a previously measured promotion. Set promo_window_id to a promo window whose lift has been measured; the server hydrates that window's live measured lift and applies it as a MULTIPLICATIVE factor on the current baseline. Because it multiplies the CURRENT baseline (not a stored absolute number), the same promo automatically re-scales as the business grows. Send value 0 - it is ignored and hydrated server-side. Optional per-modifier fields: apply_to (all or a scope key), name (label), period_key, start_date, end_date, product_id (limit to one product), promo_window_id (promo_lift only).

FILTERS (product_filters, sales_filters) Both accept a filter_groups tree: a conjunction (and / or) plus a children array of conditions and nested groups. Each condition has column, operator, and value. product_filters narrows which products are forecast; sales_filters narrows which historical orders feed the velocity baseline. A legacy filters.filterSet shape is also accepted for older saved configurations. Operators: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of, greater_than, less_than, between. Example: {"filter_groups": {"conjunction": "and", "children": [{"type": "condition", "condition": {"column": "order_status", "operator": "is_one_of", "value": ["shipped", "fulfilled"]}}]}}

DECOMPOSITION RETURNED PER LINE (inputs object) Each forecast line's inputs object exposes the multiplicative decomposition:

  • base_flat: the flat mean before any trend.
  • trend_factor: the multiplicative trend baked into the baseline (1.0 = none).
  • baseline_method: which method produced the baseline.
  • trend_capped: true if trend_max_growth_pct clamped the trend.
  • anomaly_days_excluded / anomaly_days_smoothed: how many history days were dropped / replaced by anomaly cleansing.
  • promo_factor: the multiplicative promo lift applied (null when no promo_lift modifier applied).
  • promo_window_name: the promo window that supplied the lift (null if none).
  • promo_current_baseline: the baseline the promo factor multiplied. The effective daily_average is base_flat x trend_factor x (promo_factor or 1), then any percentage/event/override demand modifiers.

SCENARIOS (copy a body variation for each)

  1. Flat baseline (default / legacy): omit baseline_method or send "flat". Velocity is the plain window mean; inputs.trend_factor is 1.0 and inputs.base_flat equals daily_average.
  2. Damped-trend for a growing SKU: baseline_method "damped_trend", trend_damping_factor 0.85, trend_max_growth_pct 100. Recent growth is projected forward but damped and capped at 2x the flat mean; inputs.trend_factor rises above 1.0 and inputs.trend_capped reports whether the cap bound.
  3. Recency-weighted: baseline_method "recency_weighted", trend_half_life_days 21. Recent days count more; no hard trend line is projected.
  4. Anomaly-cleansed baseline: exclude_detected_anomalies true (after resolving anomalies as excluded/smoothed). Promo spikes and stockout gaps are removed before the mean is taken; inputs.anomaly_days_excluded / inputs.anomaly_days_smoothed report the count.
  5. Re-apply a past promotion: add a demand_modifiers entry of type "promo_lift" with promo_window_id set to a measured promo window and value 0. The window's live lift multiplies the current baseline, so it auto-scales with growth; inputs.promo_factor, inputs.promo_window_name, and inputs.promo_current_baseline are populated.
  6. Target stock level / fill backorders: forecast_type "target_stock_level" (with target_quantity, or use_min_stock_level_as_target true) orders up to a fixed target; forecast_type "fill_backorders" clears backorders only. Baseline and trend settings do not apply to fill_backorders.

MINIMUMS enforce_minimum_order_value / enforce_minimum_order_quantity: when true, the per-supplier minimums block flags whether the forecast totals fall below the supplier's minimum_purchase_order / minimum_purchase_order_quantity. Display-only here; enforcement is applied at PO creation time (POST /api/inventory-forecasting/purchase-orders).

Each line also carries a confidence object (tier high/medium/low/new, 0-100 score, drivers, reasons, actions), a prediction_interval (point/lower/upper at a service level), and a plain-language rationale.

Returns 422 if too many products are found for any supplier.

Request

Responses

OK

Response Headers
    Content-Type