Storefront contract
The customer’s static shop — catalog from GET /api/v1/menu, cart of itemId plus quantity, publishable key without a secret.
The storefront contract (storefront-contract@v1) describes a shop the customer deploys themselves. Delieta does not host it. The artifact is a static export: the catalog bakes at build, the cart lives in localStorage, checkout is a marked seam and does not send POST.
Two env vars, no public prefix
DELIETA_API_URL=https://delieta.pl
DELIETA_PUBLISHABLE_KEY=dlt_live_…
The origin alone — no /api/v1 suffix, no trailing slash. The key is used at build for GET /api/v1/menu. No NEXT_PUBLIC_DELIETA.
Missing env → a “set up the catalog” page, not an empty shop pretending to be a store. Env is there, fetch fails → a “catalog unavailable” page, not a menu with zero items that looks like a deploy.
The publishable key is incapable, not “hidden”
kind: publishable holds only catalog:read (menu:read is an alias). It works in public HTML only because the key cannot read orders, drivers or customers.
A secret key (orders:write, eta:quote) does not go into the browser, the shop repo or Pages. dlt_test_… is also not “a safe secret in HTML” — it is the wrong kind.
The key decides the tenant. Do not send companyId.
Three surfaces
| Surface | Today | Not yet |
|---|---|---|
| Catalog | GET {origin}/api/v1/menu with a publishable Bearer. Grosz, PLN. Unavailable items omitted. | Refresh without a rebuild |
| Cart | { itemId, quantity }[] in localStorage. On-screen total from the baked catalog. | Modifier choice as separate line fields |
| Checkout | /checkout/ shows the cart. It does not POST. | The same cart goes to Delieta; the server prices; a payment URL comes back |
The customer sends what (ids and quantities), never how much. A price typed in the browser is not contractual.
flowchart TD env["Two env: origin and publishable key"] --> build["Build bakes GET /menu"] build --> html["Static HTML"] html --> cart["Cart: itemId and quantity"] cart --> seam["/checkout/ shows the cart, does not POST"] secret["Secret key"] -.->|"never in this tree"| html
The template you copy is a static shop export: catalog from the menu, cart in the browser, checkout as a seam. Delieta does not host that tree.
Money and errors
Whole grosz. Format at the display edge. Partner API errors are application/problem+json; on a static export you most often see them at build. Branch on code: unauthorized, scope_required, rate_limited.
Full operations the shop uses today and does not yet call are in Partner API.

