SegOps AIDocs

PIM API Reference

Base path: `/api/pim/`

Authentication: Bearer token (JWT) or API key (X-API-Key header).


Product Schema#

GET /api/pim/schema/#

Returns the tenant's current product schema. Returns empty fields list if no schema has been defined yet.

Response

json
{
  "id": 1,
  "fields": [
    { "name": "brand", "type": "string", "required": false, "description": "Product brand" },
    { "name": "price", "type": "price", "required": false, "description": "Sale price" },
    { "name": "tags", "type": "array", "required": false, "description": "Product tags" }
  ],
  "default_currency": "USD",
  "default_locale": "en_US",
  "created_at": "2026-05-03T10:00:00Z",
  "updated_at": "2026-05-03T10:00:00Z"
}

PUT /api/pim/schema/#

Replace the tenant's product schema.

Request body

json
{
  "fields": [
    { "name": "brand", "type": "string", "required": false, "description": "Brand name" }
  ],
  "default_currency": "EUR",
  "default_locale": "fr_FR"
}

Response — updated ProductSchema object (same shape as GET)


Products#

GET /api/pim/products/#

List products with pagination. Supports dynamic field filtering and text search.

Query params

ParamTypeDescription
qstringText search on name (case-insensitive contains)
<field_name>stringFilter by data->>'field_name' exact match (e.g. ?brand=Nike)
pageintPage number (default 1)
page_sizeintResults per page (default 50, max 200)

Response

json
{
  "count": 1024,
  "next": "/api/pim/products/?page=2",
  "previous": null,
  "results": [
    {
      "id": 1,
      "sku": "SHOE-001",
      "name": "Air Max 90",
      "data": { "brand": "Nike", "price": "129.99", "tags": ["running", "casual"] },
      "source": "csv",
      "ai_readability_score": null,
      "created_at": "2026-05-03T10:00:00Z",
      "updated_at": "2026-05-03T10:00:00Z"
    }
  ]
}

GET /api/pim/products/{id}/#

Retrieve a single product by internal ID.

PATCH /api/pim/products/{id}/#

Update product name and/or data.

Request body

json
{ "name": "Updated Name", "data": { "brand": "Adidas", "price": "99.99" } }

DELETE /api/pim/products/{id}/#

Delete a product.

POST /api/pim/products/#

Create a single product.

Request body

json
{ "sku": "SHOE-002", "name": "Stan Smith", "data": { "brand": "Adidas" } }

POST /api/pim/products/bulk/#

Bulk upsert by SKU. Creates new products or updates existing ones.

Request body

json
{
  "products": [
    { "sku": "SHOE-001", "name": "Air Max 90", "brand": "Nike", "price": "129.99" },
    { "sku": "SHOE-002", "name": "Stan Smith", "brand": "Adidas", "price": "89.99" }
  ]
}

Fields beyond sku and name are stored in data.

Response

json
{ "created": 1, "updated": 1 }

Facets#

GET /api/pim/facets/#

Returns distinct values for string and array schema fields. Useful for building filter UI.

Response

json
{
  "brand": ["Adidas", "Nike", "Puma"],
  "category": ["footwear", "apparel"],
  "tags": ["casual", "running", "sport"]
}

Values are limited to 100 per field, computed from the first 1,000 products.


CSV Import#

POST /api/pim/import/csv/#

Upload a CSV file. Returns AI-suggested schema and column mapping synchronously (< 3s).

Requestmultipart/form-data

FieldTypeRequired
fileFileYes — UTF-8 CSV

Response

json
{
  "job_id": 42,
  "headers": ["Product ID", "Title", "Brand", "Sale Price", "Stock"],
  "sample_rows": [
    { "Product ID": "SHOE-001", "Title": "Air Max 90", "Brand": "Nike", "Sale Price": "129.99", "Stock": "45" }
  ],
  "suggested_schema": [
    { "name": "brand", "type": "string", "required": false, "description": "Product brand" },
    { "name": "price", "type": "price", "required": false, "description": "Sale price" },
    { "name": "inventory_count", "type": "number", "required": false, "description": "Stock quantity" }
  ],
  "suggested_mapping": {
    "Product ID": "sku",
    "Title": "name",
    "Brand": "brand",
    "Sale Price": "price",
    "Stock": "inventory_count"
  }
}

