API Documentation

Get Started with Sortillus

To start using our API, you'll need to register for an account.

What you'll get

  • Generate API tokens for secure authentication
  • Activate the Woo–Sortillus plugin for seamless WooCommerce integration
  • Access your domain-specific API endpoints
  • Manage your products, categories, and recommendations

Endpoints

Here you'll find detailed documentation for each available endpoint.

Create Product

POST /api/v3/products

Request Body
Field Type Description
id integer External ID of the product (required)
name string Name of the product (required)
description string Detailed description of the product
image string URL of the product image
suggested_category_name string Suggested category name for the product
brand string Brand name of the product
active boolean Whether the product is active (true) or inactive (false)
gtin string Global Trade Item Number (GTIN) of the product
gtins array of strings Array of Global Trade Item Numbers (GTINs) for the product. If gtin is not provided but gtins are, the first GTIN from the array will be used as the primary gtin.
Response

On success, returns the created product with status code 201 (Created).

On error, returns validation errors with status code 422 (Unprocessable Entity).

Example Usage
curl https://admin.sortillus.com/api/v3/products \
     -X POST \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json" \
     -d '{
  "product": {
    "id": 6,
    "name": "your product name",
    "suggested_category_name": "your suggested category name",
    "brand": "product brand",
    "description": "long product description",
    "image": "https://some.com/productid.image.jpg",
    "gtin": "1234567890123",
    "gtins": ["1234567890123", "1234567890124"]
  }
}'
Example Response
{
  "sortillus_id": 101,
  "external_id": 6,
  "name": "your product name",
  "description": "long product description",
  "image": "https://some.com/productid.image.jpg",
  "canonical_id": null,
  "category_external_id": null,
  "sortillus_category_id": null,
  "active": true,
  "state": "new",
  "updated_at": "2025-06-12T18:08:59Z",
  "created_at": "2025-06-12T18:08:58Z",
  "recommendations": [],
  "recommended_at": null
}

Update Product

PATCH /api/v3/products/:id

Update an existing product by its external ID. Only the provided fields will be updated.

Note: Parameters can be sent either wrapped in a product object or directly at the root level. Both formats are supported for backward compatibility.

URL Parameters
Parameter Type Description
id integer External ID of the product to update (required)
Request Body
Field Type Description
name string Updated name of the product
description string Updated description of the product
image string URL of the updated product image. To clear the image, send an empty string "".
category_id string Full external ID (e.g., "L4_2345") or regular external ID of the category to assign to the product. The endpoint will first try to find by full_external_id, then fall back to external_id if not found.
active boolean Whether the product is active (true) or inactive (false)
canonical boolean Make this product the canonical for its cluster (true) or leave unchanged (false/not provided)
external_canonical_product_id string External ID of the product to use as canonical for this product's cluster
gtin string Global Trade Item Number (GTIN) of the product. To clear the GTIN, send an empty string "". If both gtin and gtins are provided, gtin takes precedence.
gtins array of strings Array of Global Trade Item Numbers (GTINs) for the product. If gtin is not provided but gtins are, the first non-empty GTIN from the array will be used as the primary gtin. To clear all GTINs, send an empty array [].
Response

On success, returns the updated product with status code 200 (OK).

On error, returns validation errors with status code 422 (Unprocessable Entity).

If the product is not found, returns status code 404 (Not Found).

If the category is not found, returns status code 422 (Unprocessable Entity).

If the external canonical product is not found, returns status code 422 (Unprocessable Entity).

If required parameters are missing (e.g., missing product wrapper when using strict format), returns status code 400 (Bad Request).

Note: The id parameter in the request body (if provided) is ignored. The product is identified by the id in the URL path only.

