Developers Store

Platform

Store

Frontpage Store: live catalog, storefront markup, Stripe checkout, CLI, and the public v1 API.

What this is

Frontpage Store is the product list for a site. You create items, give them prices and stock, and Frontpage charges those prices at checkout. Pay buttons and the online cart read the same list. The sellable unit is the variant (the SKU): price, sale price, and stock live there. Public shipping is the store fulfillment setting, not a per-variant extra. The item is the merchandising parent: title, description, photos, and option names such as Size or Color.

An item with no real options still has one variant, id default. That is how a $50 deposit and a t-shirt in S/M/L share one set of commands.

Store is a platform product, like People. It is not a Modules card and it is not Square. Do not enable it with frontpage modules enable shop — that command does not exist. Write data-fp-store-grid / data-fp-store-product markup (see Build the storefront). Do not invent a bare data-fp-store attribute.

frontpage store categories create hats --label Hats
frontpage store items list
frontpage store items create camp-hat --title "Camp hat" --price-cents 4999 --categories hats
frontpage store items list --category hats
frontpage store variants add camp-hat --id m --option Size=M --price-cents 4999 --stock 8
frontpage store items stock camp-hat --variant m --add 5
frontpage store settings get
frontpage store connect --no-open
frontpage store status

Requires frontpage-host 0.1.21 or later for frontpage store connect|status|disconnect. 0.1.20 has categories. 0.1.19 has items, variants, stock, and settings. Upgrade with npm i -g frontpage-host@latest.

Build the storefront

The catalog lives in Store. The page is yours. Write markup; Frontpage fills live items from the Store catalog and a single product from the live Store item. Do not type titles, prices, or images into the file. Do not invent a site-page getShopItems helper. Do not write src/content/products/. Do not invent a bare data-fp-store attribute — that is not a category flag. The real attrs are data-fp-store-grid, data-fp-store-product, data-fp-store-bind, and the cart attrs below.

A fully functional shop needs all of these. Missing any one leaves visitors unable to buy.

  1. At least one active item (frontpage store items create or manage_store). Money is integer cents only.
  2. A product grid: <section data-fp-store-grid data-fp-requires="store"> with <template data-fp-product-card>, binds, CSS, and <button data-fp-add-to-cart data-fp-store-bind="itemId" data-fp-requires="store">.
  3. Header cart chrome: data-fp-cart-open and data-fp-cart-count, both with data-fp-requires="store".
  4. A cart: <aside data-fp-cart data-fp-requires="store"> with <template data-fp-cart-line>, empty state, binds, empty <div data-fp-cart-method></div>, data-fp-cart-close, and data-fp-checkout.
  5. Stripe: frontpage store connect (admin key; finish logged-in in the browser). Do not paste a stripeAccountId.
  6. Fulfillment: frontpage store settings set-fulfillment after charges are on (pickup and/or shipping).

Store-ready = Stripe chargesEnabled and fulfillment set. Visitor Add / Cart unhide only then. Pay buttons use data-fp-pay + data-fp-requires="pay" and unhide when charges are on (fulfillment not required). The Store owner panel is Starter+; Free hides that panel and manage_store, not a Store-ready visitor cart.

Product grid

<section data-fp-store-grid data-fp-requires="store">
  <div data-fp-store-list>
    <template data-fp-product-card>
      <img data-fp-store-bind="image" alt="" />
      <h3 data-fp-store-bind="title"></h3>
      <p data-fp-store-bind="description"></p>
      <p data-fp-store-bind="price"></p>
      <a data-fp-store-bind="href">View</a>
      <button data-fp-add-to-cart data-fp-store-bind="itemId" data-fp-requires="store">
        <span data-fp-store-bind="cta">Add to cart</span>
      </button>
    </template>
  </div>
  <p data-fp-store-empty>Nothing here yet.</p>
</section>

Card binds: itemId, title, description, descriptionHtml, descriptionExcerpt, image, price, compareAtPrice, cta, href, url, optionName, stock. Dates or invented keys stay empty. itemId stamps add-to-cart or pay from the live store checkout. cta is Add to cart or Buy now. href / url default to /products/{itemId}. Set data-fp-product-base="/shop" on the grid when the product template lives under /shop. Filter a listing with data-category="hats". After settings set-checkout, do not rewrite the cards — the platform stamps the button.

