Public API reference

Premium inventory and forecasting APIs

Use the inventory optimization and reinforcement-learning forecasting endpoints with a 30-day Bearer token. This page documents the current contracts, entitlement behavior, and usage signals.

At a glance

These endpoints support inventory planning and demand forecasting. Inventory optimization and prediction can be called without authentication; the learning loop is available only to accounts with an active paid entitlement.

MethodPathAccess mode
POST/api/tools/inventory-optimizationOptional auth; free limit or active-paid limit
POST/api/forecasting/predictOptional auth; free or Premium response
POST/api/forecasting/feedbackBearer auth + active entitlement required
GET/api/forecasting/accuracy?account_key=...Public read endpoint
GET/api/forecasting/platform-statsPublic aggregate endpoint

Authentication

Obtain a 30-day JWT by posting JSON credentials to POST /api/auth/login. Use the returned token on authenticated API calls as Authorization: Bearer <token>.

Login request
{
  "email": "ops@example.com",
  "password": "your-password"
}
Successful login response
{
  "success": true,
  "token": "eyJ...30-day-jwt...",
  "user": {
    "id": 42,
    "name": "Operations Lead",
    "email": "ops@example.com",
    "company_name": "Example Distribution"
  }
}

The API reads the current users.plan_tier and users.subscription_status from the database. Do not rely on stale plan claims stored in a client-side token. An unverified login attempt returns 403 with code: "EMAIL_NOT_VERIFIED" and message: "Please verify your email before logging in.".

Browser fetch
const login = await fetch('/api/auth/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: 'ops@example.com', password: 'your-password' })
});
const { token } = await login.json();

const forecast = await fetch('/api/forecasting/predict', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify({
    account_key: 'example-distribution',
    historical_demand: [820, 860, 910, 940],
    product_name: 'Widget A',
    industry: 'industrial'
  })
});
console.log(await forecast.json());
Node.js 18+ fetch
const response = await fetch('https://supplychainstack.ai/api/forecasting/platform-stats', {
  headers: { 'User-Agent': 'Mozilla/5.0 SupplyChainStack-api-client' }
});
console.log(await response.json());

Inventory optimization

POST /api/tools/inventory-optimization

Send company_name, an array of skus, and optionally service_level (the route defaults to 95). SKU records may include the planning inputs shown below.

Request
{
  "company_name": "Example Distribution",
  "service_level": 95,
  "skus": [
    {
      "name": "Widget A",
      "sku": "WID-A",
      "category": "industrial",
      "current_stock": 420,
      "avg_monthly_demand": 300,
      "unit_cost": 12.5,
      "selling_price": 24,
      "lead_time": 21,
      "supplier_reliability": "mostly_reliable",
      "monthly_sales": [280, 310, 295, 315]
    }
  ]
}

Unauthenticated and free requests support up to 50 SKUs. An active paid request supports up to 500 SKUs. An over-limit free request returns 402 with gate: true; a paid request above 500 returns 400 with message: "Maximum 500 SKUs per analysis.".

