aov-post-purchase-upsell
🤖 MCP
Tools reference

Tool reference

All eleven tools, their inputs, and what comes back. Every tool is read-only.

The same data is available over plain HTTPS — each tool maps to one endpoint of the Store API.

On every result

FieldMeaning
shopDomainThe *.myshopify.com these numbers belong to, stamped from the connection itself so the answer can never name the wrong store
currencyThe store's currency, present wherever money is
appliedWindowOn date-range tools: the start, end and source actually used — source tells you whether you chose the range or it defaulted
appliedFilterOn list tools: scope is shop or filtered, plus whichever of name, placement, status, abTest narrowed it
appliedWindow and appliedFilter exist so a short list or a small number can never be read as the whole store. If Claude tells you "you have 3 offers", the payload it read said whether that was 3 in total or 3 matching a filter.

An offer row in any list carries: id, name, status, type (the placement), abTest, a url that opens the offer in your Shopify admin, and — where they apply — revenue, claims, acceptanceRate, priority, createdAt, isDeleted.

Offers

search_offers

Find or list offers by name, placement, or status.

InputTypeNotes
querystringMatches against the offer name
statusenumactive, scheduled, expired
typeenumOne of the four placements
pageinteger20 per page, 1-based. An empty page means the end
sortenumnewest (default), oldest, revenue, revenue_low
abTestbooleantrue returns only offers running an A/B test

Returns offers, total, hasMore, currency, appliedFilter.

revenue_low is the way to reach offers that never earned — they are absent from revenue rankings entirely.

list_offers_by_priority

One placement's offers in display order, 1 shown first.

InputTypeNotes
placementenumRequired. Priority restarts per placement, so there is no shop-wide order
pageinteger20 per page, 1-based

Returns offers (each with priority), total, hasMore, currency, appliedFilter.

count_offers

Offer counts per placement, broken down by status.

InputTypeNotes
typeenumOne placement. Omit to count all four plus a grand total

Returns total and byType — one row per placement with count and a byStatus breakdown of Active, Scheduled and Expired.

get_offer_detail

One offer's setup: who sees it and when.

InputTypeNotes
idstringRequired. The offer id, or the resource uri returned by search_offers

Returns offerName, status, type, startDate and endDate (only when a schedule is set), triggerCriteria (no-condition or set-conditions), triggerConditions, the offer-level discountType / discountValue when one applies, plus the offer's own stats and url.

⚠️
Trigger conditions must all be met together, and the products, collections and tags they name are the trigger — never what is being upsold. A trigger resource that has since been deleted from Shopify is marked as such rather than dropped.

What the offer sells is not here — that is get_offer_stages.

get_offer_stages

What an offer offers, stage by stage.

InputTypeNotes
idstringRequired. Offer id or resource uri

Returns offerName, currency, and stages. Each stage carries:

  • products — each with its own discountType and discountValue, stated even when there is none
  • bundleDealtype, discountType, and tiers, when a bundle is live
  • abTest — when a stage is being tested, it replaces that stage's products and bundle

Only Post-purchase upsell runs a multi-stage funnel; each of its stages names the one it follows and whether it was accepted or declined. The other three placements have exactly one stage.

A discount is not a price. These payloads carry discounts, never final prices.

Results

get_offer_summary

One shop-wide roll-up for a date range, matching the Analytics dashboard.

InputTypeNotes
startdateYYYY-MM-DD, inclusive
enddateYYYY-MM-DD, inclusive

Returns totalUpsoldValue, totalUpsoldOrders, totalOrders, upsellSuccessRate, averageUpsoldValue, offerViews, currency, appliedWindow.

upsellSuccessRate is upsold orders over total orders — every order the store took, not only those that saw an offer.

No per-placement breakdown; that is get_offer_stats.

get_offer_stats

Impressions, revenue and claims for a date range, one row per placement.

InputTypeNotes
startdateYYYY-MM-DD, inclusive
enddateYYYY-MM-DD, inclusive
typeenumOne placement. Omit for all four

Returns placements (each with placement, impressions, claims, and revenue), shopOfferCount, currency, appliedWindow.

⚠️
These rows are separate measurements, not parts of a total. Post-purchase counts impressions and claims on its first upsell only, while its revenue spans every slot — the columns do not share a denominator. Coupon offers omit revenue entirely, because they reward a future order the app cannot attribute.

shopOfferCount is there so an all-zero result can be told apart from a store that has never created an offer.

get_top_offers

Offers ranked by revenue earned in a date range.

InputTypeNotes
startdateYYYY-MM-DD, inclusive
enddateYYYY-MM-DD, inclusive
orderenumhighest (default) or lowest

Returns offers — each with revenue, claims and acceptanceRate — plus shopOfferCount, currency, appliedWindow.

There is no row cap here: the ranking comes back three deep, and Claude cannot ask for more. The Store API endpoint takes a limit for that.

The ranking is built from claim history, so:

  • an offer that earned nothing in the period is absent, not last — reach those with search_offers and sort: revenue_low
  • a top earner can be an offer already deleted, marked isDeleted and carrying no link
  • claims and acceptanceRate are facts about that one row, not a second ranking

get_shop_lifetime_stats

All-time totals, matching the Statistics card on Home. No inputs.

Returns totalRevenue, offerViews, upsoldOrders, conversionRate, revenuePerVisit, currency.

Here conversionRate is measured against offer views — unlike the date-range success rate above.

⚠️
These are all-time figures. They are never compared with, or subtracted from, a dated result, and never presented as "this month".

get_offer_coverage

Which placements have an active offer and which do not. No inputs at all.

Returns placements — each with placement, hasActive, activeCount, totalCount, and recentRevenue — plus currency, appliedWindow.

The counts are current-state, so there is nothing to date. recentRevenue is the one windowed column, and it runs the default last 30 days — appliedWindow says so. Ask for coverage "last quarter" and you will get the same answer as today's, dated the last 30 days. The Store API endpoint does take start and end, deliberately: a script knows what window it asked for.

totalCount alongside activeCount is what separates "nothing created here" from "offers exist but none are active". Coupon offers omit recentRevenue.

Products

recommend_products

Upsell suggestions ranked by real bought-together history, padded with same-category best sellers when that history is thin.

InputTypeNotes
offerIdstringPreferred. The app reads that offer's own trigger products, so nothing has to be named
productIdstringA Shopify product gid
productNamestringA product title, resolved on the server

Pass none of them and you get the store's best sellers.

The reply takes one of four shapes, each naming its own cause in fields rather than by absence:

ShapeMeaning
triggers[] with boughtTogether and sameCategoryPicksThe normal answer. boughtTogether rows carry coPurchasedOrders and pastRevenue; sameCategoryPicks are padding and carry neither
shopBestSellers with bestSellersOnly: trueNo usable product trigger, or nothing named — a real answer, not an error
candidatesThe product title was ambiguous; pick one and ask again
holdsUpsellProducts: falseA Coupon offer has no product slot to fill
An empty boughtTogether list is a real answer — this store has no co-purchase history for that product yet. It never silently becomes a best-seller list.

Related

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