Allowed exception: one featured product may use <button data-fp-add-to-cart="camp-hat" data-fp-requires="store"> when that itemId already exists. A shop or merch section must be the grid template.

One product page

Write one src/pages/products/item.astro (or src/pages/shop/item.astro). The gateway serves that template for /products/{kebabId} and /shop/{kebabId} before 404.html. An exact static file still wins. Do not use a leading underscore — Astro will not emit that file. Do not write one typed file per product.

<article data-fp-store-product data-fp-requires="store">
  <div data-fp-store-images>
    <template data-fp-store-image><img data-fp-store-bind="image" alt="" /></template>
  </div>
  <h1 data-fp-store-bind="title"></h1>
  <p data-fp-store-bind="price"></p>
  <p data-fp-store-bind="compareAtPrice"></p>
  <div data-fp-store-bind="description"></div>
  <p data-fp-store-bind="optionName"></p>
  <div data-fp-store-variants></div>
  <p data-fp-store-bind="stock"></p>
  <div data-fp-store-customizations></div>
  <button data-fp-add-to-cart data-fp-store-bind="itemId" data-fp-requires="store">
    <span data-fp-store-bind="cta">Add to cart</span>
  </button>
  <p data-fp-store-empty>This product is gone.</p>
</article>

Optional data-item-id pins an item. Otherwise the last kebab path segment is the id. Frontpage fills from the live Store item. optionName is the live version type. Live variants mount into the empty slot from optionNames in catalog order — do not invent SKUs. Choices and text fields mount into data-fp-store-customizations above Add to cart. stock is remaining for the selected version; omit or hide that bind to hide leftover counts. Each distinct version, choice, or typed value is its own cart line. The picker stamps data-fp-variant. Product JSON-LD and the document title come from the live item and the presentment currency when present. Do not bake a Product price on BaseLayout jsonLd. A missing item shows data-fp-store-empty and noindex.

Product description

Description is a small markdown subset stored on the item: paragraphs, **bold**, *italic*, ## / ###, - or 1. lists, and [text](url) (https, http, /, mailto:). First paragraph is the default card. Story, dimensions, and lists go below. The product-page bind is <div data-fp-store-bind="description"> so headings and lists can render; cards stay <p>. Bind descriptionHtml to show the full story on a card, or descriptionExcerpt to force the first paragraph on the product page. Do not write HTML. Do not bake the story into item.astro. CLI: --description still works; JSON --file is the multiline path.

Cart

<button data-fp-cart-open data-fp-requires="store">
  Cart (<span data-fp-cart-count>0</span>)
</button>
<aside data-fp-cart data-fp-requires="store">
  <button data-fp-cart-close aria-label="Close cart">Close</button>
  <div data-fp-cart-empty>Your cart is empty.</div>
  <div data-fp-cart-list>
    <template data-fp-cart-line>
      <img data-fp-cart-bind="image" alt="" />
      <p data-fp-cart-bind="title"></p>
      <p data-fp-cart-bind="unitPrice"></p>
      <button data-fp-cart-qty="-1">−</button>
      <span data-fp-cart-bind="qty"></span>
      <button data-fp-cart-qty="+1">+</button>
      <p data-fp-cart-bind="lineTotal"></p>
      <button data-fp-cart-remove>Remove</button>
    </template>
  </div>
  <div data-fp-cart-summary>
    <p data-fp-cart-bind="subtotal"></p>
    <div data-fp-cart-method></div>
    <p data-fp-cart-bind="shipping"></p>
    <p data-fp-cart-fee hidden>Service fee <span data-fp-cart-bind="fee"></span></p>
    <p data-fp-cart-bind="total"></p>
    <button data-fp-checkout>Checkout</button>
  </div>
</aside>

