.dev
REST API

Products

Product CRUD with variants, offers and addons, category tree operations, and per-product analytics.

Products are store-scoped catalog documents. Variants, offers and addons are embedded on the product — there are no separate variant/offer resources to manage. Reads are public (the storefront consumes them directly); writes require a bearer token with product access on the store. List conventions (pagination, flat filters, the filterator) are covered in API conventions.

At a glance

Products

MethodPathDescriptionAuth
GET/v1/productsList — store_id (or store_ids[]), q, status, category_id, price_min/price_max, in_stock, order_by=field:dir, filteratorPublic
GET/v1/products/randomRandom published products (limit, default 12)Public
GET/v1/products/{id}Show — by selects the lookup column (e.g. by=slug); merchants get a live row incl. integrationsDataPublic
POST/v1/productsCreate (multipart supported for image files)Bearer + scope products
PUT/v1/products/{id}UpdateBearer + scope products
DELETE/v1/products/{id}Soft delete — members still resolve deleted products on historical ordersBearer + scope products

Per-product analytics

MethodPathDescriptionAuth
GET/v1/stores/{storeId}/products/{productId}/sellsSales chart for the last 7 days (date → count)Bearer + scope orders.read
GET/v1/stores/{storeId}/products/{productId}/reportDetailed report — sales/revenue/profit for today, yesterday (hourly), week, month (daily)Bearer + scope orders.read
GET/v1/stores/{storeId}/products/{productId}/analytics/lorLite orders report (8 UTC day buckets + total)Bearer

Categories

Categories form a tree per store (parentId links). List endpoints take flat params only (store_id, parent_id, q) — the filterator is not supported here.

MethodPathDescriptionAuth
GET/v1/categoriesFlat list for a store — store_id, optional parent_id (null for roots), qPublic
GET/v1/categories/treeFull nested tree for a store — store_idPublic
GET/v1/categories/{id}Show one categoryPublic
POST/v1/categoriesCreate — { name, storeId, parentId?, photoUrl?, ... }Bearer + scope categories
PUT/v1/categories/{id}Update (move a subtree by changing parentId)Bearer + scope categories
DELETE/v1/categories/{id}DeleteBearer + scope categories

Feedbacks

Feedbacks are platform feedback (bug reports and feature requests from merchants about Feeef itself), scoped to the submitting user — they are not product reviews. Storefront product reviews are not a dedicated API resource today.

MethodPathDescriptionAuth
GET/v1/feedbacksList own feedback (admins see all; supports filterator, status, priority, q, date filters)Bearer
GET/v1/feedbacks/{id}Show oneBearer
POST/v1/feedbacksCreate — { title, details?, priority?, tags?, appVersion? }; multipart files become attachmentsBearer
PUT/v1/feedbacks/{id}Update (admins may change status with history)Bearer
DELETE/v1/feedbacks/{id}DeleteBearer
POST/v1/feedbacks/{id}/commentsAdd a comment — { comment }Bearer

Variants, offers, addons

Three embedded structures drive pricing (all optional):

  • variant — a single root group ({ name, options[], view?, required? }). Each option can override price, discount, stock, sku, and can nest another group via child, producing a tree (e.g. Color → Size). Order items address a leaf with a slash-joined variantPath such as red/xl.
  • offers[] — quantity bundles: { code, title, price?, minQuantity?, maxQuantity?, freeShipping? }. Order items reference them by offerCode. forceOffer: true on the product requires customers to keep an offer selected; defaultOfferCode pre-selects (and with forceOffer locks) one.
  • addons[] — optional extras purchasable alongside the product; order items carry them as a name → quantity map.

The server recalculates all prices from these structures during order calculate/submit — client-sent prices are never trusted.

List products for a storefront grid

curl "https://api.feeef.org/v1/products?store_id=STORE_ID&in_stock=1&price_max=5000&order_by=sold:desc&limit=12" \
  -H "Accept: application/json"

For arbitrary conditions pass a filterator — see API conventions.

Create a product with variants and offers

curl -X POST "https://api.feeef.org/v1/products" \
  -H "Authorization: Bearer $FEEEF_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "storeId": "STORE_ID",
    "name": "Classic Tee",
    "slug": "classic-tee",
    "price": 2200,
    "media": ["https://storage.feeef.net/.../tee.webp"],
    "variant": {
      "name": "Color",
      "options": [
        { "name": "Red", "type": "color", "value": "#f00",
          "child": { "name": "Size", "options": [
            { "name": "M" }, { "name": "L", "price": 2400 }
          ] } }
      ]
    },
    "offers": [
      { "code": "x2", "title": "Buy 2", "price": 2000, "minQuantity": 2, "freeShipping": true }
    ]
  }'

Upload images first via POST /v1/services/storage/upload (see API conventions) and reference the returned URLs in mediamedia is a plain array of URL strings.

Fetch a product by slug

curl "https://api.feeef.org/v1/products/classic-tee?by=slug"

Guest reads may be served from a short-lived cache; authenticated store members always get a live row (including the secret integrationsData — guests only ever see publicIntegrationsData).

Category tree

curl "https://api.feeef.org/v1/categories/tree?store_id=STORE_ID"

/tree returns root nodes with recursive children arrays — one request instead of one per level. Product counts are not included; filter products by category_id instead.

Notes

  • Webhooks: product create / update / delete fire productCreated, productUpdated and productDeleted — same envelope as orders, with the entity under data.product. See Webhooks. Inbound connector writes are skipped to avoid echo loops.
  • Slugs are unique per store; both products and stores support ?by=slug lookups.
  • Status: products carry a status (e.g. published/draft) — unpublished products stay out of public lists but remain fetchable by id for their store members.
  • Views/sold counters update server-side (show tracks views; orders update sold).
  • Promo codes (/v1/promos) are subscription discount codes, not product coupons — see Payments. Product-level discounts live on the product/variant/offer discount fields.
  • Related lists for landing pages and templates: see Templates.

On this page