Authentication & conventions
Calling the REST API without the SDK: the three auth modes as raw headers, plus the conventions every endpoint shares.
Everything the SDK does over HTTP you can do from any language — this page is the auth matrix and the house rules.
Base URL: https://api.getenvie.com · everything under /v1.
The three auth modes, as requests
Liquid storefronts — the app proxy (no key)
Theme code calls the shop's own domain under /apps/envie/*; Shopify signs the request and
forwards it to Envie. Never call api.getenvie.com from a Liquid theme — only the proxy carries the
signature.
# From theme JS this is just a same-origin fetch:
curl "https://your-store.myshopify.com/apps/envie/wishlist"Headless browsers — public key
Two headers, browser-safe, anonymous scope only:
curl "https://api.getenvie.com/v1/storefront/wishlist?aid=1e5f4b6a-0000-4000-8000-000000000000" \
-H "X-Envie-Shop: your-store.myshopify.com" \
-H "X-Envie-Public-Key: env_pk_your_store_xxxxxxxx"aid is the shopper's anonymous id — a UUID your code mints once and keeps in the browser. Your
storefront's origin must be declared under Envie → Developers (CORS is enforced against that
list). A public key can never act as a logged-in customer.
Servers — private key
One header, full shop scope, server-side only:
curl "https://api.getenvie.com/v1/shop" \
-H "Authorization: Bearer env_sk_your_key"Keys are created in Envie → Developers, shown once, revocable in one click. If one ever reaches a browser or a repo, rotate it.
Which endpoints take which mode
| Surface | Paths | Modes |
|---|---|---|
| Storefront | /v1/storefront/* | proxy · public · private |
| Private | everything else (/v1/shop, /v1/lists, …) | private only |
The generated reference marks the accepted modes per endpoint, and its playground sends real requests from your browser.
Conventions (all endpoints)
- Ids are strings. Shopify's 64-bit ids overflow JSON numbers, so they travel as decimal
strings:
"productId": "8123456789012". Envie's own ids carry a type prefix (lst_…,itm_…) — a wrong-prefix id is a400, not a mysterious404. - Times are ISO-8601 UTC.
2026-08-05T14:32:11.000Z. - Pagination is cursor-based.
?cursor=&limit=(limit ≤ 100); responses carrypageInfo.nextCursor,nullwhen done. No page numbers, no offsets. - Idempotency. Send an
Idempotency-Keyheader (any unique string) on any POST; a retry with the same key within 24 h returns the original result instead of repeating the work. - Errors are problem documents. RFC 9457
application/problem+json, with a stabletypeURL that resolves to an explanation. Branch ontype, never on message text. - Rate limits. 60/min storefront, 600/min private; budget on
X-RateLimit-Remaining-*headers,429carriesRetry-After. Details in limits. - Stability. Additive-only within
/v1: fields are never removed or repurposed, deprecations are announced at least 90 days ahead (changelog +Deprecationheader), breaking changes only ever in a/v2.
A complete round trip
# Save an item (public mode), idempotently:
curl -X POST "https://api.getenvie.com/v1/storefront/wishlist/toggle" \
-H "X-Envie-Shop: your-store.myshopify.com" \
-H "X-Envie-Public-Key: env_pk_your_store_xxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 5f3c1a2b-save-hoodie" \
-d '{
"productId": "8123456789012",
"handle": "soft-hoodie",
"anonymousId": "1e5f4b6a-0000-4000-8000-000000000000"
}'
# → { "added": true, "count": 1, "itemId": "itm_…" }The machine-readable version of this page is the spec itself:
/openapi.json.