Cart binds: image, title, unitPrice, qty, lineTotal, subtotal, shipping, fee, total. Line items go in data-fp-cart-list. Totals, fee, method, and checkout go in data-fp-cart-summary so the list scrolls and checkout stays on screen. Restyle those hooks, or set data-fp-cart-scroll="off" if you own the layout. Write empty data-fp-cart-method only. The platform mounts pickup/delivery and puts the country picker inside Delivery. You may restyle .fp-cart-method-option; do not rebuild the picker. A leftover data-fp-cart-country slot is optional and stays empty. Do not write /__shop/choose, prompt(), card fields, or a pay modal. Pay now extras (amount, pickup vs delivery, country) open this same cart drawer. The first Pay now quote does not reuse a leftover cart method. If the cart is position: fixed, start it at top: var(--fp-toolbar-offset, 0px). If you omit the fee row, the platform injects a Service fee line when the quote has one.

Checkout is store-wide

frontpage store settings set-checkout --mode cart|instant (agent: set_store_checkout). Default is cart. Instant is Pay now: the platform stamps the same grid/product button as data-fp-pay and hides header cart-open/count. Do not set --checkout on items create to pick cart vs Pay now — that leftover flag does not win. Gift cards still use per-item --amount open + --min-cents.

Optional Service fee: frontpage store settings set-checkout-fee --cents 200 (or --off). It applies to Add to cart and Pay now. Bind it as fee.

Pay buttons

Write them for a deposit, retainer, gift card, or one-off fee. Gate with data-fp-requires="pay". The click charges one Store item with that same itemId. Create the item first. Fixed amount uses data-amount in integer cents (5000 is $50.00). Customer-chosen amount uses data-variable="true" and data-min. Do not build card fields.

Store vs Stripe vs Square

These are easy to mix up. They do different jobs.

Product What you manage Who takes payment Command
Frontpage Store Your products, prices, and stock Stripe, after you connect frontpage store …
Stripe connect The owner’s Stripe account, currency, and (for the cart) pickup or shipping Stripe frontpage store connect
Pay button on the page A buy-now button. The product lives in Store. Store checkout instant stamps grid buttons as Pay now; leftover data-fp-pay still charges that itemId Stripe Page markup data-fp-pay plus Store
  • frontpage store connect connects Stripe for the cart and pay buttons. Neither command creates products.
  • Square stays supported. If they already run pickup on Square, use ordering. Do not copy a Square menu into Store.
  • Published checkout reads Store. Visitor pages fill from the Store catalog. New product work uses these commands, not src/content/products/.
  • The in-product editor can write Store with manage_store. Create a category before assigning it. Do not write src/content/products/. Do not paste a stripeAccountId. A public product page is one src/pages/products/item.astro (or shop) with data-fp-store-product binds. Frontpage fills it from the live Store item. Do not type titles or prices into one file per product. Do not invent a site-page getShopItems helper.

Who can read and write

Same site fence as push, inbox, and people. A key cannot open another tenant’s products by guessing a slug or an item id.

Who What happens
No key, or a bad key HTTP 401. CLI exit 4. Nothing is read or written.
Valid key, but you are not a member of --site HTTP 404 Site not found. Same response for an unknown slug or a deleted site.
Member with role viewer items list, items get, categories list, settings get, and shop status work. Create, update, archive, activate, images, stock, variants, categories writes, and settings writes return HTTP 403. CLI exit 4. Orders and payments need editor (same as inbox). Connect and disconnect need admin.
Member with role editor or admin Read and write Store on that site only. shop connect and shop disconnect need admin.
Item id copied from site B, used with --site of site A HTTP 404 Item not found. Same public id on two sites is two products.
  • --site is a subdomain slug only (example: acme). Not a custom domain, not acme.frontpage.host, not a site UUID. See Flags.
  • A JSON body that includes siteId is ignored. The server uses the site from the slug after membership is checked.
  • Keys are account-wide. One fp_live_… acts as that user on every site they belong to.

Full 401 / 403 / 404 table: Isolation & keys.

Targeting a site

  1. --site <slug>, -s <slug>, or --site=<slug> on this command. Wins for this process only.
  2. Otherwise the slug from frontpage use <slug> in this folder.
  3. If neither is set, exit 2: No site selected. Run: frontpage use <subdomain> or pass --site <subdomain>.
frontpage use acme
frontpage store items list

