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.
- At least one active item (
frontpage store items createormanage_store). Money is integer cents only. - 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">. - Header cart chrome:
data-fp-cart-openanddata-fp-cart-count, both withdata-fp-requires="store". - 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, anddata-fp-checkout. - Stripe:
frontpage store connect(admin key; finish logged-in in the browser). Do not paste astripeAccountId. - Fulfillment:
frontpage store settings set-fulfillmentafter 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 connectconnects 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 writesrc/content/products/. Do not paste astripeAccountId. A public product page is onesrc/pages/products/item.astro(orshop) withdata-fp-store-productbinds. Frontpage fills it from the live Store item. Do not type titles or prices into one file per product. Do not invent a site-pagegetShopItemshelper.
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. |
-
--siteis a subdomain slug only (example:acme). Not a custom domain, notacme.frontpage.host, not a site UUID. See Flags. -
A JSON body that includes
siteIdis 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
-
--site <slug>,-s <slug>, or--site=<slug>on this command. Wins for this process only. -
Otherwise the slug from
frontpage use <slug>in this folder. -
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-hator--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 ornullfor unlimited) andchange. nullstock is unlimited.--add/--removeon unlimited is an error.- You cannot subtract below zero. Use
--set 0to mark sold out. --addand--settogether 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 quoteorshop checkout. - Stripe connect is
frontpage store connect. - No site-page
getShopItemshelper. A public hats listing isdata-fp-store-gridwithdata-category="hats". A public product URL is the oneitem.astrotemplate. - No coupon codes.
If you are writing an agent
- Read this page,
frontpage help store, andget_module_docs({ module: "store" })before inventing verbs, flags, or markup. - Unknown leftover flags fail closed.
- Integer cents only.
$49.99is4999. --add 5adds 5.--remove 5removes 5.--set 20means 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). Omitcheckouton 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) withdata-fp-store-productbinds. Do not use a leading underscore. Do not write one file per product.get_itemis catalog work, not paste. - Do not write
src/content/products/. Do not inventgetShopItemson the site. Do not bake titles, prices, orProductJSON-LD prices. - Do not write a pay modal,
/__shop/choose, orprompt(). 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
- Content & settings for inbox
- Rate limits
- Command reference