Storefront API

Read-only REST API for products, categories, and promotional offers. All endpoints return JSON.

Base URL

https://shop.innosate.com

Authentication

Send your API key as a Bearer token in the Authorization header. Keys are issued by an admin from the API keys page and shown only once at creation. Each key has one or more scopes:storefront:read (read products/categories/offers),products:write (bulk create / stock updates),orders:read (fetch synced-product orders),orders:write (mark orders as synced).

Authorization: Bearer lvbl_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Errors

Errors return a JSON body with an error.message and the HTTP status mirrored in the body.

{ "error": { "message": "Invalid API key", "code": null }, "status": 401 }
  • 401 — missing/invalid/revoked key
  • 403 — key lacks required scope
  • 404 — resource not found
  • 500 — server error

Endpoints

GET/api/public/v1/productsscope: storefront:read

List active, approved products with pagination, search, and category filter.

Query parameters

  • search (string) Free-text match on name or description.
  • category (string) Category slug to filter by.
  • limit (number) Page size (1–100). Default 20.
  • offset (number) Pagination offset. Default 0.
  • sort (string) newest | oldest | price_asc | price_desc | name_asc. Default newest.

Example request

curl -H "Authorization: Bearer lvbl_..." \
  "https://shop.innosate.com/api/public/v1/products?limit=20&sort=price_asc"

Example response

{
  "data": [
    {
      "id": "uuid",
      "slug": "marble-desk-lamp",
      "name": "Marble Desk Lamp",
      "description": "…",
      "image_url": "https://…",
      "mrp": 4999,
      "sale_price": 3499,
      "stock_quantity": 12,
      "category_id": "uuid",
      "created_at": "2026-06-01T10:00:00Z"
    }
  ],
  "pagination": { "limit": 20, "offset": 0, "total": 137 }
}
GET/api/public/v1/products/{slug}scope: storefront:read

Get a single product by slug, including images and variants.

Example request

curl -H "Authorization: Bearer lvbl_..." \
  "https://shop.innosate.com/api/public/v1/products/marble-desk-lamp"

Example response

{
  "data": {
    "id": "uuid",
    "slug": "marble-desk-lamp",
    "name": "Marble Desk Lamp",
    "description": "…",
    "details": "…",
    "image_url": "https://…",
    "video_url": null,
    "mrp": 4999,
    "sale_price": 3499,
    "stock_quantity": 12,
    "gst_pct": 18,
    "size_chart": null,
    "category_id": "uuid",
    "images": [{ "url": "https://…", "alt": "…", "sort_order": 0 }],
    "variants": [
      {
        "id": "uuid",
        "size": "M",
        "color": "White",
        "color_hex": "#ffffff",
        "sku": "MDL-M-WHT",
        "mrp": 4999,
        "sale_price": 3499,
        "stock_quantity": 5
      }
    ]
  }
}
GET/api/public/v1/categoriesscope: storefront:read

List all storefront categories ordered by display order.

Example request

curl -H "Authorization: Bearer lvbl_..." \
  "https://shop.innosate.com/api/public/v1/categories"

Example response

{
  "data": [
    { "id": "uuid", "name": "Lighting", "slug": "lighting", "image_url": "https://…", "sort_order": 0 }
  ]
}
GET/api/public/v1/offersscope: storefront:read

List active promotional offers.

Example request

curl -H "Authorization: Bearer lvbl_..." \
  "https://shop.innosate.com/api/public/v1/offers"

Example response

{
  "data": [
    {
      "id": "uuid",
      "slug": "summer-sale",
      "title": "Summer sale",
      "subtitle": "Up to 40% off",
      "badge_text": "HOT",
      "image_url": "https://…",
      "rule_kind": "category",
      "rule_value": "lighting",
      "sort_order": 0
    }
  ]
}
POST/api/public/v1/products/bulkscope: products:write

Bulk create or update products. Each product is matched by its `sku` — existing rows are updated, new ones are created. The SKU is stored on the product as the external reference for later stock/price updates.

Request body

{
  "products": [
    {
      "sku": "MDL-001",                  // required, unique external reference
      "name": "Marble Desk Lamp",        // required
      "slug": "marble-desk-lamp",        // optional, auto-derived if omitted
      "description": "…",
      "details": "…",
      "category_id": "uuid",             // either category_id or category_slug
      "category_slug": "lighting",
      "mrp": 4999,
      "sale_price": 3499,
      "min_sale_price": 3000,
      "stock_quantity": 12,
      "gst_pct": 18,
      "image_url": "https://…",
      "video_url": null,
      "vendor_id": "uuid",
      "vendor_cost": 2500,
      "b2b_price": 3200,
      "b2b_min_qty": 5,
      "is_active": true,
      "approval_status": "approved",
      // Optional inline images — uploaded to Cloud storage and linked.
      "image_base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA...",
      "image_content_type": "image/jpeg",      // required if image_base64 is raw (no data: prefix)
      "images": [
        // Each entry may be:
        //  • a data URL string ("data:image/png;base64,...")
        //  • a raw base64 string (requires content_type on the object form below)
        //  • a public https URL string
        //  • an object: { base64 | data | url, content_type?, alt?, sort_order? }
        { "base64": "/9j/4AAQSkZJ...", "content_type": "image/jpeg", "alt": "Front", "sort_order": 0 },
        { "url": "https://cdn.example.com/lamp-side.jpg", "alt": "Side", "sort_order": 1 }
      ]
    }
  ]
}