frontpage store items list --site acme
frontpage store items create camp-hat --site bakery --title "Camp hat" --price-cents 4999

# Rejected locally (exit 2), never sent
frontpage store items list --site acme.frontpage.host
frontpage store items list --site https://acme.frontpage.host

Money

Every money flag is integer cents. $49.99 is 4999. Do not send 49.99. Do not send $4999. The CLI rejects those locally (exit 2) before any request.

Field Allowed What it means
priceCents Integer 50 to 10,000,000 Regular price. $0.50 to $100,000.00
discountPriceCents Integer 0 to 10,000,000, and strictly less than priceCents Sale price Frontpage charges. Not a coupon. Not a “compare at” number. Omit it if there is no sale.
shippingCents Integer 0 or greater. Default 0 Stored on the variant. Public checkout does not add it — shipping is the store fulfillment setting
minCents Same bounds as priceCents. Required when amount is open Lowest gift / open amount the buyer may choose

Charge amount is the discount when it is set and valid, otherwise the regular price. A caller cannot send a cheaper price at checkout and have it stick. Open-amount items (gift cards) need the buyer’s chosen amountCents at checkout, at least minCents.

Item id

Public id for the product. Kebab-case, 2 to 40 characters: ^[a-z0-9][a-z0-9-]{1,39}$. Unique on that site, not across Frontpage. Examples: camp-hat, session-deposit.

  • Positional: frontpage store items get camp-hat
  • Flag: frontpage store items get --id camp-hat or --item camp-hat
  • A string that does not match the regex fails locally as Item not found.
  • A valid id that is not on this site is HTTP 404 Item not found.
  • Creating the same id twice on one site is HTTP 409 That item id already exists on this site.

Items

items list

Returns non-archived items and their variants. Bare frontpage store items is the same as list.

Flag Required What it does
--archived no Include archived items. Server query is ?archived=1.
--category hats no Only items in that live category. Unknown or archived slug returns an empty list, not 404. Server query is ?category=hats.
frontpage store items list
frontpage store items list --category hats
frontpage store items list --archived
frontpage store items list --json

JSON: { "slug": "acme", "items": [ … ] }. Empty list prints No items.

items get

One item plus every variant (including inactive). Required: item id.

frontpage store items get camp-hat
frontpage store items get --id camp-hat --json

items create

Creates one item. Variants are allowed only on create. A later update that sends variants is rejected. After create, use variants add|update|archive.

Defaults if you omit them: amount is fixed. Checkout is store-wide via settings set-checkout. You still need a title and a price (or a --file that includes them).

Flag Required Values What it does
item id (positional, --id, or --item) yes Kebab id, 2–40 chars Public product id
--title yes (unless in --file) Text, trimmed, max 200 chars Name shown to the owner and, later, to buyers
--description no Plain text, max 8000 chars Optional body. Not HTML
--checkout no (default cart) instant or cart Leftover. Checkout is store-wide via settings set-checkout. Do not use this flag to pick cart vs Pay now
--amount no (default fixed) fixed or open fixed charges the variant price. open is a gift card / buyer-chosen amount. Open requires --min-cents
--min-cents when amount is open Integer cents, 50–10,000,000 Lowest amount the buyer may choose. Illegal on fixed
--requires-shipping no (default false) true or false Whether this item needs a ship-to / pickup path
--option-names no Comma list, max 8 names, each max 40 chars, unique Example Size,Color. Set at create only. To change the shape, archive the item and create a new id
--categories no Comma list of live slugs, max 8 Example hats,summer. Create each category first. An unknown slug is 400 Create category hats first.
--price-cents yes unless --file already has variants Integer cents, 50–10,000,000 Builds the first variant when you are not passing a variants array in a file
--discount-cents no Integer cents, strictly less than price Sale price on that first variant
--shipping-cents no Integer cents, 0 or more Per-item shipping on that first variant
--stock no Integer count, or unlimited Counted inventory, or unlimited (services, deposits, digital). Omit to leave the server default (unlimited)
--variant-id / --variant no (default default) Kebab id, 1–40 chars Id of the first variant. Use s / m when you also pass option names
--option when the item has option names Name=Value, repeatable. Value max 80 chars Example --option Size=S --option Color=Red. Keys must match --option-names
--urls / --url / --image no HTTPS URLs, max 8. Repeat or comma-separate Site path /__media/uploads/<this-slug>/… or an HTTPS URL already saved on this site. Upload first. No http:// or data URIs
--file no Path to a JSON object Body sent as create. Flags override file fields. File may include a variants array for several SKUs at once
# One product, no sizes
frontpage store items create session-deposit --title "Session deposit" --price-cents 5000

