---
title: "get forecast accuracy"
method: GET
path: "/api/v1/inventory/forecast-accuracy"
tags: ["inventory"]
---

# get forecast accuracy

`GET /api/v1/inventory/forecast-accuracy`

## Request

- Base URL: `https://api.app.tightly.io`
- URL: `https://api.app.tightly.io/api/v1/inventory/forecast-accuracy`
- Auth: HTTP bearer

What the weekly forecast-accuracy job measured, read back: a `trailing` window and a `latest` week, each carrying wmape, wmase and the population they were scored over, and a `weeks` series behind them. Nothing is recomputed, so the figures are the ones measured at the time.

Every headline figure is measured on the organisation's managed direct-to-consumer book and says so in `population` and `population_label`. There is no sell-in forecast to score, so an organisation with wholesale volume is not being told about its whole book.

`latest.by_product_type` and `trailing.by_product_type` cut the same measurement by product type and reconcile to the headline. `by_channel` cuts it per active sales channel, each scored on its own series, and does NOT reconcile: absolute error is not additive across channels. A slice with too few selling lines to be evidence carries a `refusal` and null scores. `unattributed_variants` is the rest of the scored population, whose forecast carries no recorded model.

`model_wmase[<method>]` carries each model's error on both denominators. Only `wmase_byhand`, over the by-hand four-week moving average the headline divides by, is on the headline's scale, and it is null on a week with no by-hand denominator.

`weeks` bounds the trailing window, and `product_type` or `sales_channel_id` (one, not both) restricts every figure and echoes the cut in `scope`. A cut no measured week covers answers `refusal` with `trailing` and `latest` null and `weeks` empty.

Scope: `inventory:read`.

## Query parameters

- `weeks` integer
- `start_date` string, date
- `end_date` string, date
- `current_state` boolean
- `product_type` string
- `sales_channel_id` string

## Headers

- `Tightly-Version` string

## Response `200`

The record. `trailing` and `latest` are null, not absent, on a book with no measured weeks, and on a refused slice.

- object
  - `data` object
    - `latest` object, nullable: The most recent measured week, with the detail the series omits
    - `refusal` string, nullable: Why there is no figure for the requested cut, as a sentence to render. Present exactly when `trailing` and `latest` are null because of the cut.
    - `scope` object, nullable: The cut the figures were measured on, when one was requested. Null means the whole measured book.
    - `trailing` object, nullable: The whole window as one figure, sums divided once, never a mean of weekly ratios
    - `weeks` object[]: The measured weeks, oldest first, so a chart plots without reversing
  - `message` object: Response envelope severity, service and description

## Other responses

- `400`: Bad Request. An out-of-range `weeks`, or an unparseable date
- `401`: No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.
- `403`: The key may not reach this operation. `scope_missing` when the key does not hold Stock; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.
- `429`: The key has spent its allowance for this minute. 600 reads and 120 writes a minute, counted per key, refused calls included. `Retry-After` says how long to wait.
- `503`: The database is briefly out of reach, which is what a failover or a full connection pool looks like from outside. Wait the seconds `Retry-After` names and send the request again. A read is safe to repeat. A write is not, because the connection was lost in the middle of it, so read the object back before sending it a second time. Branch on the status rather than on `code`, which is `POSTGRES_UNAVAILABLE` from one of the two seams that raise this and `SERVICE_UNAVAILABLE` from the other.

---

[API](https://skmtc.dev/tightly/apis/tightly-api.md) · [All operations](https://skmtc.dev/tightly/apis/tightly-api/llms.txt) · [OpenAPI document](https://skmtc.dev/tightly/apis/tightly-api/revisions/199f7eb35afb?raw)