Free over-limit response (51 SKUs)
{
  "success": false,
  "gate": true,
  "message": "Free Explorer plan supports up to 50 SKUs. You submitted 51 SKUs.",
  "upgrade_url": "/api/checkout/professional",
  "upgrade_message": "Upgrade to Professional ($149/mo) for unlimited access."
}
Paid over-limit response (501 SKUs)
{
  "success": false,
  "message": "Maximum 500 SKUs per analysis."
}
Successful response groups
{
  "success": true,
  "company_name": "Example Distribution",
  "service_level": 95,
  "analysis_date": "2026-09-02T12:00:00.000Z",
  "portfolio": {
    "health_score": 86,
    "total_skus": 1,
    "total_inventory_value": 5250,
    "total_monthly_carrying_cost": 122.5,
    "total_annual_carrying_cost": 1470,
    "dead_stock_value": 0,
    "dead_stock_pct": 0,
    "excess_capital": 0,
    "inventory_turns": 6.9,
    "status_summary": { "optimal": 1, "overstocked": 0, "reorder_now": 0, "dead_stock": 0 }
  },
  "skus": [{
    "name": "Widget A", "sku": "WID-A", "category": "industrial",
    "current_stock": 420, "avg_monthly_demand": 300, "unit_cost": 12.5,
    "selling_price": 24, "lead_time": 21, "supplier_reliability": "mostly_reliable",
    "safety_stock": 46, "reorder_point": 256, "eoq": 155,
    "days_of_supply": 42, "monthly_carrying_cost": 122.5,
    "inventory_value": 5250, "excess_value": 0, "status": "optimal",
    "status_emoji": "✅", "days_until_reorder": 16, "optimal_stock": 334,
    "ai_insight": { "name": "Widget A", "key_insight": "Inventory levels are within optimal range", "recommendation": "Maintain current reorder cadence" }
  }],
  "dead_stock_analysis": { "skus": [], "total_value": 0, "strategy": "No significant dead stock." },
  "cash_flow": { "current_monthly_carry": 122.5, "optimized_monthly_carry": 85.75, "monthly_savings": 36.75, "capital_freed": 0, "insight": "Optimize inventory to free working capital." },
  "reorder_calendar": [{ "sku": "Widget A", "order_date": "2026-09-18", "days_from_now": 16, "order_quantity": 155, "urgency": "upcoming" }],
  "benchmark_comparison": { "category": "Industrial & MRO", "your_turns": 6.9, "industry_turns": 6.2, "turns_gap_pct": 11, "your_dos": 42, "industry_dos": 59, "your_dead_pct": 0, "industry_dead_pct": 5.8, "interpretation": "Inventory is turning efficiently." },
  "ai_analysis": {
    "executive_summary": "Portfolio health score: 86/100.",
    "top_priority_action": "Maintain calculated reorder points.",
    "dead_stock_strategy": "No significant dead stock.",
    "cash_flow_insight": "Current carrying costs can be reduced by right-sizing.",
    "reorder_narrative": "No immediate reorder actions required.",
    "sku_insights": [{ "name": "Widget A", "key_insight": "Inventory levels are within optimal range", "recommendation": "Maintain current reorder cadence" }],
    "supplier_dependency_risk": "Review backup suppliers for frequently-late vendors.",
    "benchmark_interpretation": "Inventory turns compare favorably with the industry average.",
    "optimization_roadmap": [{ "week": "Week 1-2", "action": "Review reorder points", "expected_impact": "Prevent stockouts" }],
    "automated_monitoring_checklist": ["Weekly: Check SKUs at or below reorder point"]
  },
  "disclaimer": "AI-generated recommendations for informational purposes only. Not financial advice. Verify with your operations team. <a href=\"/ai-disclaimer\">Learn more</a>"
}

Demand prediction

POST /api/forecasting/predict

Authentication is optional. Send a string account_key, at least three oldest-first historical_demand values, and optionally horizon (default 3, capped at 12), product_name, and industry. The submitted tier is not trusted; the route derives the effective tier from the current account entitlement.

Request
{
  "account_key": "example-distribution-widget-a",
  "historical_demand": [820, 860, 910, 940],
  "horizon": 3,
  "product_name": "Widget A",
  "industry": "industrial"
}

Without authentication, or for a non-Premium caller, the response is the free shape:

{
  "success": true,
  "tier": "free",
  "account_key": "example-distribution-widget-a",
  "forecasts": [
    { "h": 1, "point": 952, "low80": 923, "high80": 981, "low95": 907, "high95": 997 },
    { "h": 2, "point": 964, "low80": 922, "high80": 1006, "low95": 899, "high95": 1029 },
    { "h": 3, "point": 976, "low80": 922, "high80": 1030, "low95": 894, "high95": 1058 }
  ],
  "model_info": { "alpha": 0.3, "beta": 0.1, "gamma": 0.1, "total_cycles": 0, "avg_accuracy_pct": null, "accuracy_trend": [] },
  "upsell": {
    "message": "Upgrade to Pro to unlock the learning loop — your forecasts improve each cycle.",
    "accuracy_potential": "60% → 90%+ over 12 months"
  }
}

An active paid entitlement receives the Premium learning-loop shape. It has the same forecast entry fields, but model_info reflects learned parameters and there is no free-tier upsell object.

