Tool guide
14 read tools, available on every connection. 12 more write tools, available only on a Read and write connection. Every one is scoped to the single store its connection belongs to.
For the compact list, see the tools reference.
Read tools
get_setup_status
Purpose: Report whether the bundle widget is actually live on the storefront, and if not, why.
When to use: First, whenever a bundle, a volume table or an upsell is described as "not showing". It reaches a conclusion rather than handing back raw data.
Parameters: none
Returns: the store's name, domain, plan 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.
| Code | Means | Fix |
|---|---|---|
APP_EMBED_DISABLED | The app embed is off on the live theme, so nothing can render | Turn it on in the theme editor, on the live theme |
NO_BLOCK_ADDED | The theme supports app blocks, and none of ours is placed | Add the block where the widget should appear |
NO_ACTIVE_OFFERS | Nothing is currently active, every offer is draft, scheduled or expired | Activate one, or check its schedule |
THEME_NOT_OS2 | The live theme does not support app blocks | Switch to an Online Store 2.0 theme |
NO_SETTINGS | The store has no saved widget settings | Open and save the settings once |
The list is in priority order, fix the first one. The rest are usually noise until it is done.
get_offers
Purpose: List and search this store's offers.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
query | string | No | Searches the offer name |
type | enum | No | One offer type, see accepted values |
status | enum | No | active, scheduled, expired, draft, all |
sort | enum | No | newest or oldest by creation date; newest by default |
limit | number | No | Up to 50. Defaults to 10 |
after | string | No | next_cursor from the previous page; not combined with query |
Returns: each offer's id, name, type, status, discount, additional_revenue_usd,
whether it is limit_reached (held off by the free-plan revenue cap), and next_cursor for
paging.
The default limit is 10, not "all". On a store with fifty offers, "list my offers" answers
with ten of them unless a higher limit is asked for, or the result is paged with after.
get_offer
Purpose: Read the full configuration of one offer. (The original tool was named
get_offer_details; connections made before September 2026 still use that name.)
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
offer_id | string | Yes | The id returned by get_offers |
Returns: id, type, status, admin_url, additional_revenue_usd, the store's timezone,
and fields, every field in exactly the shape update_offer accepts. A Product Fixed Bundle also
returns bundle_status (whether Shopify has finished building the bundle product: processing,
ready or failed). A Mix & Match or Bundle Builder offer also returns
bundle_discount_source: discount_code means the offer owns a Shopify discount code the widget
applies after add-to-cart, bundle_price means the saving is built into the bundle's own price.
This is fixed when the offer is created.
The id has to come from get_offers, it is not the offer's name and not anything visible in the
app's URL bar. An unknown id answers Offer not found rather than guessing.
get_offer_fields
Purpose: The fields one offer type has, as this store allows them, before reading or writing one.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
offer_type | enum | Yes | One of the six offer types |
Returns: each field's kind, allowed values, range, whether it is required, whether the store's plan locks it, and the text keys its translations take.
Read this before update_offer or a create tool's fields, both of which follow it exactly. A
field marked plan-locked can only keep its current value until the store upgrades.
get_offer_timeline
Purpose: The change history the app keeps for one offer.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
offer_id | string | Yes | A Fixed Bundle offer id from get_offers |
after | string | No | next_cursor from the previous page |
Returns: up to 20 events per page, newest first: created, edited, switched on or off, and
product changes Shopify synced in. An event made through an AI connection is marked by: "assistant".
Only Product Fixed Bundle offers keep this timeline. The other five offer types answer
no_timeline.
get_analytics
Purpose: Bundle performance over a date range.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
start | YYYY-MM-DD | No | Defaults to 30 days before end |
end | YYYY-MM-DD | No | Defaults to today |
offer_id | string | No | Narrows the totals to one offer |
Returns:
| Field | What it is |
|---|---|
additional_revenue | Extra revenue attributed to bundles in the range |
bundle_orders | Orders that included a bundle |
total_orders | All orders in the range |
attach_rate | bundle_orders ÷ total_orders, as a decimal |
top_offers | The five offers that earned most, with their revenue and order counts |
currency | Always USD |
Figures are in USD, whatever currency your store sells in. Orders are converted when they are recorded.
Zero and "unavailable" are different answers. If the analytics warehouse cannot be queried,
the tool returns analytics_unavailable rather than zeros, so a temporary outage is never reported
as "you earned nothing".
get_plan_status
Purpose: Report your plan and how much of it you have used this cycle.
Parameters: none
Returns:
| Field | What it is |
|---|---|
plan | The plan this store is on |
additional_revenue_used | Bundle-attributed revenue this billing cycle, in USD |
reset_date | When the cycle rolls over |
free_cap | The cap block, or null on plans with no cap |
When free_cap is present it carries cap, used, is_capped, near_limit (true from 80% of
the cap) and reset_date.
is_capped: true is the answer to "my bundles stopped mid-month". Once a capped plan reaches
its cap, running offers are switched off automatically until reset_date.
get_widget_settings
Purpose: Read the storefront widget design of one offer type, as the merchant sees it on Widget display.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
offer_type | enum | Yes | One of the six offer types |
Returns: every setting with its id, group, kind, limits and current value, colors, font sizes,
texts, layout and display options, plus color_style (basic or advanced), text translations
by locale, and the store's published languages besides the default.
Labels repeat inside a widget (several colors are called "Background"), so an assistant quoting
one back should pair the label with its group. With a Read and write connection, the ids here are
what update_widget_settings takes.
search_products
Purpose: Find products in the Shopify catalogue by name, to use in an offer.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
query | string | Yes | Product name or part of it |
limit | number | No | Capped server-side |
Returns: each product's numeric id and handle, both required when building an offer, plus
has_more. Only active products published to the Online Store are returned; an out-of-stock
product is still returned. A product this app generated for a bundle is excluded.
Boolean operators (AND, OR, NOT) are stripped from the query; every remaining word must match. This never picks for the merchant, it returns matches for the merchant to choose from.
search_collections
Purpose: Find collections in the store by name, to scope an offer to one.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
query | string | Yes | Collection name or part of it |
limit | number | No | Capped server-side |
Returns: each collection's numeric id, handle, and how many products it holds.
get_discounts
Purpose: List the store's active Shopify discounts, including ones this app created for its own offers.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
limit | number | No | Up to 100. Defaults to 50 |
Returns: each discount's details, including whether it combines with product, order and shipping discounts.
Check combinability here before telling a merchant a new bundle discount will stack with a discount they already run.
search_customer_segments
Purpose: Find the store's Shopify customer segments by name, for an offer's audience targeting.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
query | string | No | Part of a segment name; omit to list them all |
after | string | No | next_cursor from the previous page |
Returns: {id, name} pairs, 20 at a time, in the shape an offer's customerSegments and
excludeSegments fields take. Segment names only, no customer data.
get_markets
Purpose: The store's Shopify markets.
Parameters: none
Returns: {id, name} for each market. A Frequently Bought Together or Volume Discount offer's
marketId field takes one of these ids; null shows the offer in every market.
get_product_variants
Purpose: The variants of one product, to scope an offer to specific variants.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
product_id | string | Yes | From search_products |
Returns: each variant's id, title, price, compare_at_price, available, and its
options. A product with more than 250 variants lists only the first 250; total_variants says
how many it really has.
Write tools
Every tool below needs a Read and write connection. A request to any of them on a Read-only connection is refused with "This connection is read-only. Create a new connection with write access to change offers or design."
Nothing is created, changed or deleted until the merchant has approved it. The assistant is instructed to summarize what it is about to send (offer type, products, discounts, or a setting's old value and new value) and wait for a yes before calling any tool in this section. That rule lives in the assistant's own instructions, not in the server, so it's worth reading what an assistant proposes before approving it.
The six create tools
Each of the six create tools takes a name plus the fields specific to that offer type, and any
other setting (advanced settings, texts, translations, schedule) goes in a fields object, in the
same shape update_offer takes (see get_offer_fields). Every one of them:
- Creates an offer the store does not have yet. To change one that already exists, use
update_offer, never delete-and-recreate. - Starts switched off. Turn it on with
set_offer_statusonce the merchant agrees. - Calls once per offer the merchant confirmed, never to "try out" a template or show an example.
| Tool | Offer type | Key parameters |
|---|---|---|
create_fbt_offer | Frequently Bought Together | Trigger as product or collection ids, plus the products to offer alongside it |
create_volume_discount_offer | Volume Discount | scope (all products or specific ids), either tiers with a discount_type, or one of six template presets (classic-volume, buy-x-get-x, volume-gift, mix-match-volume, table-layout, mystery-tier) and a color_scheme |
create_mix_match_offer | Mix & Match Bundle | options, each with a name and at least one product with a quantity |
create_bundle_builder_offer | Bundle Builder | A pool of products or collections (not mixed), layout (list or category), a quantity-ladder discount |
create_fixed_bundle_offer | Product Fixed Bundle | product_ids (up to 30), at most 3 options to choose between, at most 2048 resulting variants |
create_upsell_offer | Product Recommendation | Where it shows (store-wide, products, or collections) and what it recommends |
Volume Discount templates bring their whole ladder. Never re-type a template's tiers by hand,
send the template key and let it fill in the discounts and widget layout. Using a template or
preset also sets the shared Volume widget style (corner radius, spacing, title size), which changes
every Volume offer's widget in the store, not only the new one.
Product Fixed Bundle and Mix & Match offers build a Shopify bundle product in the background.
For a Fixed Bundle, check get_offer's bundle_status is ready before turning it on.
update_offer
Purpose: Change fields of one existing offer, the way the merchant edits it in the app. Works for every offer type, there is no per-type update tool.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
offer_id | string | Yes | From get_offers |
fields | object | Yes | {<field>: value}, in the shapes get_offer returns; see get_offer_fields |
An object field merges key by key; a list field (tiers, options, categories) replaces the whole
list, so changing one item means sending the whole list back with that one item changed. Dates are
store-local time, YYYY-MM-DDTHH:mm. The storefront follows within seconds.
Turning an offer on or off is set_offer_status, not a field here.
set_offer_status
Purpose: Turn 1 to 10 offers on or off.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
offer_ids | array of string | Yes | 1 to 10 ids from get_offers |
status | enum | Yes | active or paused |
The storefront follows within seconds. Turning on is refused for the whole call, and nothing changes, when the store is past its free-plan revenue limit, when an offer is past its end date, when its start date is still ahead, or when the plan does not include that offer type; the reply names each offer and why.
duplicate_offer
Purpose: Copy one offer to start a second, similar one.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
offer_id | string | Yes | From get_offers |
Returns: the new offer's id. The copy is named "Copy of <name>", keeps its schedule, texts, translations and every setting, gets its own new Shopify discount or bundle product, and starts switched off.
delete_offers
Purpose: Permanently delete 1 to 10 offers.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
offer_ids | array of string | Yes | 1 to 10 ids from get_offers |
Deletes each offer's Shopify discount or bundle product along with it. There is no undo from inside the app. A deleted bundle product disappears from the storefront at once. An id that is not one of this store's offers refuses the whole call.
Always confirm the exact offers with the merchant before calling this. Deleting and recreating is
never how an offer is edited, update_offer changes one in place.
update_widget_settings
Purpose: Change storefront widget design for one offer type, colors, font sizes, texts, layout and display options, and their translations.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
offer_type | enum | Yes | One of the six offer types |
changes | object | No | {id: value} with ids from get_widget_settings, colors as "#1A2B3C" |
translations | object | No | {locale: {text id: text}}; an empty string removes that translation |
Every change is checked first, if one is refused, nothing is written. Setting colorStyle to
"basic" also applies the Basic colors preset and lists every value it overwrote. Each change
comes back with its previous_value, send that value back to undo it. storefront in the result
is "live" when shoppers already see the change, or "syncing" when it lands within about a
minute.
import_image
Purpose: Copy an image from a public URL into the store's Shopify Files, for use in an offer or widget image field.
Parameters:
| Name | Type | Required | Notes |
|---|---|---|---|
url | string | Yes | A public https:// URL |
alt | string | No | Alt text for the image |
Returns: the Shopify CDN URL. Offer image fields (a Volume tier image, a Bundle Builder banner, the Mix & Match volume button image) only accept a Shopify CDN URL, so an image from elsewhere needs to go through this tool first. The file stays in the store's Files even if no offer ends up using it.
Related
- Tools reference, the compact list
- Recipes, these tools in sequence, on real questions