# Hat with Size, first SKU is S, in hats
frontpage store items create camp-hat --title "Camp hat" --requires-shipping true --option-names Size --variant-id s --option Size=S --price-cents 4999 --stock 10 --categories hats

# Gift card
frontpage store items create gift-card --title "Gift card" --amount open --min-cents 2500 --price-cents 2500

items update

Patches merchandising on an existing item. Required: item id and at least one field. Rejects a variants array. Does not change optionNames to a new shape (same names are allowed; a different list returns 400).

Flag What it does
--title Replace the title
--description Replace the description (pass a string; empty clears to blank)
--checkout Leftover. Use settings set-checkout --mode cart|instant
--amount fixed or open. Switching to open requires --min-cents
--min-cents New open-amount floor
--requires-shipping true or false
--active true or false. Inactive items stay in the owner list and are not for sale. Prefer items activate --off for the same job
--categories Replace the category list. Empty is not a flag; pass the slugs you want. Create them first.
--urls Replace the image list (same rules as create)
frontpage store items update camp-hat --title "Camp hat v2"
frontpage store items update camp-hat --categories hats
frontpage store settings set-checkout --mode instant

items archive

Soft-deletes the item. It disappears from the default list. It is not for sale. The id stays taken on that site (a new create with the same id is 409). Required: item id.

frontpage store items archive camp-hat

items activate

Sets active without archiving. Required: item id.

Flag What it does
(none) Turns the item on (active: true)
--off Turns the item off. Still listed for the owner. Not for sale
--active true|false Same as on / off. Do not combine with --off unless you mean off
frontpage store items activate camp-hat --off
frontpage store items activate camp-hat

items images

Replaces the whole image list. Required: item id. Each image is a site path /__media/uploads/<this-slug>/… or an HTTPS URL already saved on this site. Max 8, each max 2000 characters. Passing no URLs stores an empty list.

frontpage store items images camp-hat --urls https://example.com/hat.webp
frontpage store items images camp-hat --url https://a.example/1.webp --url https://a.example/2.webp

Stock

items stock changes how many are left. Pass exactly one of --add, --remove, or --set.

Flag Required What the server stores Result copy
--add N one of the three delta: N (N must be a positive integer) added 5
--remove N one of the three delta: -N removed 5
--set N one of the three set: N. --set 0 is sold out. --set unlimited or --set null is unlimited set to 20 or set to unlimited
--variant / --variant-id when the item has options Which SKU to change. Optionless items use default
  • JSON always includes stockNow (number or null for unlimited) and change.
  • null stock is unlimited. --add / --remove on unlimited is an error.
  • You cannot subtract below zero. Use --set 0 to mark sold out.
  • --add and --set together fail locally (exit 2). No request is sent.
frontpage store items stock camp-hat --variant m --add 5
frontpage store items stock camp-hat --variant m --remove 5
frontpage store items stock camp-hat --variant m --set 20
frontpage store items stock session-deposit --set unlimited

After --add 5 on a variant that had 4, change is added 5 and stockNow is 9.

Categories

Hats vs coats. Not Size or Color. Those are optionNames on one product. Create the category on the site first, then assign its slug to items. An item can be in more than one (hats and summer). An item with no categories still appears in the unfiltered list. It does not appear on --category hats.

Slug uses the same kebab rule as itemId. Label is owner-facing, max 60 characters. Cap: 32 live categories per site, 8 per item. Archive drops the filter; items keep the slug in the database until you remove it, and reads strip archived slugs so a Hats list does not show a dead chip.

Assigning a slug that was never created is 400 Create category hats first. Assigning an archived slug is 400. We do not auto-create tags.

