.dev
REST API

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

MethodPathDescriptionAuth
GET/v1/storesList stores — guests see ranked stores only; pass user={userId} for owned + member storesPublic
POST/v1/storesCreate 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 columnPublic
PUT/v1/stores/{id}Update (multipart supported for logo files)Bearer
DELETE/v1/stores/{id}DeleteBearer + scope store
POST/v1/stores/{id}/ensure-projectBackfill projectId on legacy stores (finance/inventory)Bearer

Analytics & settings surfaces

MethodPathDescriptionAuth
GET/v1/stores/{id}/summaryOrder counts + revenue for a from/to rangeBearer + scope orders.read
GET/v1/stores/{id}/orders/status-countsAll-time order status counts (draft, pending, review, accepted, followup, processing, completed, cancelled; excludes soft-deleted)Bearer + scope orders.read
GET/v1/stores/{id}/chartOrders-over-time chart dataBearer + scope orders.read
GET/v1/stores/{id}/leaderboardConfirmer leaderboard (XP: 100 per delivery + 1 per assignment); from/to or period=today|week|month|allBearer
GET/v1/stores/{storeId}/analytics/lorLite orders report (8 UTC day buckets + total, cached 1h)Bearer
POST/v1/stores/analytics/lor/batchBatch 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

MethodPathDescriptionAuth
POST/v1/stores/{id}/membersAdd member by email — { email, role, scopes?, expiredAt?, metadata? }Bearer + scope store.members
PUT/v1/stores/{id}/members/{userId}Update member role/scopes/activeBearer + scope store.members
DELETE/v1/stores/{id}/members/{userId}Remove member — owner removes anyone; a member may remove themselves (leave)Bearer

Invites

MethodPathDescriptionAuth
POST/v1/stores/{id}/invitesCreate invite + send email (store owner only)Bearer
GET/v1/stores/{id}/invitesList invites for a storeBearer
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/inviteePublic
POST/v1/stores/{storeId}/invites/{inviteId}/acceptAccept — caller's email must match; body { token } from the email linkBearer
POST/v1/stores/{storeId}/invites/{inviteId}/declineInvitee declines (invite becomes revoked)Bearer
GET/v1/me/store-invitesPending invites for the signed-in user (includes token)Bearer

Custom domains

MethodPathDescriptionAuth
POST/v1/stores/{id}/domainAttach a domain (registers with Vercel, returns DNS instructions)Bearer
GET/v1/stores/{id}/domain/statusPoll domain + DNS instruction statusBearer
POST/v1/stores/{id}/domain/check-dnsReal public-DNS lookups per record; auto-verifies when all foundBearer
POST/v1/stores/{id}/domain/verifyTrigger Vercel verificationBearer
GET/v1/stores/{id}/domain/verification-checkDeep DNS/TXT + verification state (step-by-step UIs)Bearer
DELETE/v1/stores/{id}/domainDetach the domain (store + Vercel)Bearer

Template preview (draft themes)

MethodPathDescriptionAuth
PUT/v1/stores/{id}/template-previewUpsert draft TemplateData — returns previewUrl, previewToken, expiresAt; never touches the live themeBearer
GET/v1/stores/{id}/template-previewFetch the draft with ?token={previewToken}Public
DELETE/v1/stores/{id}/template-previewClear the draftBearer

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:

RoleTypical use
editorManage catalog, orders, settings
viewerRead-only dashboards
confermerOrder 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:

  • * and store grant everything.
  • A parent scope implies its .read child (ordersorders.read).
  • store.integrations additionally implies the integration-gated modules (inventory*, finance*).
  • Empty/missing scopes on 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

Owner creates the invite; Feeef emails the invitee a link containing a secret token.
The invitee signs in (or signs up with the same email) and accepts with the token.
Acceptance creates the store member with the role/scopes from the invite.
# 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/stores activates 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 the store.integrations scope and are documented in Webhooks.

On this page