aov-bundle-upsell
🔌 Store API
Endpoints

Endpoints

Every path below is relative to your store's base URL:

https://avada-bundle-upsell.firebaseapp.com/public-api/shop/YOUR-STORE.myshopify.com

All are GET. Anything else is refused — see Errors.

Everything a token can call

EndpointAnswers
/statusThe store record — plan, install date, revenue used this cycle
/subscriptionPlan and app plan, and nothing else
/eligibilityWhether the store is on a paid plan
/offersList and search offers
/offer/:idOne offer, in full
/offers/countOffer counts by type
/analyticsRevenue, orders and conversion, with a comparison period
/settingsThe store's widget settings
/diagnosticWhy the widget is or is not live — the one that reaches a verdict
/app-statusTheme embed state, per offer-type block
/themesThe store's themes
/themes/checkEmbed and theme support, in one small response
/languagesPublished storefront languages
/discountsActive app discounts in Shopify
/installed-appsWhich other AOV.ai apps are on the store

/diagnostic

Start here. It reaches a conclusion rather than handing back raw data, and it is the same data the get_setup_status MCP tool returns.

Returns: the store's identity and install date, the theme embed state, whether settings have been saved, offer counts by type and status, and issues — a list of codes in the order worth acting on.

CodeMeans
APP_EMBED_DISABLEDThe app embed is off on the live theme
NO_BLOCK_ADDEDThe theme supports app blocks and none of ours is placed
NO_ACTIVE_OFFERSNo offer is currently active
THEME_NOT_OS2The live theme does not support app blocks
NO_SETTINGSNo widget settings have been saved

The order is the point. Act on issues[0] and re-check; the rest are usually consequences of it. A monitor that alerts on every code will page you five times for one cause.

This endpoint calls Shopify to inspect the live theme, so it is slower than the rest. Poll it on the order of minutes, not seconds.


/offers

Query parameters:

NameNotes
nameSearch text. Present and non-empty switches to search mode
typeOne offer type — see below
statusactive, scheduled, expired, draft, all
pageDefaults to 1
limitDefaults to 20

Offer types: fbt, volume-discount, mix-match-bundle, bundle-builder, product-fixed-bundle, upsell. The last is what the app calls Product Recommendation.

Returns: {offers: [...], pagination: {...}}.

⚠️

The default limit here is 20; the MCP tool's is 10. The two surfaces do not share a default, so a script and an assistant asked the same question can report different counts. Always send an explicit limit when the total matters.


/offer/:id

Returns: the full offer record — every field, not the trimmed shape the MCP tool returns.

⚠️

An unknown id answers 200 with success: true and no data, not a 404. Check for data before using it.


/offers/count

Returns: a count per type plus total:

{"success": true, "data": {
  "fbt": 3, "volume-discount": 12, "mix-match-bundle": 0,
  "bundle-builder": 1, "product-fixed-bundle": 0, "upsell": 4,
  "total": 20
}}

Cheaper than listing offers to count them, and the right call for a dashboard tile.

On an internal failure this endpoint answers success: false with data: {total: 0}. A client that reads data.total without checking success will render a confident zero. Check success.


/analytics

Query parameters:

NameNotes
from / toYYYY-MM-DD. Preferred
start / endAccepted as aliases for the same thing
offerIdNarrow to one offer
timezoneDefaults to Asia/Bangkok
firstRequesttrue also returns the offer list, for populating a filter

Returns: additionalRevenue, bundleOrders, totalOrders, avgExtraRevenue, conversion, ordersByType, and the range actually used.

Each metric comes back with a comparison against the immediately preceding period of equal length, so a 30-day window is compared against the 30 days before it. That comparison is computed whether you want it or not — it is not something you request.

⚠️

Send timezone explicitly. The default is Asia/Bangkok, which is almost certainly not your store's. A day boundary in the wrong timezone silently shifts revenue between days, and nothing in the response flags it.

An invalid range answers success: false with Invalid date range. A valid-looking but impossible date (2026-02-31) may be accepted and normalised instead — compare the range in the response against what you sent.


/status

Returns: the store record, restricted to a fixed allowlist: shopifyDomain, name, plan, appPlan, appStatus, isInstalled, shopifyPlan, shopifyPlanName, shopCountry, installedAt, createdAt, updatedAt, pricingVersion, pricingAdditionalRevenue, pricingResetDate, plus the store's feature flags.

pricingAdditionalRevenue and pricingResetDate are the useful pair: revenue used this billing cycle, and when it resets. Both are USD.

Feature flags are always present, defaulting to false, even on a store where they have never been set. You can test === false safely.


/subscription

Returns: shopId, domain, plan, appPlan. Nothing else.

Use it when a plan check is all you need — /status returns far more for the same cost.


/eligibility

Returns: shopDomain, isPaid, and a features map.

The features map is a capability list that today reports a single always-true entry. Treat it as forward-looking: read a named key if you need one, do not assume the set is stable.


/settings

Returns: the store's saved widget settings, or {} on a store that has never saved any.

An empty object here is exactly what NO_SETTINGS in /diagnostic is reporting.


/app-status

Returns: embedEnabled, isSupportThemeOS, themeId, and blockStatuses — a flag per offer type saying whether that type's app block has been placed in the theme.

This is the endpoint that answers "is the Volume Discount block actually on the page", per type, which /diagnostic only summarises.

Calls Shopify. Slower than the Firestore-backed endpoints.


/themes

Returns: every theme on the store, as id, name and role. The live one has role: "main".

Read-only, and deliberately so: nothing on this API can write to a theme. Enabling the app embed happens in the merchant's own theme editor.


/themes/check

Returns: embedEnabled, isSupportThemeOS, themeId.

The small, fast version of /app-status when you only need to know whether the embed is on.


/languages

Returns: the store's published storefront languages, each with locale, name, primary and published.


/discounts

Returns: up to 50 active app discounts from Shopify — id, title, status, type (DiscountCodeApp or DiscountAutomaticApp) and code where there is one.

Only active discounts, and only the first 50. There is no pagination, so on a store with many discounts this is a sample, not an inventory.


/installed-apps

Returns: which other AOV.ai apps are installed on the store. Useful for deciding whether a cross-app integration is available before offering it.


Not available to a merchant token

Some routes under this prefix exist for internal support tooling and answer 403 This endpoint requires an internal API key. Reading theme file contents is among them — /diagnostic and /app-status inspect the theme server-side and return conclusions, which is what an integration actually needs.

Every write route — creating, updating, deleting or toggling offers, and updating settings — is refused for a different reason: the token is read-only regardless of the route.

Related

Product
Install AppWebsiteAvada Apps
Resources
DocumentationFAQPrivacy Policy
Company
Avada GroupContact
© 2026 Avada Group. All rights reserved.