These filters are for the CLI, v1, cookie, and manage_store. A public hats listing is a normal data-fp-store-grid with data-category="hats". A public product URL is served by the one item template. Do not invent a site-page getShopItems helper. Square menu categories stay inside data-fp-order-menu.

frontpage store categories list
frontpage store categories create hats --label Hats
frontpage store categories create coats --label Coats
frontpage store categories rename hats --label Hats
frontpage store categories archive summer
frontpage store items create camp-hat --title "Camp hat" --price-cents 4999 --categories hats,summer
frontpage store items update camp-hat --categories hats
frontpage store items list --category hats

Variants

Variant ids may be one character (s, m, xl), 1 to 40 chars, same kebab rules as items except the first character can stand alone. For these verbs the item is positional or --item. --id is the variant, not the item.

variants add

Flag Required What it does
item id yes Parent product
--id / --variant yes New SKU id
--price-cents yes Regular price
--option Name=Value when the item has option names Must include every option name exactly once
--discount-cents no Sale price
--shipping-cents no Per-item shipping
--stock no Count or unlimited
--active no (default true) true or false
frontpage store variants add camp-hat --id m --option Size=M --price-cents 4999 --discount-cents 3999 --stock 4

variants update

Patch one SKU. Required: item id, --id (variant), and at least one field. Same money / stock / option / active flags as add.

frontpage store variants update camp-hat --id m --price-cents 5499
frontpage store variants update camp-hat --id m --stock unlimited
frontpage store variants update camp-hat --id m --active false

variants archive

Soft-deletes a SKU. You cannot archive the last live variant (400 Cannot archive the last variant.).

frontpage store variants archive camp-hat --id s

Settings

Fulfillment, currency, store-wide checkout, and Service fee live on Store settings. They do not accept a Stripe account id. Connect Stripe with frontpage store connect.

settings get

No extra flags. Viewer allowed. JSON: { "slug", "settings": { fulfillment, currency, storeCheckout, checkoutFee } }.

frontpage store settings get
frontpage store settings get --json

settings set-fulfillment

Flag Required Values What it does
--mode / --shipping yes pickup, free-shipping, flat-rate, per-country How customers get orders. Also pass --pickup / --delivery and pickup place fields when both are on
--pickup / --delivery no Bare flag means true Turn pickup and/or delivery on. Pickup copy is --pickup-location, --pickup-address, --pickup-hours, --pickup-instructions
--flat-cents when mode is flat-rate Integer cents One shipping price for every country. $5.00 is 500
--country-rates when mode is per-country (unless rest-of-world is on) JSON array [{"country":"US","rateCents":800}] country is ISO 3166-1 alpha-2. Rate is integer cents
--rest-of-world no Bare flag means true. false / true also work Catch-all for countries not in --country-rates
--rest-cents when rest-of-world is on Integer cents That catch-all price
--free-over no Bare flag means true Waive paid shipping when the cart reaches a subtotal
--free-over-cents when free-over is on Integer cents, 1–1,000,000 That subtotal. $100.00 is 10000
frontpage store settings set-fulfillment --mode pickup --pickup-location "The shop" --pickup-address "100 Main St"
frontpage store settings set-fulfillment --pickup --delivery --mode flat-rate --flat-cents 500 --pickup-location "The shop"
frontpage store settings set-fulfillment --mode flat-rate --flat-cents 500
frontpage store settings set-fulfillment --mode per-country --country-rates '[{"country":"US","rateCents":800}]' --rest-of-world --rest-cents 1500

settings set-currency

Required: --currency usd|cad|eur|gbp|aud. Writes the Store presentment currency. Prices are integer minor units of that currency.

frontpage store settings set-currency --currency usd

settings set-checkout

Store-wide buyer checkout. Required: --mode cart|instant. cart is Add to cart. instant is Pay now (the platform stamps the same buttons and hides header cart chrome). Do not rewrite cards after this.

frontpage store settings set-checkout --mode cart
frontpage store settings set-checkout --mode instant

settings set-checkout-fee

Optional Service fee on Add to cart and Pay now. Required: --cents (integer, $2.00 is 200) or --off. Bind the amount as data-fp-cart-bind="fee".

