Stores
Store CRUD, slug/domain lookup, members & RBAC scopes, invites, custom domains, template preview and analytics surfaces.
A store is the tenancy root of Feeef — products, orders, shipping prices, integrations and finance all hang off a store id. This page covers the store resource itself: CRUD and lookup, the member/RBAC model, invites, custom domains, the draft template preview, and the small analytics surfaces (summary, chart, leaderboard). Conventions (pagination, filterator, errors) are documented in API conventions; webhook management lives in Webhooks.
At a glance
Store resource
| Method | Path | Description | Auth |
|---|---|---|---|
GET | /v1/stores | List stores — guests see ranked stores only; pass user={userId} for owned + member stores | Public |
POST | /v1/stores | Create a store (grants a 7-day Pro trial) | Bearer |
GET | /v1/stores/{id} | Show — {id} matches id, slug or custom domain name; by selects the column | Public |
PUT | /v1/stores/{id} | Update (multipart supported for logo files) | Bearer |
DELETE | /v1/stores/{id} | Delete | Bearer + scope store |
POST | /v1/stores/{id}/ensure-project | Backfill projectId on legacy stores (finance/inventory) | Bearer |
Analytics & settings surfaces
| Method | Path | Description | Auth |
|---|---|---|---|
GET | /v1/stores/{id}/summary | Order counts + revenue for a from/to range | Bearer + scope orders.read |
GET | /v1/stores/{id}/orders/status-counts | All-time order status counts (draft, pending, review, accepted, followup, processing, completed, cancelled; excludes soft-deleted) | Bearer + scope orders.read |
GET | /v1/stores/{id}/chart | Orders-over-time chart data | Bearer + scope orders.read |
GET | /v1/stores/{id}/leaderboard | Confirmer leaderboard (XP: 100 per delivery + 1 per assignment); from/to or period=today|week|month|all | Bearer |
GET | /v1/stores/{storeId}/analytics/lor | Lite orders report (8 UTC day buckets + total, cached 1h) | Bearer |
POST | /v1/stores/analytics/lor/batch | Batch lite orders report — { "storeIds": [...] } | Bearer |
Store settings (name, logo, configs, shipping defaults, decoration) are plain fields on the
store document — edit them with PUT /v1/stores/{id}. Integration configs are a separate
surface under /v1/stores/{id}/integrations/{key} gated by the
store.integrations scope.
Members
| Method | Path | Description | Auth |
|---|---|---|---|
POST | /v1/stores/{id}/members | Add member by email — { email, role, scopes?, expiredAt?, metadata? } | Bearer + scope store.members |
PUT | /v1/stores/{id}/members/{userId} | Update member role/scopes/active | Bearer + scope store.members |
DELETE | /v1/stores/{id}/members/{userId} | Remove member — owner removes anyone; a member may remove themselves (leave) | Bearer |
Invites
| Method | Path | Description | Auth |
|---|---|---|---|
POST | /v1/stores/{id}/invites | Create invite + send email (store owner only) | Bearer |
GET | /v1/stores/{id}/invites | List invites for a store | Bearer |
DELETE | /v1/stores/{id}/invites/{inviteId} | Revoke a pending invite (owner only) | Bearer |
GET | /v1/stores/{storeId}/invites/{inviteId} | Show invite — public shape is sanitized; full (incl. email) for owner/invitee | Public |
POST | /v1/stores/{storeId}/invites/{inviteId}/accept | Accept — caller's email must match; body { token } from the email link | Bearer |
POST | /v1/stores/{storeId}/invites/{inviteId}/decline | Invitee declines (invite becomes revoked) | Bearer |
GET | /v1/me/store-invites | Pending invites for the signed-in user (includes token) | Bearer |
Custom domains
| Method | Path | Description | Auth |
|---|---|---|---|
POST | /v1/stores/{id}/domain | Attach a domain (registers with Vercel, returns DNS instructions) | Bearer |
GET | /v1/stores/{id}/domain/status | Poll domain + DNS instruction status | Bearer |
POST | /v1/stores/{id}/domain/check-dns | Real public-DNS lookups per record; auto-verifies when all found | Bearer |
POST | /v1/stores/{id}/domain/verify | Trigger Vercel verification | Bearer |
GET | /v1/stores/{id}/domain/verification-check | Deep DNS/TXT + verification state (step-by-step UIs) | Bearer |
DELETE | /v1/stores/{id}/domain | Detach the domain (store + Vercel) | Bearer |
Template preview (draft themes)
| Method | Path | Description | Auth |
|---|---|---|---|
PUT | /v1/stores/{id}/template-preview | Upsert draft TemplateData — returns previewUrl, previewToken, expiresAt; never touches the live theme | Bearer |
GET | /v1/stores/{id}/template-preview | Fetch the draft with ?token={previewToken} | Public |
DELETE | /v1/stores/{id}/template-preview | Clear the draft | Bearer |
Used by the CLI feeef dev flow — see Templates for the data
model.
Roles & member scopes
Every store has one owner (store.userId) with full access. Additional members carry a
role and an optional scopes array:
| Role | Typical use |
|---|---|
editor | Manage catalog, orders, settings |
viewer | Read-only dashboards |
confermer | Order confirmation staff — sees assigned/queued orders, appears on the leaderboard |
Scopes narrow what a member (or an OAuth token — the vocabulary is shared, see
Scopes) can touch. Canonical values: store, store.read,
store.settings, store.integrations, store.members, orders, orders.read, products,
products.read, categories, categories.read, pages, pages.read,
product_landing_pages, product_landing_pages.read, shipping_prices,
shipping_prices.read, template_components, template_components.read, store_templates,
store_templates.read, finance, finance.read, inventory, inventory.read.
Hierarchy rules:
*andstoregrant everything.- A parent scope implies its
.readchild (orders⇒orders.read). store.integrationsadditionally implies the integration-gated modules (inventory*,finance*).- Empty/missing
scopeson a member = legacy full access (backward compatible). An empty scope list on an access token denies everything.
Look up a store by slug or domain
The show endpoint resolves {id} against id, slug, and custom domain in one query. Pass
by=slug or by=domain.name to force a column:
curl "https://api.feeef.org/v1/stores/my-shop?by=slug" \
-H "Accept: application/json"Responses are the store's public JSON: integration secrets are stripped for non-members, and
subscription/members are only present for members (and platform admins). Optional includes:
with=lor (lite orders report) and with=template (resolved theme document).
Add a member with scopes
curl -X POST "https://api.feeef.org/v1/stores/{storeId}/members" \
-H "Authorization: Bearer $FEEEF_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "teammate@example.com",
"role": "confermer",
"scopes": ["orders", "products.read"]
}'The user must already have a Feeef account (lookup is by email) — otherwise use an invite, which works for people without an account yet.
Invite flow
token.# Create (owner)
curl -X POST "https://api.feeef.org/v1/stores/{storeId}/invites" \
-H "Authorization: Bearer $FEEEF_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "email": "new-hire@example.com", "role": "editor" }'
# Accept (invitee, token from the email link or GET /v1/me/store-invites)
curl -X POST "https://api.feeef.org/v1/stores/{storeId}/invites/{inviteId}/accept" \
-H "Authorization: Bearer $INVITEE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "token": "..." }'Attach a custom domain
curl -X POST "https://api.feeef.org/v1/stores/{storeId}/domain" \
-H "Authorization: Bearer $FEEEF_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "shop.example.com" }'
# Then poll until verified
curl "https://api.feeef.org/v1/stores/{storeId}/domain/status" \
-H "Authorization: Bearer $FEEEF_TOKEN"The add call registers the domain with the hosting layer and returns the DNS records to create.
check-dns performs real public-DNS lookups and automatically runs verification once every
record resolves; status is the cheap polling endpoint. Domain names are unique across Feeef —
attaching a domain already used by another store fails with 400.
Notes
- Creation trial:
POST /v1/storesactivates a Pro trial (7 days, unlimited orders,subscription.metadata.trial = true). - Caching: list and show responses are cached server-side per user. Mutations bust the cache; role/scope edits invalidate the member-access cache immediately.
- Security block:
POST /v1/stores/{id}/integrations/security/block(Bearer) sets the same rate-limit keys used by public checkout for a given order's customer — a moderation tool for blocking abusive phone numbers/IPs. - Webhooks and integrations under
/v1/stores/{storeId}/integrations/...require thestore.integrationsscope and are documented in Webhooks.