Example Usage (Basic Update - with product wrapper)
curl https://admin.sortillus.com/api/v3/products/6 \
     -X PATCH \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json" \
     -d '{
  "product": {
    "name": "Updated product name",
    "description": "Updated product description",
    "active": true,
    "category_id": "L4_2345"
  }
}'
Example Usage (Basic Update - without product wrapper)
curl https://admin.sortillus.com/api/v3/products/6 \
     -X PATCH \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json" \
     -d '{
  "name": "Updated product name",
  "description": "Updated product description",
  "active": true,
  "category_id": "L4_2345"
}'
Example Usage (Update GTINs)
curl https://admin.sortillus.com/api/v3/products/6 \
     -X PATCH \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json" \
     -d '{
  "product": {
    "gtin": "1234567890123",
    "gtins": ["1234567890123", "9876543210987"]
  }
}'
Example Usage (Clear GTINs)
curl https://admin.sortillus.com/api/v3/products/6 \
     -X PATCH \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json" \
     -d '{
  "product": {
    "gtin": "",
    "gtins": []
  }
}'
Example Usage (Clear Image)
curl https://admin.sortillus.com/api/v3/products/6 \
     -X PATCH \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json" \
     -d '{
  "product": {
    "image": ""
  }
}'
Example Usage (Make Product Canonical)
curl https://admin.sortillus.com/api/v3/products/6 \
     -X PATCH \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json" \
     -d '{
  "product": {
    "canonical": true
  }
}'
Example Usage (Assign to External Canonical)
curl https://admin.sortillus.com/api/v3/products/6 \
     -X PATCH \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json" \
     -d '{
  "product": {
    "external_canonical_product_id": "123"
  }
}'
Example Response
{
  "sortillus_id": 101,
  "external_id": 6,
  "name": "Updated product name",
  "description": "Updated product description",
  "image": "https://some.com/productid.image.jpg",
  "canonical_id": null,
  "category_external_id": "L4_2345",
  "sortillus_category_id": 123,
  "active": true,
  "state": "ready",
  "updated_at": "2025-06-12T18:08:59Z",
  "created_at": "2025-06-12T18:08:58Z",
  "recommendations": [],
  "recommended_at": null
}

Retrieve detailed information about a specific product by its external ID.

URL Parameters
Parameter Type Description
id integer External ID of the product to retrieve (required)
Response

On success, returns the product details with status code 200 (OK).

If the product is not found, returns status code 404 (Not Found).

The response includes a recommendations array when the product has recommended products. Each recommendation contains:

Field Type Description
product_id integer External ID of the recommended product (not the internal sortillus_id)
explanation string Human-readable explanation for why this product is recommended
reason_json object (optional) Additional metadata about the recommendation (e.g., similarity score, source)
Example Usage
curl https://admin.sortillus.com/api/v3/products/6 \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json"
Example Response (without recommendations)
{
  "sortillus_id": 101,
  "external_id": 6,
  "name": "your product name",
  "description": "long product description",
  "image": "https://some.com/productid.image.jpg",
  "canonical_id": null,
  "category_external_id": null,
  "sortillus_category_id": null,
  "active": true,
  "state": "new",
  "updated_at": "2025-06-12T18:08:59Z",
  "created_at": "2025-06-12T18:08:58Z",
  "recommendations": [],
  "recommended_at": null
}
Example Response (with recommendations)
{
  "sortillus_id": 101,
  "external_id": 6,
  "name": "your product name",
  "description": "long product description",
  "image": "https://some.com/productid.image.jpg",
  "canonical_id": null,
  "category_external_id": "L4_2345",
  "sortillus_category_id": 123,
  "active": true,
  "state": "recommended_products",
  "updated_at": "2025-06-12T18:08:59Z",
  "created_at": "2025-06-12T18:08:58Z",
  "recommendations": [
    {
      "product_id": 789,
      "explanation": "Same model; different color.",
      "reason_json": {"source": "embedding", "score": 0.92}
    },
    {
      "product_id": 456,
      "explanation": "Bundle frequently bought together."
    },
    {
      "product_id": 123,
      "explanation": "Complementary product"
    }
  ],
  "recommended_at": "2025-06-12T18:10:00Z"
}

Trigger Product Recommendations

POST /api/v3/products/:id/recommend

Trigger the product recommendation process for a specific product identified by its internal product ID (sortillus_id). The domain_id is automatically determined from the access token provided in the Authorization header. This endpoint initiates the AASM state machine transition to start generating product recommendations asynchronously.

URL Parameters
Parameter Type Description
id integer Internal product ID (sortillus_id) of the product to trigger recommendations for (required)
Request Headers
Header Value Description
Authorization Bearer <access_token> Your domain access token (required)
Content-Type application/json Content type header
Response

On success, returns a confirmation message with status code 200 (OK).

If the product is not found, returns status code 404 (Not Found).

If the access token is missing or invalid, returns status code 401 (Unauthorized).

If the recommendation process fails to start (e.g., invalid product state, AASM guard conditions not met), returns status code 422 (Unprocessable Entity).

