---
title: "Learn the product from its public site and docs (answers at once; the inventory follows)."
method: POST
path: "/story-projects/{projectId}/feature-inventory"
tags: ["Sources"]
---

# Learn the product from its public site and docs (answers at once; the inventory follows).

`POST /story-projects/{projectId}/feature-inventory`

## Request

- Base URL: `https://docs.sleeperhit.studio/api/v1`
- URL: `https://docs.sleeperhit.studio/api/v1/story-projects/{projectId}/feature-inventory`
- Auth: HTTP bearer

Sleeper reads the product's public site and docs (its pages, llms.txt, sitemap and OpenAPI description, up to 24 pages, obeying robots.txt) and lists its features, who it is for, what it claims and what it costs, each with the page it came from. When the product's app is connected to Sleeper — a sign-in Sleeper has tested — Sleeper also signs in with that connection and walks the app's own screens, honouring any areas you have blacked out. With no connection, nothing but public pages is read: no login, cookie or key is sent for them, and private or reserved addresses are refused. It runs in the background: the answer is the run (`learning`), and the inventory reads `ready` in a minute or two. For a product project only. Free, up to 10 runs a day. `siteUrl` is the product's https address; leave it out to read the last run's site again. `connectionId` names the project's own app connection to walk; leave it out to use its verified one (public pages only when it has none). 202 with the new run; 200 with `alreadyRunning: true` and the run already learning (no second run starts, so no Idempotency-Key). Poll GET until the run is `ready` or `failed`. 400 `validation_failed` for an address that is not https or not public, or for a `connectionId` that is not this project's own connection (field `connectionId`); 409 `project_not_product` for a story project; 403 `usage_limit_reached` past the daily limit.

## Request body

- LearnFromSiteBody
  - `siteUrl` string: The product's public site (https, a public host). Leave out on a refresh: the last run's site is read again.
  - `connectionId` string, nullable: The project's app connection the run signs in with and walks (surrounding whitespace is trimmed); `null` learns the public site only. Leave it out to use the project's verified connection (or its public pages when it has none).

## Response `200`

A run was already learning: this is it (`alreadyRunning: true`).

- FeatureInventoryLearnStarted
  - `projectId` string, required
  - `run` FeatureInventoryRun, required
    - `id` string, required
    - `status` 'learning' | 'ready' | 'failed', required: `learning` until the inventory is saved (`ready`) or the run stops (`failed`, with `error`). A run that stopped answering reads `failed`.
    - `siteUrl` string, required
    - `connectionId` string, nullable, required: The app connection the run signs in and walks the app's screens with; `null` learns the public site only.
    - `startedAt` string, date-time, required
    - `finishedAt` string, date-time, nullable, required
    - `error` string, nullable, required: Why it stopped, in words for the person. Null unless `failed`.
    - `pagesRead` integer, nullable, required
    - `documentVersion` integer, nullable, required: The inventory version this run saved.
    - `changeSummary` object, nullable, required: Which features are new and which are gone since the inventory before, by name.
      - `previousVersion` integer, nullable, required
      - `added` string[], required
      - `removed` string[], required
  - `alreadyRunning` boolean, required: True when a run was already learning: this is that run, and no new one started.

## Other responses

- `202`: The new run, `learning`.
- `400`: Request payload failed validation. The error message names the offending field.
- `401`: Missing, malformed, revoked, or expired API key.
- `403`: API key is valid but lacks the scope, is blocked by IP allowlist, or the account is disabled.
- `404`: The named project or source does not exist or is owned by another account.
- `409`: `project_not_product` — the project is a story project. PATCH its `kind` to `product` first; nothing was started.
- `429`: Rate limit for this API key + IP + route family was exceeded.
- `500`: The API failed unexpectedly. Safe to retry with the same Idempotency-Key.

---

[API](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api.md) · [All operations](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/revisions/4534f5d25540?raw)