frontpage store settings set-checkout-fee --cents 200
frontpage store settings set-checkout-fee --off

Connect

Stripe Connect for the cart and pay buttons. One Express account per site. The CLI cannot finish Stripe KYC. It returns a Frontpage URL. Open that URL while logged in. Stripe then runs in the browser. Disconnect keeps the shared Stripe account so leftover pay-button vault rows still resolve. Cancel never deletes the Express account at Stripe.

connect and disconnect need an admin key. status is viewer. Connect is not plan-gated. The Store owner panel and manage_store are Starter+. A Free site can still connect from the CLI and charge a Store-ready visitor cart.

connect

Posts to v1 and returns the cookie start URL (/api/store/connect/start?slug=…). That v1 call does not mint a Stripe account. Opening the URL in a logged-in browser is what talks to Stripe.

Flag What it does
--no-open Print the URL and stop. Does not open a browser. Does not mint Stripe. Use this in CI.
frontpage store connect
frontpage store connect --no-open
frontpage store connect --json

status

Viewer. Shows whether the store can charge, whether Stripe charges are on, and whether an Express account is present. May refresh currency from Stripe. It never creates an Express account.

frontpage store status
frontpage store status --json

disconnect

Admin. Turns Store connect off and keeps the shared Stripe account. Requires --yes when stdin is not a TTY (global flag).

frontpage store disconnect --yes

Orders and payments

These read the same ledgers as frontpage inbox orders and frontpage inbox payments. They do not create orders. Editor role required.

frontpage store orders list
frontpage store orders get 11111111-1111-1111-1111-111111111111
frontpage store payments list
frontpage store payments get 11111111-1111-1111-1111-111111111111

Get requires a row UUID. A missing or foreign id is 404. List is newest first, capped at 200.

Public v1 API

Base: https://app.frontpage.host/api/v1/sites/:slug/shop. Bearer fp_live_…. Same envelope as the rest of v1. Reads are RL-READ. Writes are RL-WRITE-LIGHT. Writes are audited (site.shop.item.create, .update, .archive, .activate, .images, .stock, site.shop.variant.add, .update, .archive, plus the four settings writes (fulfillment, currency, checkout, checkout-fee)).

Method Path Role Body / query Returns
GET /shop/items viewer ?archived=1 and ?category=hats optional { slug, items }
POST /shop/items editor Item fields plus optional variants and categories. 201 { slug, item }. Duplicate id: 409
GET /shop/items/:itemId viewer { slug, item }
PATCH /shop/items/:itemId editor Merchandising only. variants is 400. categories replaces the list { slug, item }
POST /shop/items/:itemId/archive editor Empty object { slug, item }
POST /shop/items/:itemId/activate editor { "active": true|false } (default true) { slug, item }
PUT /shop/items/:itemId/images editor { "images": ["https://…"] } { slug, item }
POST /shop/items/:itemId/stock editor { "delta": 5 } or { "set": 20 }, optional variantId { slug, itemId, variantId, stockNow, change }
POST /shop/items/:itemId/variants editor Variant object { slug, item }
PATCH /shop/items/:itemId/variants/:variantId editor Variant patch { slug, item }
POST /shop/items/:itemId/variants/:variantId/archive editor Empty object { slug, item }
GET /shop/categories viewer ?archived=1 optional { slug, categories }
POST /shop/categories editor { "slug": "hats", "label": "Hats" }. 201 { slug, category }. Duplicate: 409
PATCH /shop/categories/:slug editor { "label": "Hats" } { slug, category }
POST /shop/categories/:slug/archive editor Empty object { slug, category }
GET /shop/connect viewer Connect status. May refresh leftover currency. Never mints Stripe.
POST /shop/connect admin { "action": "connect"|"disconnect" } connect returns the cookie start URL only. It does not mint Stripe. disconnect keeps the vault on payments.
GET /shop/settings viewer { slug, settings }
POST /shop/settings/fulfillment editor See set-fulfillment flags { slug, settings }
POST /shop/settings/currency editor { "currency": "usd" } { slug, settings }
POST /shop/settings/checkout-fee editor { "enabled": true, "cents": 200 } { slug, settings }
GET /shop/orders and /shop/orders/:id editor Orders ledger
GET /shop/payments and /shop/payments/:id editor Pay-button ledger

