Shelf
The shelf is the home screen of the app as an API: every product Skoup tracks for a brand on a market — every establishment for a local brand, every offer for a SaaS — with how the AI assistants treat it, already in the order to look at it. One call answers « which of my products is in trouble in the AI answers, and why? ».
A line is not a record of its own: its id is the product’s (prod_…) or the establishment’s (loc_…), so you can pass it to GET /v1/brands/{brand}/products/{product} or to the product’s perception. The shelf writes nothing.
Tracked = a product linked to at least one query of the market — cited in an answer, or close to a query by its category — cited or not. An active product linked to no query is not on the shelf (the app shows it as « not tracked » in the catalogue). During the trial, only the trial selection is tracked.
I want to…
| I want to… | Call |
|---|---|
| The shelf of a market, in order | GET /v1/brands/{brand}/shelf?market=FR |
| Only the products an assistant gets wrong | GET /v1/brands/{brand}/shelf?q=is:error |
| The products no answer names | GET /v1/brands/{brand}/shelf?q=is:absent |
| The products that sell through the assistants | GET /v1/brands/{brand}/shelf?q=has:revenue |
| Where a competitor stands in front of you | GET /v1/brands/{brand}/shelf?q=leader:"canyon" |
| What to fix on one product | GET /v1/brands/{brand}/inbox?q=product:"Strade 7" |
Scope: metrics:read (the shelf is a measure, not the catalogue). An assistant reads the same shelf with the MCP tool get_shelf.
The states
Skoup judges the state of each line — the same judge as the app, never a client-side rule. In the order they are listed:
errorreinforcegoodabsentunmeasuredstate | When | state_reason.kind |
|---|---|---|
error | An open alert about it (wrong price, wrong stock, invented spec, wrong address / phone / hours of an establishment), or a perception verdict judged false (a spec, the price…) | alert (with alert_kind) · perception_false |
absent | Its queries were asked over the period, no answer named it | never_cited (count = its queries) |
reinforce | Cited, but a differentiator of its sheet is missing from at least half of the answers judged about it, or its perception score is under 60 | usp_absent (label = the differentiator) · weak_perception (count = the score) |
unmeasured | No answer on its queries over the period yet | null |
good | None of the above | null |
state_reason.model is the assistant that said it most, element what is at fault (price, availability, spec, attribute, address…).
Order
Lines come sorted: what an assistant gets wrong, then what to reinforce, then the products the assistants cite; never-cited and never-measured lines come last. Within a state, the AI revenue of the period, then the share.
Search
q takes the app’s search grammar:
| Qualifier | Values |
|---|---|
is: | error, absent, reinforce, good, unmeasured |
category: | part of the category (the city of an establishment), e.g. category:"gravel" |
model: | cited at least once by chatgpt, claude, gemini, perplexity… |
leader: | part of the name or brand of the competitor cited first |
has: | revenue — an AI revenue over the period |
- negates a qualifier (-is:good); anything else is free text on the title or the SKU.
Scenario: a weekly digest of the products in trouble
curl -G "https://api.skoup.ai/v1/brands/br_034PJIMnN3EOTCU3TMUdLS/shelf" \
--data-urlencode "market=FR" \
--data-urlencode "q=is:error" \
-H "Authorization: Bearer skoup_test_4eC39HqLyjWDarjtT1zdp7dc"Run it after the weekly sampling.completed: each line says what is wrong (state_reason) and which assistant says it.
Pitfalls
- No event. The shelf is computed when you call it. Follow the objects behind it:
sampling.completedfor the measure,alert.createdfor an error. nullis never 0.shareisnullfor a line never measured;perception_scoreisnullbefore enough judged answers;ai_revenueisnullwithout store orders (or before the first order). Once orders flow, a product that sold nothing through an assistant hasamount: 0.deltacompares the last two weekly cycles,sharecovers the whole period: they are not the same window.by_modellists the measured models only. A model with no answer on the line’s queries is absent, never a row of zeros;trendhas 12 weeks, oldest first, an emptyby_modelfor a week without sampling.- Before the first completed sampling of the market, the list is empty.
- Pagination is a cursor in the shelf’s order:
limit(1–100, default 20) andstarting_after= theidof the last line of the previous page. A new sampling reorders the shelf: start over rather than resume an old cursor.