Field Type Description
message string Confirmation message indicating the recommendation process was triggered
product_id integer The internal database ID of the product (same as the id parameter)
external_id integer The external ID of the product
state string The current AASM state of the product after triggering recommendations (typically recommending_products)
Notes
  • The recommendation process is asynchronous. The endpoint returns immediately after triggering the process.
  • The product must be in a valid state for recommendations to be triggered (guards are checked by AASM).
  • Recommendations are generated by an external service that processes messages from the RabbitMQ queue.
  • The domain must have should_recommend_products enabled for recommendations to work properly (checked by AASM guards).
  • You can check the product state using the GET /api/v3/products/:id endpoint to see when recommendations are complete.
  • The domain_id is automatically determined from the access token, so you don't need to include it in the URL.
Example Usage
curl https://admin.sortillus.com/api/v3/products/6/recommend \
     -X POST \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json"
Example Success Response
{
  "message": "Recommendation process triggered successfully",
  "product_id": 12345,
  "external_id": 6,
  "state": "recommending_products"
}
Example Error Response (Product Not Found)
{
  "error": "Product not found"
}
Example Error Response (Invalid Token)
{
  "error": "Invalid access token"
}
Example Error Response (Failed to Trigger)
{
  "error": "Failed to trigger recommendations",
  "message": "Detailed error message"
}

Retrieve a paginated list of products for your domain. Each product includes its current processing state.

Query Parameters
Parameter Type Description
page integer Page number (default: 1)
per_page integer Items per page (default: 1000)
aasm_states string / array Filter products by AASM state(s). Accepts a comma-separated list (?aasm_states=ready,error) or array form (?aasm_states[]=ready&aasm_states[]=error). Only states defined on the model are permitted; invalid values return 422 Unprocessable Entity. When this filter or updated_before/updated_after is provided, the response omits the total_pages and total_count keys to improve performance.
updated_before string (ISO8601)
updated_after string (ISO8601) Return only products updated on or after this timestamp (e.g., 2025-09-01T00:00:00Z). Can be combined with updated_before to form a range. When supplied, the response omits total_pages and total_count in meta.
Return only products updated on or before this timestamp (e.g., 2025-09-15T00:00:00Z). When supplied, the response omits total_pages and total_count in meta. Results are ordered by updated_at DESC, id DESC.
Response

Returns a list of products with their id, external_id, processing state, timestamps, optional category_external_id, and external_canonical_id (the external_id of the canonical product if this product is a duplicate), along with pagination metadata.

Note: If the aasm_states or any of updated_before/updated_after is supplied, the meta object will only contain current_page; total_pages and total_count are omitted.

Results are ordered by updated_at DESC with id DESC as a tiebreaker.

Field Type Description
id integer Internal product ID
external_id integer External product ID
state string Current processing state
updated_at string (ISO8601) Last update timestamp
created_at string (ISO8601) Creation timestamp
category_external_id string/integer External ID of the product's category (if any)
external_canonical_id integer/null The external_id of the canonical product if this product is a duplicate; null for canonical products.
Example Usage
curl https://admin.sortillus.com/api/v3/products?per_page=100&aasm_states=ready,error \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json"
Example Usage (time-capped page)
curl https://admin.sortillus.com/api/v3/products?updated_before=2025-09-15T00:00:00Z&per_page=1000 \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json"
Example Usage (time range)
curl https://admin.sortillus.com/api/v3/products?updated_after=2025-09-01T00:00:00Z&updated_before=2025-09-15T00:00:00Z&per_page=1000 \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json"
Example Response
{
  "products": [
    {
      "id": 101,
      "external_id": 6,
      "state": "ready",
      "updated_at": "2025-06-12T18:08:59Z",
      "created_at": "2025-06-12T18:08:58Z",
      "category_external_id": "L4_2345",
      "external_canonical_id": null
    },
    {
      "id": 102,
      "external_id": 7,
      "state": "error",
      "updated_at": "2025-06-12T17:00:00Z",
      "created_at": "2025-06-12T16:50:00Z",
      "category_external_id": null,
      "external_canonical_id": 6
    }
  ],
  "meta": {
    "current_page": 1
  }
}

Retrieve a paginated list of shop categories for your domain. By default, returns all categories (including those without attributes). Use the with_attributes parameter to filter to only categories that have attributes assigned. Categories are ordered by the most recently created attribute, with categories without attributes appearing last.

