Deploy & hosting
Cartbase builds and hosts your storefront. You send the app's source files — one snapshot of the whole project — and Cartbase turns it into a running site: first on a permanent preview URL, then, when you press Publish (or call the publish endpoint), on the store's live domain. You never touch build servers, DNS, or hosting configuration; the platform provisions all of it on your first deploy.
Two URLs exist per store, both created automatically:
| Surface | URL | What serves there |
|---|---|---|
| Preview | https://preview--{store-slug}.cartbase.net |
The newest deploy, always — the link never changes |
| Live | https://{store-slug}.cartbase.net |
The last published deploy, and nothing else |
A failed build never replaces what's live or what's on preview — the previous successful deploy keeps serving. Custom domains for the live site attach in the admin (Settings → Domains) and don't change anything on this page.
These are Admin API endpoints — they operate the store, so they
authenticate with an admin session (sign-in cookie), not the storefront
headers the rest of this corpus uses. In practice you'll deploy through
the admin UI (Storefront in the sidebar), the Cartbase CLI, or an
agent connection — all three are wrappers over exactly these calls.
Users who belong to several stores name the target store with an
x-barter-store: <client-id> header.
What a deploy is
A deploy is a full snapshot: the complete file tree of your storefront app, uploaded in one request. Snapshot semantics cut both ways — a file you stop sending is removed from the deployed site. There is no partial-update mode; your working directory is the truth, every time.
Bundle rules (enforced server-side; a violation fails the whole deploy
with a readable 400 before anything is built):
- ≤ 1500 files, ≤ 3 MB per file, ≤ 25 MB total (base64-decoded sizes)
package.jsonmust exist at the bundle root — the app must be a buildable Next.js project (next build)- File content travels base64-encoded (
content_base64), so binary assets — images, fonts — work - Never include:
node_modules/(dependencies install at build time),.next/(build output is generated),.git/, any.env*file - Not allowed in storefront bundles:
middleware.*,app/api/orpages/api/routes,vercel.json— a storefront is pages plus the@cartbase/storefrontpackage; server seams and hosting config are platform territory. The CORS proxy from the runbook is anext.configrewrite, which is fine.
Error codes you can hit: empty_bundle, bundle_too_large,
invalid_bundle_file, invalid_path, blocked_path, duplicate_path,
invalid_encoding, file_too_large, missing_package_json.
Environment — provided, not configured
Hosted storefronts receive the three runbook inputs automatically at
build time; do not put them in the bundle (.env* files are blocked
anyway):
| Variable | Value |
|---|---|
NEXT_PUBLIC_BARTER_URL |
The store's API origin |
NEXT_PUBLIC_BARTER_CLIENT_ID |
The store's client id |
NEXT_PUBLIC_BARTER_PUBLISHABLE_KEY |
The store's publishable key, when one exists |
Only these public values ever reach a storefront build — secret keys are never injected, so code that expects one is a design error.
Deploy to preview
POST /api/admin/storefront/deploys — the one ingestion door. The first
call on a store also provisions its hosting (takes a few extra seconds);
every later call is just a deploy.
# doc-noexec — admin-session auth; run from an authenticated context.
curl -s -X POST "$BASE/api/admin/storefront/deploys" \
-H "content-type: application/json" \
-d '{
"files": [
{ "path": "package.json", "content_base64": "<base64>" },
{ "path": "app/layout.js", "content_base64": "<base64>" },
{ "path": "app/page.js", "content_base64": "<base64>" }
],
"message": "homepage copy update"
}'Response 201:
{
"deploy": {
"id": "sfd_01J…",
"target": "preview",
"status": "building",
"url": "https://preview--my-store.cartbase.net",
"message": "homepage copy update",
"file_count": 3,
"created_at": "2026-08-03T22:41:00.000Z"
}
}message (≤ 500 chars) is your label in the deploy history — write what
changed. source (api | cli | mcp | admin | agent) tags where
the deploy came from; omit it unless you're building tooling.
Statuses move queued → building → ready (or failed / canceled).
Poll the overview endpoint until the deploy you created is ready, then
open the preview URL. Builds of a small app land in roughly a minute;
failed rows carry a readable error_message.
Overview — hosting status + deploy history
GET /api/admin/storefront returns the hosting record and the deploy
ledger, newest first:
# doc-noexec — admin-session auth.
curl -s "$BASE/api/admin/storefront"{
"storefront": {
"id": "sfp_01J…",
"status": "ready",
"production_url": "https://my-store.cartbase.net",
"preview_url": "https://preview--my-store.cartbase.net",
"last_error": null,
"created_at": "…"
},
"deploys": [
{
"id": "sfd_01J…",
"target": "preview",
"status": "ready",
"message": "homepage copy update",
"url": "https://preview--my-store.cartbase.net",
"source": "cli",
"file_count": 3,
"total_bytes": 1930,
"error_message": null,
"promoted_deploy_id": null,
"created_at": "…",
"updated_at": "…"
}
]
}storefront is null until the store's first deploy. status: "provisioning" / "failed" (with last_error) describe hosting setup,
not builds; a failed provisioning resumes automatically on the next
deploy attempt.
Publish — and rollback
POST /api/admin/storefront/publish promotes a ready preview deploy
to the live domain. This is the only path to production — nothing
deploys straight to live, no matter who's asking.
# doc-noexec — admin-session auth.
# Bare POST publishes the newest ready preview:
curl -s -X POST "$BASE/api/admin/storefront/publish"
# Naming an older deploy id IS the rollback mechanism:
curl -s -X POST "$BASE/api/admin/storefront/publish" \
-H "content-type: application/json" \
-d '{ "deploy_id": "sfd_01H…older…" }'Response 201 is a new ledger row with target: "production" and
promoted_deploy_id pointing at the preview it promoted. Publishing is
near-instant — the snapshot was already built; the live domain switches
to it without a rebuild.
Rollback is not a separate feature: publish any earlier ready deploy
from the history and the live site is that snapshot again. Roll forward
the same way.
Workflow summary
- Build locally against your store (the runbook).
- Deploy → check the permanent preview URL.
- Iterate — every deploy replaces preview, live is untouched.
- Publish when it's right; the deploy history is your undo button.