{
  "success": true,
  "tier": "premium",
  "account_key": "example-distribution-widget-a",
  "forecasts": [{ "h": 1, "point": 952, "low80": 923, "high80": 981, "low95": 907, "high95": 997 }],
  "model_info": { "alpha": 0.312, "beta": 0.104, "gamma": 0.098, "total_cycles": 7, "avg_accuracy_pct": 88.4, "accuracy_trend": [87.1, 88.4] }
}

Forecast feedback

POST /api/forecasting/feedback

This endpoint requires a Bearer token and an active entitlement. Send account_key, numeric predicted, numeric positive actual, and optionally forecast_period and product_name.

Request
{
  "account_key": "example-distribution-widget-a",
  "predicted": 952,
  "actual": 970,
  "forecast_period": "30d",
  "product_name": "Widget A"
}
Successful response
{
  "success": true,
  "mape": 1.9,
  "accuracy_pct": 98.1,
  "reward": 0.981,
  "model_updated": {
    "before": { "alpha": 0.3, "beta": 0.1, "gamma": 0.1 },
    "after": { "alpha": 0.301, "beta": 0.101, "gamma": 0.1 }
  },
  "total_cycles": 8,
  "avg_accuracy_pct": 89.6,
  "accuracy_history": [{ "cycle": 8, "mape": 1.9, "accuracy_pct": 98.1, "reward": 0.981, "timestamp": "2026-09-02T12:00:00.000Z" }]
}

Accuracy and platform stats

GET /api/forecasting/accuracy?account_key=...

This is a public read endpoint. It returns a no-model response when the account has not created a Premium model:

{
  "success": true,
  "account_key": "new-account",
  "has_model": false,
  "total_cycles": 0,
  "avg_accuracy_pct": null,
  "accuracy_history": [],
  "model_params": null
}

When a model exists, the response adds the Premium tier, improvement, model parameters, and platform context:

{
  "success": true,
  "account_key": "example-distribution-widget-a",
  "has_model": true,
  "tier": "premium",
  "total_cycles": 8,
  "avg_accuracy_pct": 89.6,
  "improvement_pct": 4.2,
  "accuracy_history": [],
  "model_params": { "alpha": 0.301, "beta": 0.101, "gamma": 0.1 },
  "platform_avg_accuracy": 84.7,
  "platform_total_models": 42,
  "platform_total_cycles": 318
}

GET /api/forecasting/platform-stats

This public endpoint returns aggregate platform context:

{
  "success": true,
  "total_models": 42,
  "avg_accuracy_pct": 84.7,
  "avg_mape": 15.3,
  "models_above_80_pct": 29,
  "total_feedback_cycles": 318,
  "stat_date": "2026-09-02"
}

Entitlement and status matrix

SituationResult
feedback with missing or invalid Bearer auth401 JSON: {"success":false,"message":"Authentication required"} for missing auth; an invalid token returns {"success":false,"message":"Invalid or expired token"}.
feedback with an authenticated Explorer or inactive user403 JSON: {"success":false,"message":"Active Premium subscription required"}.
predict without auth or with a non-Premium entitlement200 free forecast shape with tier: "free" and upsell.
predict with an active paid entitlement200 Premium learning-loop shape with tier: "premium".
Inventory optimization without auth or over 50 SKUs on free accessOptional auth falls back to free access; over the free limit returns 402 with gate: true, upgrade_url, and the route’s upgrade message.
Inventory optimization with active paid access over 500 SKUs400 JSON with message: "Maximum 500 SKUs per analysis.".

These JSON responses are different from the server-level scanner response. Requests whose User-Agent matches the security scanner blocker are stopped before routing with plain-text 403 Forbidden. Use a normal browser-like User-Agent for manual requests; the examples above use fetch instead of assuming raw curl will pass that middleware.

Usage expectations

There is no endpoint-specific rate limiter on these documented routes. A successful authenticated predict records one forecast_generated credit asynchronously. Feedback and inventory optimization do not consume credits.

For server-returned account state, authenticated callers can use GET /api/billing/status and GET /api/billing/usage. Do not promise a fixed Premium monthly allowance: the current TIER_CREDITS map has no premium entry.

Next steps

See the Premium page for entitlement details, pricing for current plan options, or try the demand forecaster in the browser.

AI-generated planning output: Forecasts and inventory recommendations are estimates for informational purposes. Validate results against your operational data and decisions with your team; they are not financial advice or a guarantee of future performance.