Query Parameters
Parameter Type Description
page integer Page number (default: 1)
level integer Filter categories by level (e.g., 1 for top-level categories)
parent_external_id integer Filter categories by parent external ID
search string Search categories by name (case-insensitive)
with_attributes boolean If true, 1, or yes, returns only categories that have at least one product attribute assigned. Default: false (returns all categories)
Response

Returns a list of categories with their attributes and options, along with pagination metadata. Categories are ordered by the most recently created attribute (categories without attributes appear last, ordered by updated_at). Categories without attributes will have an empty attributes array and last_attribute_created_at will be null.

Field Type Description
id integer Internal category ID
external_id integer External category ID
full_external_id string Full external ID of the category
level integer Category level in the hierarchy
name string Category name
en_name string English category name
en_description string English category description
updated_at string (ISO8601) Last update timestamp
created_at string (ISO8601) Creation timestamp
last_attribute_created_at string (ISO8601) or null Timestamp of the most recently created attribute. null if the category has no attributes.
attributes array Array of attributes assigned to this category
Attribute Object Structure
Field Type Description
id integer Internal attribute ID
name string Attribute name
external_id integer External attribute ID
measurement string Measurement unit for the attribute
description string Attribute description
options array Array of options for this attribute
Option Object Structure
Field Type Description
id integer Internal option ID
name string Option name
external_id integer External option ID
measurement string Measurement unit for the option
Example Usage
# Get all categories
curl https://admin.sortillus.com/api/v3/shop/categories?page=1&level=1 \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json"

# Get only categories with attributes
curl https://admin.sortillus.com/api/v3/shop/categories?with_attributes=true \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json"
Example Response
{
  "categories": [
    {
      "id": 1,
      "external_id": 2345,
      "full_external_id": "L4_2345",
      "level": 4,
      "name": "Electronics",
      "en_name": "Electronics",
      "en_description": "Electronic devices and components",
      "updated_at": "2024-01-15T10:30:00Z",
      "created_at": "2024-01-01T00:00:00Z",
      "last_attribute_created_at": "2024-01-15T10:30:00Z",
      "attributes": [
        {
          "id": 1,
          "name": "Color",
          "external_id": 100,
          "measurement": null,
          "description": "Product color",
          "options": [
            {
              "id": 1,
              "name": "Red",
              "external_id": 101,
              "measurement": "free_text"
            },
            {
              "id": 2,
              "name": "Blue",
              "external_id": 102,
              "measurement": "free_text"
            }
          ]
        },
        {
          "id": 2,
          "name": "Size",
          "external_id": 200,
          "measurement": "cm",
          "description": "Product size",
          "options": [
            {
              "id": 3,
              "name": "25",
              "external_id": 201,
              "measurement": "cm"
            }
          ]
        }
      ]
    }
  ],
  "meta": {
    "current_page": 1,
    "total_pages": 1,
    "total_count": 1
  }
}

Create or Update Shop Category

Creates a new category or updates an existing one. The endpoint matches or initializes by external_id within your domain. All required fields must be provided.

Request Body
Field Type Description
external_id integer External category ID. Required. Used to find or create within your domain. Must be a positive integer.
name string Category name. Required.
en_name string English category name. Required. Used for vectorization and embeddings.
en_description string English category description. Required. Used for vectorization and embeddings.
description string Category description. Optional.
external_parent_id integer External parent category ID. Optional. If provided, sets this category as a child of the parent.
Response

On create, returns the category with status code 201 Created. On update, returns 200 OK.

On validation error (e.g., missing required fields, invalid parent), returns 422 Unprocessable Entity.

Example Usage (create/update category)
curl https://admin.sortillus.com/api/v3/shop/categories \
     -X POST \
     -H "Authorization: Bearer " \
     -H "Content-Type: application/json" \
     -d '{
  "category": {
    "external_id": 456,
    "name": "Electronics",
    "en_name": "Electronics",
    "en_description": "Electronic devices and components",
    "description": "Long category description",
    "external_parent_id": 20
  }
}'
Example Response
{
  "id": 123,
  "external_id": 456,
  "name": "Electronics",
  "description": "Long category description",
  "en_name": "Electronics",
  "en_description": "Electronic devices and components",
  "parent_id": 20,
  "full_external_id": "L2_456",
  "level": 2,
  "created_at": "2025-06-12T18:08:58Z",
  "updated_at": "2025-06-12T18:08:59Z"
}