Response shapes

{
  "slug": "acme",
  "item": {
    "itemId": "camp-hat",
    "title": "Camp hat",
    "checkout": "cart",
    "amount": "fixed",
    "requiresShipping": true,
    "active": true,
    "optionNames": ["Size"],
    "categories": ["hats"],
    "variants": [
      {
        "variantId": "m",
        "options": { "Size": "M" },
        "priceCents": 4999,
        "discountPriceCents": 3999,
        "shippingCents": 0,
        "stock": 20,
        "active": true
      }
    ]
  }
}
{
  "slug": "acme",
  "itemId": "camp-hat",
  "variantId": "m",
  "stockNow": 25,
  "change": "added 5"
}

Worked example

frontpage use acme
frontpage store categories create hats --label Hats
frontpage store items create camp-hat --title "Camp hat" --requires-shipping true --option-names Size --variant-id s --option Size=S --price-cents 4999 --stock 10 --categories hats
frontpage store variants add camp-hat --id m --option Size=M --price-cents 4999 --discount-cents 3999 --stock 4
frontpage store items stock camp-hat --variant m --add 5
frontpage store items list --json
frontpage store items get camp-hat
frontpage store settings set-fulfillment --mode pickup
frontpage store settings set-checkout --mode cart
frontpage store settings set-currency --currency usd
frontpage store connect --no-open
frontpage store status

Errors

What you did HTTP / CLI Typical message
--price-cents 49.99 or $4999 exit 2, no request $49.99 is 4999
Assign hats before creating the category 400 / exit 2 Create category hats first.
--add and --set together exit 2, no request Pass either --add, --remove, or --set
Unknown leftover flag exit 2, no request Unknown flag
No site selected exit 2 No site selected.
Bad or missing API key 401 / exit 4 Unauthorized
Viewer create or stock 403 / exit 4 This action requires editor access
Slug you do not belong to 404 / exit 4 Site not found.
Item id not on this site 404 / exit 4 Item not found.
Duplicate item id 409 / exit 3 That item id already exists on this site.
Discount not lower than regular 400 / exit 2 discountPriceCents must be lower than priceCents.
Subtract below zero 400 / exit 2 Cannot subtract stock below zero.
Delta on unlimited stock 400 / exit 2 Cannot apply delta to unlimited stock.
Archive the last variant 400 / exit 2 Cannot archive the last variant.
Change option names after create 400 / exit 2 Archive the item and create a new one
Rate limit 429 / exit 2 rate_limited. Honor Retry-After

Not in this CLI

  • No shop quote or shop checkout.
  • Stripe connect is frontpage store connect.
  • No site-page getShopItems helper. A public hats listing is data-fp-store-grid with data-category="hats". A public product URL is the one item.astro template.
  • No coupon codes.

If you are writing an agent

  • Read this page, frontpage help store, and get_module_docs({ module: "store" }) before inventing verbs, flags, or markup.
  • Unknown leftover flags fail closed.
  • Integer cents only. $49.99 is 4999.
  • --add 5 adds 5. --remove 5 removes 5. --set 20 means 20 left.
  • Create a category before assigning it. Unknown slug is 400, not an auto-create.
  • Checkout is store-wide (settings set-checkout / set_store_checkout). Omit checkout on create.
  • When they ask to add, show, or build a store: list items (create if empty), then write both templates plus CSS on the current page in that same turn, then connect Stripe once if needed, then set fulfillment.
  • When they ask for a product page: write one src/pages/products/item.astro (or shop) with data-fp-store-product binds. Do not use a leading underscore. Do not write one file per product. get_item is catalog work, not paste.
  • Do not write src/content/products/. Do not invent getShopItems on the site. Do not bake titles, prices, or Product JSON-LD prices.
  • Do not write a pay modal, /__shop/choose, or prompt(). Pay now extras open the cart drawer.
  • Do not paste a stripeAccountId.
  • Do not treat inbox rows as products.
  • Do not publish unless the user asked. Catalog writes do not change HTML. Markup writes need a compile; then publish only if they asked.

Also see