Endpoints
Six endpoints, all GET, all relative to https://aov-cart-drawer.firebaseapp.com/store-api/v1.
Each runs the same handler as the MCP tool beside it, so the two surfaces cannot drift apart. For what the returned fields mean, the MCP tool guide is the longer explanation; this page is the HTTP detail.
| Endpoint | Query | Answers |
|---|---|---|
/setup-health | — | Is the drawer switched on and working? |
/drawer-settings | — | Which features are on? |
/analytics | start end | Revenue, orders, AOV, top upsell products |
/templates | — | Saved templates, and which is live |
/translations | template locale | Storefront wording |
/plan | — | Subscription and drawer-attributed orders |
GET /store-api/v1 itself returns this list.
/setup-health
Query: none
Returns: normal (true when nothing is wrong), the drawer's status, the live template, total
impressions, and issues — each with a code and its fix.
Issue codes: APP_EMBED_DISABLED, THEME_NOT_OS2, CART_DRAWER_DISABLED,
APP_BLOCK_CART_MISSING, APP_BLOCK_PRODUCT_MISSING, NO_SETTINGS, NO_UPSELL_SETTINGS,
NO_TRANSLATION, NO_IMPRESSIONS.
NO_IMPRESSIONS is not a fault. It means the drawer has never been shown to a shopper — a store
can be configured perfectly and still report it. Do not alert on it the way you would on the others.
/drawer-settings
Query: none
Returns: cartDrawerEnabled, then one flag per feature — upsellEnabled,
rewardBarEnabled, announcementEnabled (with announcementType), giftWrapEnabled,
shippingProtectionEnabled (with shippingProtectionPrice) — plus template,
primaryColor, fontFamily and currency.
Then a detail block per feature: rewardBar (goal type and each tier's reward, title and
threshold), upsell, giftWrap and shippingProtection.
upsell.defaultRule lists the slots in the merchant's own order, with slotCount and a
slotsBySource tally. Each slot carries a source to match on and the label shown in the Cart
Editor — they are not the same word.
Only a specific-product slot names a product. The other sources pick theirs at render time from
the shopper's cart, so there is nothing saved to list. variantSelection: "all" means every
variant, not zero.
Product titles and handles are what was recorded when the merchant picked them, not a live Shopify
lookup. A store never saved through the current Cart Editor returns slotCount: 0 with a note.
/analytics
Query:
| Name | Type | Notes |
|---|---|---|
start | YYYY-MM-DD | Store timezone. Defaults to 30 days ago |
end | YYYY-MM-DD | Store timezone. Defaults to today |
Returns: dateRange, currency, totalOrders, attributedRevenue, aov,
abandonedCartRate, revenueByFeature and topUpsellProducts.
revenueByFeature breaks the total down into rewardsBar, upsellProducts, giftWrap and
shippingProtection. It is the field worth building a dashboard on — the total says the drawer
earned, the breakdown says what to change.
Two rules are enforced and return 400:
- A window that runs backwards. A reversed range used to return zero orders, which reads as "this store sold nothing" rather than "you asked backwards".
- A window longer than 400 days.
The date format is not currently checked on this surface. A value that is not a real calendar
day — 2026-02-31, or anything that is not a date at all — is passed through rather than refused,
and you get a window you did not ask for. Validate dates on your side, and always compare
dateRange in the response against what you sent.
/templates
Query: none
Returns: every template saved on this store, and which one is live.
Useful before switching: wording and settings do not carry across templates, so comparing them first is the check worth doing.
/translations
Query:
| Name | Notes |
|---|---|
template | Defaults to the template currently live |
locale | One language code, e.g. fr. Omit for all |
Returns: the template, the locales it has wording for, and two layers of wording:
text— the wording saved on the Design screen. This is what buyers see, and what any language without a translation of its own falls back to. Keys are the drawer's own field paths, such ascontentSettings.footer.checkoutButtonLabel.translations— the per-language overrides added on the Translations screen.
Storage bookkeeping — internal ids, and the second copy of every source string the admin keeps for change detection — is stripped before it leaves the server.
A store's own primary language usually has no entry in translations, because there was
nothing to translate it from. That does not mean buyers see nothing: they see text. Only when
both layers are empty does the response come back with found: false, and it says so in words.
On a multi-language store the unfiltered response regularly exceeds the 16,000 character cap.
template will not narrow it — the result is already one template — so pass locale. It is the
axis the payload actually grows along.
/plan
Query: none
Returns: subscription (isPaid, pricingVersion, installedAt) and usage with
ordersAttributedToDrawer.
ordersAttributedToDrawer counts orders this app recorded as going through the cart drawer,
since install. It is not the store's total order count and will not reconcile against Shopify's order
numbers.
This endpoint reports your own store's situation. It does not list plans or quote prices.