GET /api/pim/import/{job_id}/#

Poll import job status.

Response

json
{
  "id": 42,
  "status": "importing",
  "filename": "catalog.csv",
  "row_count": 10000,
  "imported_count": 3400,
  "mapping": { "Product ID": "sku", "Title": "name" },
  "error_message": "",
  "completed_at": null,
  "created_at": "2026-05-03T10:00:00Z"
}

Status values: pendingmapping (after upload) → importing (task running) → done or failed

POST /api/pim/import/{job_id}/confirm/#

Confirm the column mapping and schema, start the import task.

Request body

json
{
  "mapping": {
    "Product ID": "sku",
    "Title": "name",
    "Brand": "brand",
    "Sale Price": "price"
  },
  "schema": [
    { "name": "brand", "type": "string", "required": false, "description": "Product brand" },
    { "name": "price", "type": "price", "required": false, "description": "Sale price" }
  ],
  "default_currency": "USD",
  "default_locale": "en_US"
}
  • mapping — maps each CSV header to a schema field name, or null to skip
  • schema — full list of schema fields to save; replaces existing ProductSchema.fields
  • default_currency / default_locale — optional, updates ProductSchema

Response

json
{ "job_id": 42, "status": "pending" }

Errors

CodeCondition
400Job already in importing, done, or failed state
404Job not found for this tenant

Product Segments#

Base path: /api/product-segments/

GET /api/product-segments/#

List all product segments for the authenticated tenant.

Response:

json
[
  {
    "id": 1,
    "name": "Running shoes under $150",
    "description": "...",
    "is_active": true,
    "definition": { "logic": "AND", "conditions": [...] },
    "computed_count": 243,
    "last_computed_at": "2026-05-03T12:00:00Z",
    "refresh_interval": 60,
    "created_at": "...",
    "updated_at": "..."
  }
]

POST /api/product-segments/#

Create a new product segment.

Request body:

json
{
  "name": "Running shoes under $150",
  "description": "Optional",
  "is_active": false,
  "definition": {
    "logic": "AND",
    "conditions": [
      { "type": "product_attribute", "property": "category", "operator": "eq", "value": "running", "value_type": "string" },
      { "type": "product_attribute", "property": "price", "operator": "lte", "value": 150, "value_type": "number" }
    ]
  }
}

Response: 201 Created — full ProductSegment object.


GET /api/product-segments/{id}/#

Retrieve a single product segment.


PATCH /api/product-segments/{id}/#

Update a product segment (partial update).


DELETE /api/product-segments/{id}/#

Delete a product segment.


POST /api/product-segments/preview/#

Run a live preview of a segment definition without saving. Returns matched SKUs and the compiled SQL.

Request body: Full definition object (see DSL format above).

Response:

json
{
  "matched_count": 87,
  "sample_skus": ["SKU-001", "SKU-002", "SKU-003"],
  "sql": "SELECT sku FROM segmentation.products FINAL WHERE ..."
}

POST /api/product-segments/{id}/compute/#

Enqueue a Celery task to recompute segment membership for the given segment. Returns 200 OK with { "status": "queued", "segment_id": <id> } immediately.

The segment's computed_count and last_computed_at fields update when the task completes (poll GET /api/product-segments/{id}/ to detect completion).


GET /api/product-segments/{id}/members/#

Return matched products for a computed segment. Joins product_memberships (ClickHouse) with Product (Postgres) to return full product details.

Query params:

  • cursor — pagination cursor

Response:

json
{
  "count": 1280,
  "results": [
    { "sku": "SKU-001", "name": "Air Runner X", "data": { "category": "running", "price": 129.99 } }
  ],
  "next_cursor": 50
}

next_cursor is the integer offset to pass as the next cursor query param, or null on the last page.