Example request

curl -X POST -H "Authorization: Bearer lvbl_..." \
  -H "Content-Type: application/json" \
  -d '{ "products": [{
        "sku": "MDL-001",
        "name": "Marble Desk Lamp",
        "category_slug": "lighting",
        "mrp": 4999,
        "sale_price": 3499,
        "image_base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA..."
      }] }' \
  "https://shop.innosate.com/api/public/v1/products/bulk"

Example response

{
  "summary": { "total": 1, "created": 1, "updated": 0, "errors": 0 },
  "results": [
    { "sku": "MDL-001", "status": "created", "id": "uuid", "images_uploaded": 1 }
  ]
}
POST/api/public/v1/products/stockscope: products:write

Update stock quantity, sale price, and/or MRP for one or many SKUs. Looks up the SKU first on `products`, then on `product_variants`.

Request body

{
  "updates": [
    { "sku": "MDL-001", "stock_quantity": 25, "sale_price": 3299, "mrp": 4999 }
  ]
}

Example request

curl -X POST -H "Authorization: Bearer lvbl_..." \
  -H "Content-Type: application/json" \
  -d '{ "updates": [{ "sku": "MDL-001", "stock_quantity": 25 }] }' \
  "https://shop.innosate.com/api/public/v1/products/stock"

Example response

{
  "summary": { "total": 1, "updated": 1, "not_found": 0, "errors": 0 },
  "results": [
    { "sku": "MDL-001", "status": "updated", "target": "product", "id": "uuid" }
  ]
}
GET/api/public/v1/orders/unsyncedscope: orders:read

Fetch orders that contain at least one item not yet marked as externally synced AND whose product has a SKU set (i.e. was synced into the external system). Returns each order with only its eligible items.

Query parameters

  • limit (number) Max orders to return (1–200). Default 50.
  • status (string) Optional order status filter (e.g. paid, packed, dispatched).

Example request

curl -H "Authorization: Bearer lvbl_..." \
  "https://shop.innosate.com/api/public/v1/orders/unsynced?limit=50"

Example response

{
  "data": [
    {
      "id": "order-uuid",
      "customer_name": "Asha",
      "customer_phone": "+91…",
      "shipping_address": "…",
      "shipping_city": "Bengaluru",
      "shipping_state": "KA",
      "shipping_pincode": "560001",
      "status": "paid",
      "subtotal": 3499,
      "gst_total": 629,
      "discount": 0,
      "total": 4128,
      "created_at": "2026-06-17T05:00:00Z",
      "items": [
        {
          "id": "item-uuid",
          "product_id": "uuid",
          "variant_id": null,
          "sku": "MDL-001",
          "product_name": "Marble Desk Lamp",
          "variant_label": null,
          "quantity": 1,
          "unit_price": 3499,
          "line_total": 3499,
          "gst_pct": 18,
          "gst_amount": 629
        }
      ]
    }
  ],
  "count": 1
}
POST/api/public/v1/orders/syncscope: orders:write

Mark order items as synced so the next call to /orders/unsynced will not return them. Pass `order_item_ids` to mark specific items, or `all_items: true` to mark every item in the order. When every item in an order is synced, the order itself is marked synced.

Request body

{
  "orders": [
    { "order_id": "order-uuid", "all_items": true },
    { "order_id": "order-uuid-2", "order_item_ids": ["item-uuid-a", "item-uuid-b"] }
  ]
}

Example request

curl -X POST -H "Authorization: Bearer lvbl_..." \
  -H "Content-Type: application/json" \
  -d '{ "orders": [{ "order_id": "order-uuid", "all_items": true }] }' \
  "https://shop.innosate.com/api/public/v1/orders/sync"

Example response

{
  "summary": { "total": 1, "orders_marked": 1, "items_marked": 2, "errors": 0 },
  "results": [
    { "order_id": "order-uuid", "items_marked": 2, "order_marked": true }
  ]
}

Rate limits & caching

No fixed rate limit today; please cache responses on your side and avoid polling more than once per minute. We may introduce limits without notice for abusive callers.