# Cartrella Docs — full corpus > Documentation for Cartrella — the Shopify app suite that collapses bundles, reviews, upsell, and loyalty into one platform: one account, one data graph, one bill. # Copilot Tell Cartrella what you want done — it drafts, you approve. Canonical: https://cartrella.com/docs/copilot Copilot is the assistant inside every Cartrella app. It answers from your store's **real data** — your packs, reviews, discount rules, catalog, inventory, and 30-day sales aggregates — and drafts changes for you to approve. ## What it can do - **Answer with your numbers.** "What's my best seller this month?" "Anything about to stock out?" "What's waiting on me?" - **Draft packs from plain English** *(Bundles)*. "Sell my soap in a 2-pack for $36 and a 4-pack for $72" → a draft card with the exact tiers and prices. You pick the product and click create; it lands as a draft in Bundles for you to publish. - **Spot conflicts across modules** *(suite)*. Copilot notices things like a quantity-break discount undercutting a pack's price, and says so before you publish. ## What it will never do Copilot **cannot change anything by itself**. It holds no Shopify credentials; every proposal is a card you explicitly approve, and the change runs through the same publishing pipeline as a manual edit. Nothing goes live without your click. ## Privacy Copilot's context is your store's aggregate data — product titles, counts, and money totals. **No customer names, emails, or addresses ever enter a prompt.** Find Copilot in the app's navigation. In a standalone app it knows that app's module; in the suite it reasons across all of them. # Getting started Install Cartrella and sell your first pack in five minutes. Canonical: https://cartrella.com/docs/getting-started ## Choose your app Cartrella ships as one suite **and** as standalone apps — all on the same platform: | You want | Install | |---|---| | Everything (bundles + reviews + discounts + copilot) | **Cartrella** (the suite) — one subscription covers it all | | Just one module | **Cartrella Bundles**, **Cartrella Reviews**, or **Cartrella Discounts** — each fully standalone | You never need more than one Cartrella app. If you start standalone and want more later, install the suite — it takes over seamlessly and your data is already there. Nothing migrates, nothing re-onboards. ## Install Install your chosen app from its install link (App Store listings coming). During install you approve only the permissions that app needs. Each app's subscription is managed on its **Plan** page. ## Sell your first pack 1. Open **Cartrella** in your Shopify admin — the dashboard's setup guide tracks these steps. 2. **Bundles → Create pack** — pick a product, set a size and pack price (e.g. 2 for $36). 3. **Publish** — Cartrella creates the pack product, prices it, and puts it on your Online Store. 4. **Add the storefront block** — theme editor → product page → **Add block → Apps → Cartrella pack selector**. Your product page now shows *Single · Pack of 2* with one buy button. That's the whole loop. Details, mix & match, and theme fitting: **[Bundles](/bundles)**. ## See it before you install Click through the **[live demo store](/bundles/demo)** — the size ladder, the pack page, and the checkout expansion, running on a real store. # Cartrella documentation One suite for bundles, reviews, upsell, and loyalty — one account, one data graph, one bill. Canonical: https://cartrella.com/docs/ Cartrella helps you sell more per order. Three modules are live — **Bundles** (pack buy buttons priced by checkout itself), **Reviews** (moderation-first, verified buyers), and **Discounts** (automatic quantity breaks, thresholds, Buy-X-get-Y) — plus **Copilot**, the in-admin assistant that drafts changes from your store's real data and waits for your approval. **One platform, your choice of app.** Install the **Cartrella suite** for everything on one subscription, or start with a standalone app — *Cartrella Bundles*, *Cartrella Reviews*, or *Cartrella Discounts* — and upgrade later. Every app runs on the same platform, keyed to your shop, so upgrading never migrates anything: your data is already there. ## Start here - **[Getting started](/getting-started)** — pick your app, install, and turn on your first module. - **[Bundles](/bundles)** — packs sold with one buy button, expanded into real products at checkout. - **[Reviews](/reviews)** — collect and show product reviews, moderated by you. - **[Discounts](/discounts)** — automatic quantity breaks, thresholds, and Buy-X-get-Y. - **[Copilot](/copilot)** — tell Cartrella what you want done; approve what it drafts. - **[Live demo](/bundles/demo)** — click through Bundles on a real store before installing. ## For developers The [API reference](/developers/api) is generated from the same OpenAPI contracts that drive the product — it cannot drift from the real API. # Adopting existing products Use pack products your store already sells — keep URLs, SEO, and history. Canonical: https://cartrella.com/docs/bundles/adopting-existing-products Many stores already sell packs — a "4-Pack" variant with its own URL, reviews, and sales history. You don't have to replace it: **link it**. ## Link an existing variant In the pack editor, on any size row, choose **Link existing variant** and pick the variant that already represents that pack size. On publish, Cartrella: - sets the variant's price to the pack price, - ensures the product is on your Online Store, - wires the variant into checkout expansion — buying it now delivers the real component items. What Cartrella will **never** do to an adopted product: create, rename, re-tag, or archive it. It's your product; Cartrella only makes it expand. Deleting the pack in Cartrella leaves the product exactly as it was. ## Inventory on adopted variants Adopted pack variants should not track their own stock — the components carry the inventory. If the variant currently tracks inventory, untrack it (Product → Variant → uncheck *Track quantity*) so you don't maintain two stock pools. ## When to adopt vs. create | Situation | Do this | |---|---| | Store already sells pack SKUs with history | **Adopt** them | | Fresh store, or new pack offerings | Let Cartrella **create** pack products | | Both (some sizes exist, some are new) | Mix freely — per size row | # Creating packs Create, publish, and edit fixed packs. Canonical: https://cartrella.com/docs/bundles/creating-packs ## Create a pack In **Cartrella → Bundles → Create pack**: 1. **Pack name** — "SheaX". With multiple sizes, products are named "SheaX — Pack of 2", "SheaX — Pack of 4". 2. **Pack type** — *Same product × N* for a fixed pack. 3. **Select the product to pack** — the base product shoppers receive. 4. **Pack sizes** — one row per size you sell: items in the pack and the pack price. The per-item price shows as you type ("$18.00 per item"). A new pack starts as a **draft** — nothing is on your storefront yet. ## Publish **Publish** does everything in one step: - creates one pack product per size (priced at the pack price, tagged `cartrella-pack`, inventory untracked — components carry the stock), - publishes them to your Online Store, - activates the checkout expansion. The pack row shows **Live** with a storefront link per size. ## Edit **Edit** reopens the pack. Saving a **live** pack republishes automatically: - surviving sizes update their existing products in place (same URLs), - removed sizes have their products archived, - new sizes get new products. Prices on the storefront follow within seconds. ## Unpublish and delete - **Unpublish** archives the pack products and disables expansion; the pack stays in Cartrella as a draft. - **Delete** does the same and removes the pack from Cartrella. - Products you [adopted](/bundles/adopting-existing-products) are **never** archived — Cartrella only manages products it created. ## Inventory notes Pack products carry no stock of their own — components do. When a component sells out, Cartrella **automatically takes the pack off your storefront** (the pack product goes to Draft), and when you restock, it comes back on its own. No manual unpublishing. Packs built on [adopted products](/bundles/adopting-existing-products) are the exception: Cartrella never changes an adopted product's status, so stockout handling for those stays in your hands. # Live demo Click through Bundles running on a real Shopify store. Canonical: https://cartrella.com/docs/bundles/demo Our demo store runs the same Cartrella build we ship — nothing staged. **Store:** [cartrella-dev-store.myshopify.com](https://cartrella-dev-store.myshopify.com) · password: **`awfirt`** ## What to try 1. **The size ladder** — open the [SheaX Test Bar](https://cartrella-dev-store.myshopify.com/products/sheax-test-bar-dead-sea-salt) product page: *Single $20 · Pack of 2 $36* with one buy button, rendered by the pack selector block in "Match theme button" mode. 2. **Checkout expansion** — add the Pack of 2 and open checkout: the pack becomes **2 × SheaX Test Bar at $18.00 each, total $36.00**. That swap is the whole product: shopper buys a pack, your store fulfills real SKUs. 3. **The pack product page** — packs are real products: [sheaxpack1](https://cartrella-dev-store.myshopify.com/products/sheaxpack1). 4. **Reviews** — the same product page shows the Cartrella review block: star summary, published reviews, and a **Write a review** form. Submit one — it lands in the merchant's moderation queue, which is exactly why you won't see it appear immediately. The demo store is a development store, so checkout stops before payment — everything up to the payment step is production behavior. # Bundles Sell packs with one buy button — expanded into real products at checkout. Canonical: https://cartrella.com/docs/bundles Packs sell more units per order — but only if buying one is a single click and the price never surprises anyone. Bundles gives you both: a pack is a real product with one buy button, the price you set is the price checkout charges, and when the order lands your **inventory, fulfillment, and reports see the real items**, not a mystery SKU. ## The model in one picture ``` "SheaX 2-Pack" — $36 ──checkout──▶ 2 × SheaX Bar ($18 each) (what the shopper buys) (what your store fulfills) ``` - **The pack is a product.** It has its own page, price, and buy button. - **Checkout expands it.** A Shopify Function (running inside checkout, on Shopify's servers) swaps the pack for its components at the configured per-item prices. The price you set is the price charged — there is no client-side math to drift. - **Inventory stays true.** Pack products carry no stock of their own; selling a 2-pack decrements the component's inventory by 2. One stock pool covers singles and every pack size. ## Two kinds of packs | | Fixed pack | Mix & match | |---|---|---| | What the shopper gets | The same product × N ("Honey Bar 2-Pack") | Their choice of eligible products to fill a Pack of N | | Storefront | The pack product's own page, or a size ladder on the base product's page | A "build your pack" picker rendered by the Cartrella theme block | | Set up in | [Creating packs](/bundles/creating-packs) | [Mix & match](/bundles/mix-and-match) | ## Where to go next 1. [Creating packs](/bundles/creating-packs) — your first pack, publishing, editing 2. [Theme setup](/bundles/theme-setup) — the pack selector block and how it adapts to your theme 3. [Adopting existing products](/bundles/adopting-existing-products) — if your store already sells pack variants 4. [Live demo](/bundles/demo) — click through a working store # Mix & match Let shoppers fill a pack with their own choice of products. Canonical: https://cartrella.com/docs/bundles/mix-and-match A mix & match pack sells a **Pack of N** the shopper fills themselves — "any 2 of our six bars". ## Create one 1. **Create pack** → type **Customer picks the mix**. 2. **Select eligible products** — at least two. 3. Add sizes as usual (e.g. 2 for $36, 4 for $64). 4. **Publish.** Publishing creates one hidden **container product per size** (tagged `cartrella-pack` and `cartrella-mix`). Shoppers don't browse to containers directly — they use the picker. ## The picker Add the **Cartrella pack selector** block to the container product's page (see [Theme setup](/bundles/theme-setup)). It renders the eligible products as a grid with steppers, enforces the pack size, and adds the pack to the cart with the shopper's selection. ## How the selection is protected The shopper's choice travels with the cart line, but **checkout re-validates everything** against your published configuration: the selection must match the pack size exactly and contain only eligible products, and every unit is priced at your configured per-item price. A tampered cart can change *which* eligible items fill the pack — never the price, the count, or the eligibility. # Theme setup The pack selector block, and how it adapts to any theme. Canonical: https://cartrella.com/docs/bundles/theme-setup The **Cartrella pack selector** is one theme app block with two behaviors, decided automatically by the page it's on: - On a product that's the **base of fixed packs** → a size ladder: *Single · Pack of 2 · Pack of 4* with one buy button at the selected price. - On a **mix container** product → the build-your-pack grid. - On any other product → it stays hidden. ## Add the block Online Store → **Customize** → open a product page → **Add block → Apps → Cartrella pack selector** (place it between the price and the buy button) → **Save**. ## Fitting your theme The block inherits your theme's fonts, and its button adapts in three tiers: 1. **Match theme button** (default) — the pack button adopts your theme's own add-to-cart button styling automatically. On most themes this needs zero configuration. - Custom-built product pages: point it at your button with **Button to match (CSS selector)**, e.g. `.dark-button.main-button`. 2. **Style settings** — button text, highlight badge ("Best value" — empty hides it), show/hide savings and per-item price, accent color, corner radius, or full **Custom colors**. 3. **Custom CSS** — a block setting for theme developers. Stable hooks: `.cartrella-pack`, `.cartrella-pack__sizes`, `.cartrella-size`, `.cartrella-size__badge`, `.cartrella-size__price`, `.cartrella-size__sub`, `.cartrella-pack__grid`, `.cartrella-pack__item`, `.cartrella-pack__stepper`, `.cartrella-pack__count`, `.cartrella-pack__add`. Prices are never editable in the block — they come from your published packs, so the display can't drift from what checkout charges. ## Fully custom storefronts (headless) If your theme renders its own pack UI, skip the block entirely. Fetch the published configuration from the app proxy on your own domain: ``` GET /apps/cartrella/bundles?product_id= ``` - On a fixed-pack base product it returns the size ladder (pack variant ids to add to the cart). - On a mix container it returns the size, per-item price, and eligible products; add the container variant with the line property `_cartrella_pack` = `"gid:qty,gid:qty"`. Your UI, Cartrella's pricing authority — checkout still re-validates everything. > **Proxy path:** if another app already used the `/apps/cartrella` path when > you installed, Shopify assigns a suffix (e.g. `/apps/cartrella-1`). Set it > in the block's **App proxy path** setting. # Troubleshooting The block, the proxy path, and other sharp edges. Canonical: https://cartrella.com/docs/bundles/troubleshooting ## The block shows its heading but no options The block couldn't reach the configuration endpoint. Almost always the app proxy path: if another app owned `/apps/cartrella` when Cartrella was installed, Shopify assigned a suffixed path (e.g. `/apps/cartrella-1`). Set it in the block's **App proxy path** setting, save, and reload. ## Two Cartrella blocks appear in the theme editor You have more than one Cartrella app installed (e.g. a development install). Uninstall the one you don't use so only one set of blocks appears. ## The pack price didn't change after an edit Editing a **live** pack republishes automatically; give it a few seconds and hard-refresh. If the pack is a **draft**, publish it — drafts never touch the storefront. ## Checkout shows the pack, not the components The expansion runs inside Shopify checkout. If it isn't applying: - the pack must be **Live** in Cartrella (not draft), - the store must have completed the latest app update (reopen the Cartrella admin — it re-syncs on load actions), - development preview links (`*.shopifypreview.com`) disable checkout entirely; test on the real store domain. ## A component sold out but the pack is still buyable Cartrella takes packs off the storefront automatically when a component sells out (and restores them on restock) — this runs off Shopify's product-update events, so allow a few seconds. If a pack stays visible: adopted products are never auto-hidden (that's yours to manage), and components with "Continue selling when out of stock" enabled count as available on purpose. # Discounts Automatic discounts — quantity breaks, spend thresholds, Buy-X-get-Y. No codes. Canonical: https://cartrella.com/docs/discounts Discounts applies offers **automatically inside checkout** — no codes to type, no stacking surprises. Rules are applied by a Shopify Function, so the discount a shopper sees is the discount they're charged. ## Rule types | Type | Example | |---|---| | **Quantity break** | Buy 3+ bars → 15% off those bars | | **Cart threshold** | Spend $75 → 10% off the order, or free shipping | | **Buy X get Y** | Buy 2 bars → get a soap dish free | Every rule can be limited to specific products and scheduled with start/end dates. The rule's name doubles as the message shoppers see in cart and checkout. ## Create and publish **Cartrella → Discounts → Create rule**, pick the type, fill the numbers, **Publish**. Publishing registers one automatic discount on your store ("Cartrella Discounts") and compiles your live rules into it — unpublish or delete removes them just as cleanly. ## Stacking Rules use Shopify's native discount-combination classes, so product, order, and shipping discounts combine predictably — a quantity break and a free-shipping threshold can apply together; two order discounts won't double-apply (the better one wins). ## Coming next Customer-tag targeting, the visual rule builder in the agency portal, and copilot rule-building in plain language. # Agency portal Every store you manage, under one umbrella. Canonical: https://cartrella.com/docs/portal The Cartrella portal — [portal.cartrella.com](https://portal.cartrella.com) — is for agencies and multi-store owners: one sign-in, every linked store's Cartrella modules in one place, without handing out Shopify staff accounts. ## Sign in Enter your work email; you get a single-use sign-in link (no passwords). First sign-in creates your account. ## Create an organization An organization holds the stores you manage — your agency, or just your own brands. Creating one gives you an **invite code** (like `crt-a1b2-c3d4`). ## Link a store Linking happens **inside the store's own Shopify admin** — so only someone with access to the store can link it: 1. Open **Cartrella** in the store's Shopify admin 2. **Settings → Agency portal** 3. Enter the organization's invite code → **Link store** The store appears on your portal dashboard immediately, with its live packs and pending-review counts. Unlink any time from the same Settings page. ## What's next The portal grows with the suite: cross-store configuration copy, review moderation across stores, and analyst seats are on the roadmap. # Reviews Collect and show product reviews — moderated by you, styled by your theme. Canonical: https://cartrella.com/docs/reviews Reviews are the proof that sells — especially next to a pack buy button. Cartrella Reviews collects them on your product pages, holds every submission for **your approval**, and displays them in your theme's own look. ## How it works 1. A shopper writes a review in the review block on your product page — rating, name, and their words. 2. The review lands in **Cartrella → Reviews → Pending**. Nothing shows on your storefront yet. 3. You **Publish** (or Reject). Published reviews appear on the product page with the star summary updating automatically. ## What we store — and don't A review is the shopper's public words: rating, title, body, and the display name they typed. Cartrella stores **nothing else about the shopper** — no email, no IP address, no account link. Spam is filtered by an invisible honeypot field before it ever reaches your queue. ## The growth loop: review invitations Most reviews come from being asked. When an order is **fulfilled**, Cartrella queues one invitation email and sends it a few days later: the shopper sees their items, taps **Write a review**, and lands on your product page with the review form ready. One email per order, ever — no sequences, no nagging. The email is sent on your store's behalf (your store name leads; Cartrella is a footer note). The shopper's email address is used only to send that one message and is deleted the moment it's sent. ## Built in - **Verified buyer badges** — when a logged-in shopper reviews something they actually bought, the review carries a Verified badge. The check runs at submission; only the yes/no lands in storage. - **Stars in Google** — the review block emits the structured data (aggregate rating + reviews) search engines use for star-rating rich results. No setup. - **Bring your reviews with you** — import from any app that exports CSV: **Reviews → Import CSV**. Original dates are kept. ## Next - [Moderation](/reviews/moderation) — the queue, publishing, rejecting - [Importing reviews](/reviews/moderation#import-reviews-from-another-app) — CSV migration - [Theme setup](/reviews/theme-setup) — the review block on your pages - [Live demo](/bundles/demo) — see reviews on the demo store # Moderation Every review waits for you — publish, reject, or delete. Canonical: https://cartrella.com/docs/reviews/moderation Open **Cartrella → Reviews**. The queue has three tabs: - **Pending** — new submissions. The dashboard badge counts them. - **Published** — live on your storefront. - **Rejected** — kept for reference, never shown. Each row shows the review, rating, author name, and date. ## Actions - **Publish** — the review appears on the product page and joins the star summary immediately. - **Reject** — hides it permanently (you can still publish later if you change your mind). - **Delete** — removes it entirely. ## What shoppers see After submitting, the shopper sees "your review appears once it's approved" — no review ever shows without your say-so, and the star average only counts published reviews. ## Import reviews from another app Switching from another reviews app? Export your reviews as CSV and bring them along: **Reviews → Import CSV**. Required columns: `product_id` (the numeric Shopify product id), `rating` (1–5), `body`, `author_name`. Optional: `title`, `created_at` (original date, kept), `published` (defaults to true — imported reviews go straight to your storefront). Up to 1,000 rows per file; invalid rows are skipped and reported. ## Verified buyer badges When the reviewer was signed in and has an order containing the product, their review shows a **Verified buyer** badge on the storefront and a **Verified** tag in your queue. The purchase check happens at submission time — Cartrella stores only the outcome, never the shopper's identity. # Theme setup Add the review block to your product pages. Canonical: https://cartrella.com/docs/reviews/theme-setup Online Store → **Customize** → open a product page → **Add block → Apps → Cartrella reviews** (usually below the product description) → **Save**. The block renders: - the **star summary** (average + count over published reviews), - the **review list**, newest first, - a **Write a review** form (rating stars, name, title, review text). ## Fitting your theme Fonts inherit from your theme. Block settings cover the heading, button labels, empty-state text, accent color, and corner radius. ## Headless storefronts The review data is available from the app proxy on your own domain: ``` GET /apps/cartrella/reviews?product_id= → summary + published reviews POST /apps/cartrella/reviews → submit (lands pending) ``` POST body: `{ product_id, rating (1–5), title?, body, author_name }`. Same proxy-path note as Bundles: if Shopify assigned your store a suffixed path (e.g. `/apps/cartrella-1`), set it in the block's **App proxy path** setting. # API reference Generated from the Cartrella OpenAPI contracts. Canonical: https://cartrella.com/docs/developers/api The reference below is generated from `contracts/openapi/*.v1.yaml` — the same contracts that generate the server stubs, the TypeScript SDK, and the copilot's tools.