Products endpoints

Products are read-only over the API today. You can list them (with filters) and fetch them individually. Write endpoints (create, update, delete) aren't exposed yet — manage products through the dashboard or CSV import for now.

GET /api/v1/products

List products in your company.

Required scope: products:read

Query parameters

ParamTypeDefaultDescription
limitinteger50Page size, capped at 100. See Pagination.
cursorstringPagination cursor from a previous response.
categorystringExact-match filter on category.
searchstringCase-insensitive substring match on name, sku, or category.
activetrue / falseFilter by isActive. Any other value is ignored.

Example

curl "https://distribu.app/api/v1/products?active=true&limit=100" \
  -H "Authorization: Bearer dk_..."

Response

{
  "data": [
    {
      "id": "clxx1234abc...",
      "name": "Widget Blue",
      "sku": "WDG-001",
      "description": "A small blue widget.",
      "category": "Hardware",
      "unit": "each",
      "price": 8.5,
      "stock": 42,
      "imageUrl": "https://images.distribu.app/products/widget-blue.png",
      "isActive": true,
      "createdAt": "2026-03-01T12:00:00.000Z",
      "updatedAt": "2026-04-10T09:30:00.000Z"
    }
  ],
  "pagination": {
    "hasMore": false,
    "nextCursor": null
  }
}

Sorted by createdAt descending — newest products first.

Notes on fields

  • price — Returned as a JSON number (not a string). Precision is two decimal places internally.
  • stock — Integer count. May be negative transiently during high-concurrency order bursts; rare but possible.
  • description, sku, imageUrl, category — All can be null.
  • unit — Always a string (each, case, lb, kg, whatever you've configured). Defaults to each for products that don't set it.

GET /api/v1/products/{id}

Fetch a single product by its ID.

Required scope: products:read

Example

curl https://distribu.app/api/v1/products/clxx1234abc... \
  -H "Authorization: Bearer dk_..."

Response

{
  "data": {
    "id": "clxx1234abc...",
    "name": "Widget Blue",
    "sku": "WDG-001",
    "description": "A small blue widget.",
    "category": "Hardware",
    "unit": "each",
    "price": 8.5,
    "stock": 42,
    "imageUrl": "https://...",
    "isActive": true,
    "createdAt": "2026-03-01T12:00:00.000Z",
    "updatedAt": "2026-04-10T09:30:00.000Z"
  }
}

Errors

  • 404Product not found. — The ID doesn't exist, or belongs to another company, or you passed a malformed ID.

Common patterns

Sync your catalog to a downstream system

Paginate through GET /api/v1/products daily. Store updatedAt with each synced record. You don't have a server-side ?since= filter today, so you'll need to do a full scan and compare timestamps locally.

Find a product by SKU

There's no direct SKU lookup endpoint, but search does a case-insensitive substring match across name, SKU, and category:

curl "https://distribu.app/api/v1/products?search=WDG-001&limit=5" \
  -H "Authorization: Bearer dk_..."

Then filter client-side by sku === "WDG-001" to discard partial matches (if your SKUs aren't unique substrings).

Handling stock

Stock is only accurate at the moment of the response. A concurrent order placement from the storefront or POST /api/v1/orders can decrement it between your read and your next write. Treat stock as a guidance value, not a reservation — actual stock enforcement happens server-side at order-creation time.

What's not here (yet)

  • POST /api/v1/products — Create a product.
  • PATCH /api/v1/products/{id} — Update fields.
  • PATCH /api/v1/products/{id}/stock — Adjust stock directly (useful for returns / receiving shipments).
  • DELETE /api/v1/products/{id} — Soft-delete (isActive = false).

All planned. Email us if any of these are blocking an integration and we'll prioritize.


Next: Orders endpoints.