JENNi Runs API
Open, inspect and close runs. Every write belongs to a run, and a run is how JENNi attributes what you sent and (in snapshot mode) what to remove. ### How every ingestion integration is shaped Write your catalog and inventory into the JENNi product graph, then reconcile what JENNi holds against what you sent. ## The shape of every integration Work is always scoped to a **run**. A run is the unit of source attribution, batch idempotency and — in snapshot mode — the full-replace window. 1. `POST /provider/v1/stores` — register each store once. (Bringing a new business onto JENNi? Register it as a **retailer** first — see the Retailers guide — and pass its `retailer_id` here.) 2. `POST /provider/v1/runs` — open a run. 3. Push batches against that run (`/inventory/levels/bulk`, `/products/bulk`, or `/inventory/snapshots`). 4. `POST /provider/v1/runs/{run_id}/close` — close it. 5. `GET /provider/v1/products` / `GET /provider/v1/inventory/levels` — read back what landed. Only an **open** run accepts writes; a batch against a closed, expired or failed run is rejected with `409`. ## Two ingestion modes - **Mode A — incremental** (`mode: "incremental"`, scope `provider.write`). Each batch is a delta. Send what changed. Nothing is removed implicitly. - **Mode B — snapshot** (`mode: "snapshot"`, scope `provider.ingest`). The run carries the COMPLETE set of levels for ONE store. Anything absent from the snapshot is soft-deleted when the run closes. A snapshot run must declare its `store_id` and `expected_items` when it is opened. ## Partial success is normal Both bulk endpoints return a per-item envelope. **`200` means every item was accepted; `207` means at least one was not.** Always walk `results[]` and join on `event_id` — a 2xx status alone does not mean every item landed. An item is `accepted` (durable), `rejected` (permanently bad — do not retry unchanged) or `deferred` (indeterminate upstream failure — retry it). ## Errors Every 4xx and 5xx is RFC-7807 `application/problem+json`. Branch on the stable `type` URI, never on `title` or `detail`. Every response carries `X-Request-Id`; quote it in support requests. ## Tenancy Your tenant and your provider are derived from your API key. There is no tenant parameter anywhere in this API — not in a path, not in a query string, not in a body. A key is scoped to exactly one provider.