Skip to content

AicoNeo API Documentation

High-performance optimized API endpoints for products, customers, orders, and documents.

Base URL: /api

Authentication: All endpoints require auth:sanctum

Localization: Set Accept-Language header for translated content


🚀 Import into Postman

You can import all AicoNeo APIs into Postman using our OpenAPI specification file.

Option 1: Import from File

  1. Download the OpenAPI spec: openapi-aiconeo.yaml
  2. Open Postman
  3. Click Import (top-left corner)
  4. Drag and drop the file or click Upload Files
  5. Click Import

Option 2: Import via URL (if hosted)

  1. Open Postman
  2. Click ImportLink
  3. Paste the URL to the OpenAPI spec file
  4. Click ContinueImport

After Import

  • Set the {{baseUrl}} variable to your AICO instance URL (e.g., https://app.aico.swiss/12345/api)
  • Set the {{bearerToken}} variable with your Sanctum API token
  • All endpoints will be organized by tags (Products, Orders, Tasks, etc.)

Features Included

✅ All endpoints with full path and method
✅ Query parameters with descriptions and examples
✅ Request body schemas with examples
✅ Response schemas
✅ Filter operators documentation
✅ Authentication configuration


Table of Contents

  1. AicoNeo Optimized Endpoints
  2. Products List
  3. Product Detail
  4. Product Create
  5. Product Update (Collaborative Editing)
  6. Product Focus (Optional)
  7. Product Presence Channel
  8. Data Types & Update Scopes
  9. Product Prices
  10. Product Stocks
  11. Product Stock Changes
  12. Articles
  13. Categories
  14. Product Types
  15. Product Units
  16. Product Statuses
  17. Collections
  18. B2B Customers
  19. B2B Detail
  20. B2B Create
  21. B2B Update (Collaborative Editing)
  22. B2C Customers
  23. B2C Create
  24. B2C Update (Collaborative Editing)
  25. Orders
  26. Order Documents
  27. Ecommerce Orders
  28. Tasks
  29. Task Detail
  30. Task Logs
  31. Task Comments
  32. Warehouses
  33. Time Tracking
  34. Shopify Shops
  35. List Shopify Shops
  36. Trigger Bulk Shopify Sync
  37. Languages
  38. Languages List
  39. Language Detail
  40. Filter Operators Reference

AicoNeo Optimized Endpoints

High-performance endpoints using direct SQL queries for optimal speed.

Products List

Endpoint: GET /api/aiconeo/products

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 25 100 Items per page
sort string -id - Sort field(s), prefix with - for descending
include string - - Comma-separated list of relationships
filter[field] mixed - - Filter by field

Includes

Include Description Default
brand Brand with translated name
category Category with translated name
tags Product tags
crmTags CRM tags
shopifyShops Shopify shop connections
productPrices Product prices with currencies
variants Product variants
customFields Custom fields, currencies, stock
productUnit Product unit with translations (name, shortName)
categorySets Additional category sets beyond main category hierarchy
sellers Associated sellers
suppliers B2B suppliers with manufacturer SKU
nutritionalValues Nutritional values with translations

Include Response Structures

productUnit

{
  "id": 1,
  "name": "Piece",
  "shortName": "pc",
  "isDefault": true,
  "hasUnitContents": false
}
Field Type Description
id integer Product unit ID
name string Translated unit name
shortName string Translated short name
isDefault boolean Whether this is the default unit
hasUnitContents boolean Whether the unit has contents

categorySets

[
  {
    "id": 1,
    "category": {
      "id": 10,
      "name": "Electronics",
      "webName": "electronics",
      "image": "https://example.com/storage/categories/electronics.jpg"
    },
    "subCategory": {
      "id": 20,
      "name": "Phones",
      "webName": "phones",
      "image": "https://example.com/storage/categories/phones.jpg"
    },
    "subSubCategory": null
  }
]
Field Type Description
id integer Category set ID
category object|null Main category with id, name, webName, image
subCategory object|null Subcategory with id, name, webName, image
subSubCategory object|null Sub-subcategory with id, name, webName, image

sellers

[
  {
    "id": 1,
    "name": "Main Store",
    "isActive": true
  }
]
Field Type Description
id integer Seller ID
name string Seller name
isActive boolean Whether the seller is active

suppliers

[
  {
    "id": 1,
    "name": "Supplier Corp",
    "manufacturerSku": "SUP-12345",
    "isPrimary": true
  }
]
Field Type Description
id integer Supplier (B2B) ID
name string Supplier company name
manufacturerSku string|null Manufacturer SKU for this supplier
isPrimary boolean Whether this is the primary supplier

Sortable Fields

Field Description
id Product ID
sku SKU
barcode Barcode
manufacturerSku Manufacturer SKU
urlHandle URL handle/slug
weightValue Weight value
weightUnit Weight unit
provenance Provenance/origin
hsCode HS code
createdAt Creation date
updatedAt Last update date
name Product name (translated)
webName Web name (translated)
externalName External name (translated)
brandName Brand name (translated)
categoryName Category name (translated)
statusName Status name

Text Filters

Supports all text filter operators.

Filter Column
filter[sku] SKU
filter[barcode] Barcode
filter[manufacturerSku] Manufacturer SKU
filter[urlHandle] URL handle
filter[hsCode] HS code
filter[provenance] Provenance
filter[weightUnit] Weight unit
filter[name] Product name
filter[webName] Web name
filter[externalName] External name
filter[description] Description
filter[smallDescription] Small description
filter[pageTitle] Page title
filter[metaDescription] Meta description

DateTime Filters

Supports all datetime filter operators.

Filter Column
filter[createdAt] Creation date
filter[updatedAt] Last update date

Relationship Filters

Filter Description Example
filter[id] Product ID(s) filter[id]=1,2,3
filter[brandId] Brand ID(s) filter[brandId]=5,10
filter[categoryId] Category ID(s) filter[categoryId]=3
filter[statusId] Status ID(s) filter[statusId]=1,2
filter[tagId] Tag ID(s) filter[tagId]=5,8
filter[crmTagId] CRM tag ID(s) filter[crmTagId]=2
filter[shopifyShopId] Shopify shop ID(s) filter[shopifyShopId]=1

Numeric Filters

Filter Description Example
filter[weightValueMin] Minimum weight filter[weightValueMin]=0.5
filter[weightValueMax] Maximum weight filter[weightValueMax]=10

Boolean Filters

Filter Description Example
filter[hasVariants] Has variants filter[hasVariants]=true
Filter Description
filter[search] Searches: SKU, barcode, manufacturer SKU, URL handle, HS code, name, web name, external name

Example Requests

# Basic request
GET /api/aiconeo/products

# With includes and pagination
GET /api/aiconeo/products?include=brand,category,productPrices,variants&page[number]=1&page[size]=50

# With text filter
GET /api/aiconeo/products?filter[sku][startsWith]=PRD-

# With date filter
GET /api/aiconeo/products?filter[createdAt][dateBetween]=2024-01-01,2024-12-31

Product Detail

Endpoint: GET /api/aiconeo/products/{id}

Get comprehensive details for a single product, optimized for product detail pages.

Response Includes

Data Description
Basic fields SKU, barcode, manufacturer SKU, URL handle, product type, flags, dates
Date fields saleFrom, saleUntil, releaseDate, releaseStartTime, releaseEndTime
Weight & Dimensions All gross/net weight, length, height, width, diameter, volume fields with units
Production & Origin provenance, productionCountry, hsCode, eacNumber
Product specifications warranty, numberOfItemsInPackage, deliveryPeriod, shelfLife, totalShelfLife, consumptionPeriod, specification, storageConditions, unitContents
Shipping configuration isSuitableForShipping (boolean), shippingDestination (translated: Everywhere/EU Only/USA Only), isMondayBlocked, isTuesdayBlocked, isWednesdayBlocked, isThursdayBlocked, isFridayBlocked, isSaturdayBlocked, isSundayBlocked (all boolean), isCustomerDayBlockEnabled (boolean)
Translations All locales with name, web name, description, SEO fields, ingredients, image URLs
Product unit Product unit with name and short name
File groups Documents, images, videos, marketing documents, manufacturing documents
Main image Convenience field with the main product image
Variants All variants with options, pricing, manufacturing details, and image URLs
Variation options Option definitions (size, color, etc.) with values and image URLs
Tax classes Tax information per tax area with current rates
Categories Main category, subcategory, and sub-subcategory with image URLs
Category sets Additional category mappings beyond the main hierarchy
Shopify shops All connected Shopify shops with sync status
Custom fields All custom field values for the product
Sellers Associated sellers with name and status
Suppliers B2B suppliers with manufacturer SKU and primary flag
Nutritional values Nutritional information with value, unit, and translated text

URL Fields

All image/file URLs in the response are full URLs generated via Laravel Storage:

Location Fields
translations[].image Product translation images
variants[].image Variant images
variationOptions[].values[].image Variation option value images (e.g., color swatches)
categories.*.image Category images
fileGroups.*.files[].translations[].fileUrl File URLs
fileGroups.*.files[].translations[].optimizedFileUrl Optimized file URLs

Example Request

GET /api/aiconeo/products/123
Accept-Language: de_CH

Example Response

{
  "data": {
    "id": 1773,
    "type": "products",
    "attributes": {
      "sku": "93583658",
      "barcode": null,
      "manufacturerSku": null,
      "urlHandle": "product-1773",
      "productType": "PHYSICAL",
      "isBundle": false,
      "isAvailable": false,
      "isGiftCard": false,
      "isDiscontinued": false,
      "isAgeRestricted": false,
      "isAvailableOnAicoPos": false,
      "isAvailableInAppShop": false,
      "releaseDate": null,
      "createdAt": "2025-12-23 13:50:30",
      "updatedAt": "2025-12-23 13:50:46",
      "archivedAt": null,
      "version": 1,
      "saleFrom": null,
      "saleUntil": null,
      "releaseStartTime": null,
      "releaseEndTime": null,
      "weightValue": null,
      "weightUnit": null,
      "grossLength": null,
      "grossHeight": null,
      "grossWidth": null,
      "grossDiameter": null,
      "dimensionUnit": null,
      "netWeightValue": null,
      "netWeightUnit": null,
      "netLength": null,
      "netHeight": null,
      "netWidth": null,
      "netDiameter": null,
      "grossVolume": null,
      "netVolume": null,
      "grossVolumeUnit": null,
      "netVolumeUnit": null,
      "provenance": null,
      "productionCountry": null,
      "hsCode": null,
      "eacNumber": null,
      "warranty": null,
      "numberOfItemsInPackage": null,
      "deliveryPeriod": null,
      "shelfLife": null,
      "totalShelfLife": null,
      "consumptionPeriod": null,
      "specification": null,
      "storageConditions": null,
      "maxQuantityPerOrder": null,
      "unitContents": null,
      "isSuitableForShipping": false,
      "shippingDestination": "Everywhere",
      "isMondayBlocked": false,
      "isTuesdayBlocked": false,
      "isWednesdayBlocked": false,
      "isThursdayBlocked": false,
      "isFridayBlocked": false,
      "isSaturdayBlocked": false,
      "isSundayBlocked": false,
      "isCustomerDayBlockEnabled": false,
      "taxCode": null,
      "fibuProceeds": null,
      "fibuExpenses": null,
      "translations": [
        {
          "locale": "de_CH",
          "name": "Test Fabian",
          "webName": "Test Fabian",
          "externalName": null,
          "description": null,
          "smallDescription": null,
          "image": null,
          "manufacturer": null,
          "ingredients": null,
          "pageTitle": null,
          "metaDescription": null,
          "translatedUrlHandle": "product-1773",
          "notes": null,
          "sideEffects": null,
          "contraindications": null,
          "interactions": null,
          "usageInstructions": null
        }
      ],
      "status": {
        "id": 4,
        "name": "OUT",
        "color": "#e50000"
      },
      "brand": null,
      "productUnit": null,
      "categories": {
        "category": null,
        "subCategory": null,
        "subSubCategory": null
      },
      "categorySets": [],
      "variants": [
        {
          "id": 15156,
          "sku": "93583658_87494",
          "manufacturerSku": null,
          "barcode": null,
          "name": "Test Fabian, A",
          "image": null,
          "price": null,
          "priceCHF": null,
          "discountPriceCHF": null,
          "buyingPrice": null,
          "order": 0,
          "weightValue": null,
          "weightUnit": null,
          "grossLength": null,
          "grossHeight": null,
          "grossWidth": null,
          "netWeightValue": null,
          "netWeightUnit": null,
          "provenance": null,
          "hsCode": null,
          "maxQuantityPerOrder": null,
          "deliveryStatus": 0,
          "shouldNotSyncToShopify": false,
          "createdAt": "2025-12-23 13:52:07",
          "updatedAt": "2025-12-23 13:52:07",
          "options": [
            {
              "id": 988458,
              "name": "Wert",
              "value": "A",
              "variationOptionValueId": null
            }
          ]
        },
        {
          "id": 15157,
          "sku": "93583658_34841",
          "manufacturerSku": null,
          "barcode": null,
          "name": "Test Fabian, B",
          "image": null,
          "price": null,
          "priceCHF": null,
          "discountPriceCHF": null,
          "buyingPrice": null,
          "order": 1,
          "weightValue": null,
          "weightUnit": null,
          "grossLength": null,
          "grossHeight": null,
          "grossWidth": null,
          "netWeightValue": null,
          "netWeightUnit": null,
          "provenance": null,
          "hsCode": null,
          "maxQuantityPerOrder": null,
          "deliveryStatus": 0,
          "shouldNotSyncToShopify": false,
          "createdAt": "2025-12-23 13:52:07",
          "updatedAt": "2025-12-23 13:52:07",
          "options": [
            {
              "id": 988459,
              "name": "Wert",
              "value": "B",
              "variationOptionValueId": null
            }
          ]
        }
      ],
      "variationOptions": [
        {
          "id": 578,
          "name": "Wert",
          "isColorOption": false,
          "sortOrder": null,
          "values": [
            {
              "id": 940048,
              "value": "A",
              "image": null,
              "sortOrder": null
            },
            {
              "id": 940049,
              "value": "B",
              "image": null,
              "sortOrder": null
            }
          ]
        }
      ],
      "fileGroups": {
        "images": {
          "id": 8484,
          "mainProductFileId": 70389,
          "files": []
        },
        "mainImage": null
      },
      "mainImage": null,
      "taxClasses": [
        {
          "id": 5558,
          "taxArea": {
            "id": 2,
            "state": null,
            "country": "Germany",
            "name": "Germany"
          },
          "taxClass": {
            "id": 7,
            "name": "Mehrwertsteuerfrei"
          },
          "currentRate": null
        },
        {
          "id": 5559,
          "taxArea": {
            "id": 3,
            "state": null,
            "country": "Hong Kong",
            "name": "Hong Kong"
          },
          "taxClass": {
            "id": 8,
            "name": "Mehrwertsteuerfrei"
          },
          "currentRate": null
        },
        {
          "id": 5560,
          "taxArea": {
            "id": 1,
            "state": null,
            "country": "Switzerland",
            "name": "Switzerland"
          },
          "taxClass": {
            "id": 1,
            "name": "Umsatz 8.1 (NS)"
          },
          "currentRate": {
            "value": 8.1,
            "validFrom": "2023-06-30 22:00:00"
          }
        }
      ],
      "shopifyShops": [],
      "customFields": {
        "1_spielername": null,
        "4_waschanleitung": null,
        "5_url_360": null,
        "7_docnum": null,
        "9_mestyp": null,
        "15_trikotorder": null,
        "currency_1_chf": null,
        "warehouse_5_fanshop_sjp_stadion": null,
        "total_stock": 44,
        "warehouse_8_1050__kiosk": null,
        "warehouse_9_1060__vip": null,
        "warehouse_10_1100__backoffice": 21,
        "warehouse_11_1200__stadion": null,
        "warehouse_12_1250__schraege": null,
        "warehouse_13_1300__logistik": null,
        "warehouse_14_1400__arlesheim": null,
        "warehouse_15_1600__creadiva": null,
        "warehouse_16_1000__fanshop": null,
        "currency_2_eur": 0,
        "currency_3_usd": null,
        "currency_10_hkd": null,
        "25_mitgliedschaftprodukt": null
      },
      "sellers": [],
      "suppliers": [],
      "nutritionalValues": []
    }
  }
}

Product Create

Endpoint: POST /api/aiconeo/products

Create a new product with minimal data. The product is created immediately with just SKU, then initialization (default prices, Shopify sync, etc.) happens in a background job.

Request Body (JSON:API format)

{
  "data": {
    "type": "products",
    "attributes": {
      "sku": "OPTIONAL-SKU"
    }
  }
}
Field Type Required Description
data.type string Yes Resource type, must be "products"
data.attributes.sku string No Product SKU (auto-generated if not provided)

Response (201 Created)

{
  "data": {
    "type": "products",
    "id": "1774",
    "attributes": {
      "sku": "93583659",
      "version": 1,
      "status": "initializing",
      "createdAt": "2026-01-07T06:22:07+01:00"
    }
  }
}

Bad Request (400):

{
  "errors": [{
    "status": "400",
    "title": "Bad Request",
    "detail": "Invalid or missing resource type. Expected \"products\".",
    "source": { "pointer": "/data/type" }
  }]
}

Pusher Event

When background initialization completes, a ProductInitialized event is broadcast on the presence channel:

  • Channel: {workspaceId}.product.{productId} (presence channel)
  • Event: .initialized
  • Payload:
    {
      "product": {
        "id": 1774,
        "sku": "93583659",
        "version": 1,
        "status": "active",
        "name": null,
        "createdAt": "2026-01-07T06:22:07+01:00",
        "updatedAt": "2026-01-07T06:22:08+01:00"
      }
    }
    

Example Request

curl -X POST "http://127.0.0.1/api/aiconeo/products" \
  -H "Accept: application/vnd.api+json" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"data": {"type": "products", "attributes": {"sku": "MY-SKU-123"}}}'

Product Update (Collaborative Editing)

Endpoint: PATCH /api/aiconeo/products/{id}

Update product fields with optimistic locking. Requires the current version to prevent conflicts when multiple users edit simultaneously.

Path Parameters

Parameter Type Description
id int Product ID

Request Body (JSON:API format)

Basic field update:

{
  "data": {
    "type": "products",
    "id": "1774",
    "attributes": {
      "sku": "NEW-SKU",
      "isAvailable": true,
      "version": 2
    }
  }
}

Translations update (flat array format):

{
  "data": {
    "type": "products",
    "id": "1774",
    "attributes": {
      "version": 2,
      "translations": [
        {"locale": "de_CH", "name": "Deutscher Name", "description": "Beschreibung"},
        {"locale": "en", "name": "English Name"}
      ]
    }
  }
}

Mixed update (translations + regular fields):

{
  "data": {
    "type": "products",
    "id": "1774",
    "attributes": {
      "version": 2,
      "sku": "NEW-SKU",
      "isAvailable": true,
      "translations": [
        {"locale": "de_CH", "name": "Deutscher Name"},
        {"locale": "en", "name": "English Name"}
      ]
    }
  }
}

Field Type Required Description
data.type string Yes Resource type, must be "products"
data.id string No Resource ID (if provided, must match URL)
data.attributes.version int Yes Current version of the record (for conflict detection)
data.attributes.sku string No Product SKU
data.attributes.isAvailable boolean No Availability flag
data.attributes.translations array No Array of translation objects
...other attributes mixed No Any other Product fields (camelCase)

Translation Object Structure (flat array format):

Each object in translations array contains locale plus any translatable fields:

Field Type Required Description
locale string Yes Locale code (e.g., "de_CH", "en", "fr_CH")
name string No Product name
description string No Product description
webName string No Web display name
...other fields mixed No Any translatable field in camelCase

Benefits of flat array format: - Group multiple fields per locale in a single object - More intuitive and compact than field-by-field updates - One version increment for all translations

Response (200 OK)

For regular fields:

{
  "data": {
    "type": "products",
    "id": "1774",
    "attributes": {
      "version": 3,
      "updatedAt": "2026-01-18T18:28:35+01:00",
      "sku": "NEW-SKU",
      "isAvailable": true
    }
  }
}

For translations:

{
  "data": {
    "type": "products",
    "id": "1774",
    "attributes": {
      "version": 3,
      "updatedAt": "2026-01-18T18:28:25+01:00",
      "translations": [
        {"locale": "de_CH", "name": "Deutscher Name", "description": "Beschreibung"},
        {"locale": "en", "name": "English Name"}
      ]
    }
  }
}

For mixed update:

{
  "data": {
    "type": "products",
    "id": "1774",
    "attributes": {
      "version": 3,
      "updatedAt": "2026-01-18T18:28:35+01:00",
      "sku": "NEW-SKU",
      "isAvailable": true,
      "translations": [
        {"locale": "de_CH", "name": "Deutscher Name"},
        {"locale": "en", "name": "English Name"}
      ]
    }
  }
}

Not Found (404):

{
  "errors": [{
    "status": "404",
    "title": "Not Found",
    "detail": "Product not found."
  }]
}

Conflict (409):

Returned when the provided version doesn't match the current version:

{
  "errors": [{
    "status": "409",
    "title": "Conflict",
    "detail": "This record was modified by another user.",
    "meta": {
      "currentVersion": 3,
      "currentData": {
        "sku": "OTHER-SKU"
      }
    }
  }]
}

Pusher Event

On successful update, a ProductFieldUpdated event is broadcast to other users:

  • Channel: {workspaceId}.product.{productId} (presence channel)
  • Event: .field.updated
  • Payload:
    {
      "fields": {
        "sku": "NEW-SKU",
        "isAvailable": true
      },
      "version": 3,
      "updatedBy": {
        "id": 1,
        "name": "John",
        "color": "#3B82F6"
      }
    }
    

Example Request

Translations update (flat array format):

curl -X PATCH "http://127.0.0.1/api/aiconeo/products/457" \
  -H "Accept: application/vnd.api+json" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "data": {
      "type": "products",
      "id": "457",
      "attributes": {
        "version": 2,
        "translations": [
          {"locale": "de_CH", "name": "Deutscher Name", "description": "Beschreibung"},
          {"locale": "en", "name": "English Name"}
        ]
      }
    }
  }'

Mixed update (translations + regular fields):

curl -X PATCH "http://127.0.0.1/api/aiconeo/products/457" \
  -H "Accept: application/vnd.api+json" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "data": {
      "type": "products",
      "id": "457",
      "attributes": {
        "version": 2,
        "sku": "NEW-SKU-123",
        "isAvailable": true,
        "translations": [
          {"locale": "de_CH", "webName": "produkt-web"},
          {"locale": "en", "webName": "product-web"}
        ]
      }
    }
  }'


Product Focus (Optional)

Endpoint: POST /api/aiconeo/products/{id}/focus

Broadcast a focus change to other users viewing the same product. This is optional - clients can also use Pusher whispers directly.

Request Body

{
  "focusedField": "name"
}

Or to clear focus:

{
  "focusedField": null
}

Response (200 OK)

{
  "data": {
    "type": "products",
    "id": "1774",
    "attributes": {
      "focusedField": "name"
    }
  }
}

Not Found (404):

{
  "errors": [{
    "status": "404",
    "title": "Not Found",
    "detail": "Product not found."
  }]
}

Pusher Event

  • Channel: {workspaceId}.product.{productId} (presence channel)
  • Event: .focus.changed
  • Payload:
    {
      "focusedField": "name",
      "user": {
        "id": 1,
        "name": "John",
        "color": "#3B82F6"
      }
    }
    

Product Presence Channel

For collaborative editing, clients should subscribe to the product's presence channel:

Channel: presence-{workspaceId}.product.{productId}

Where workspaceId is extracted from the APP_URL path (e.g., https://app.aico.swiss/1234512345).

Channel Events

Event Description
pusher:subscription_succeeded Initial list of users viewing the product
pusher:member_added New user started viewing
pusher:member_removed User stopped viewing
.field.updated Product field was updated
.initialized Background initialization completed
.focus.changed User focus changed

User Object in Presence

{
  "id": 1,
  "name": "John",
  "color": "#3B82F6"
}

The color is deterministically generated from the user ID for consistent display.


Data Types & Update Scopes

The product update endpoint (PATCH /api/aiconeo/products/{id}) uses optimistic locking (version checking) to prevent conflicts. Here's how different data types are handled:

Protected by Product Version (Phase 1)

These fields increment the product version and are conflict-checked:

Direct Product Fields

All fields in the Product model's $fillable array can be updated via the PATCH endpoint. Use camelCase in API requests:

Category Fields (camelCase for API)
Identifiers sku, barcode, manufacturerSku, urlHandle
Availability isAvailable, isBundle, isGiftCard, isDiscontinued, isAgeRestricted, isAvailableOnAicoPos, isAvailableInAppShop, releaseDate
Manufacturing weightValue, weightUnit, grossLength, grossHeight, grossWidth, grossDiameter, dimensionUnit, netWeightValue, netWeightUnit, netLength, netHeight, netWidth, netDiameter, grossVolume, netVolume, provenance, hsCode, maxQuantityPerOrder, unitContents
Shipping isSuitableForShipping, isMondayBlocked, isTuesdayBlocked, isWednesdayBlocked, isThursdayBlocked, isFridayBlocked, isSaturdayBlocked, isSundayBlocked, isCustomerDayBlockEnabled
Accounting fibuProceeds, fibuExpenses, taxCode
Foreign Keys categoryId, subcategoryId, subsubcategoryId, brandId, statusId, manufacturerId, colorId

Example - Update category:

{
  "data": {
    "version": 3,
    "categoryId": 15,
    "subcategoryId": 42
  }
}

Example - Update unit contents:

{
  "data": {
    "version": 3,
    "unitContents": 500
  }
}

Translations

Update via the translations array using flat array format (locale + field values per object). These also increment the product version.

Field Description
name Product name
webName Web display name
externalName External/export name
description Full description
smallDescription Short description
image Translation-specific image path
manufacturer Manufacturer text
ingredients Ingredients text
pageTitle SEO page title
metaDescription SEO meta description
notes, sideEffects, contraindications, interactions, usageInstructions Additional text fields

Example - Translations update (flat array format):

{
  "data": {
    "version": 3,
    "translations": [
      {"locale": "de_CH", "name": "Deutscher Name", "description": "Deutsche Beschreibung"},
      {"locale": "en", "name": "English Name"}
    ]
  }
}

Benefits of flat array format: - Group multiple fields per locale in a single object - More intuitive and compact than field-by-field updates - One version increment for all translations - Atomic operation - all translations succeed or none - Can be combined with regular field updates in the same request

Example - Mixed update (translations + regular fields):

{
  "data": {
    "version": 3,
    "sku": "NEW-SKU",
    "isAvailable": true,
    "translations": [
      {"locale": "de_CH", "name": "Deutscher Name"}
    ]
  }
}

Custom Fields

Custom fields are stored in a flattened table (custom_fields_products) keyed by product_id and locale. The read endpoint returns them as a dynamic object:

{
  "customFields": {
    "1_spielername": "John Doe",
    "currency_1_chf": 49.90,
    "warehouse_10_1100__backoffice": 21,
    "total_stock": 44
  }
}

How custom fields work: - Field names are prefixed with their ID (e.g., 1_spielername = custom field #1) - Special computed fields: currency_* (prices), warehouse_* (stock by warehouse), total_stock - Custom fields are read-only in the current aiconeo API - For updates, use the existing custom fields service or v1 API

Relation Updates via Foreign Keys

To update relations like brand or category, pass the foreign key ID in camelCase:

{
  "data": {
    "version": 3,
    "brandId": 5,
    "categoryId": 12,
    "statusId": 2
  }
}

The response will include the updated version, and the next read will show the related entity data.


Product Prices

Endpoint: GET /api/aiconeo/products/{id}/prices

Get all product prices and variant prices for a specific product.

Response Structure

Field Description
productPrices Array of product-level prices
variantPrices Array of variant-level prices

Price Fields

Field Type Description
id int Price ID
value float Base price value
buyingPrice float Buying/cost price
discountPrice float Discounted price
unitPrice float Unit price (for price per kg, etc.)
priceType string Price type (BASE, SCHEDULED, etc.)
activeFrom datetime Start date for scheduled prices
activeUntil datetime End date for scheduled prices
isActive boolean Whether price is currently active
currency object Currency info (id, name)

Example Request

GET /api/aiconeo/products/123/prices

Example Response

{
  "data": {
    "id": 123,
    "type": "product-prices",
    "attributes": {
      "productPrices": [
        {
          "id": 1,
          "value": 99.90,
          "discountPrice": 79.90,
          "priceType": "BASE",
          "isActive": true,
          "currency": { "id": 1, "name": "CHF" }
        }
      ],
      "variantPrices": [
        {
          "id": 10,
          "variantId": 5,
          "variantSku": "PRD-001-S",
          "variantName": "Small",
          "value": 99.90,
          "currency": { "id": 1, "name": "CHF" }
        }
      ]
    }
  }
}

Product Stocks

Endpoint: GET /api/aiconeo/products/{id}/stocks

Get all stock levels for a product across all warehouses.

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 100 500 Items per page
filter[warehouseId] string - - Filter by warehouse ID(s)
filter[variantId] string - - Filter by variant ID(s), use 'null' for product-level

Stock Fields

Field Type Description
id int Stock record ID
sku string Stock SKU
amount int Current stock amount
minimumStockAmount int Minimum stock threshold
unconfirmedReservedAmount int Pending reservations
overallAvailableAmount int Total available stock
blockedAmount int Manually blocked stock
warehouse object Warehouse info
variant object Variant info (if variant-level stock)
binLocation object Bin location info (id, code, barcode)

Example Request

# Get all stocks
GET /api/aiconeo/products/123/stocks

# Filter by warehouse
GET /api/aiconeo/products/123/stocks?filter[warehouseId]=1,2

# Get only product-level stocks (no variants)
GET /api/aiconeo/products/123/stocks?filter[variantId]=null

Example Response

{
  "data": {
    "id": 123,
    "type": "product-stocks",
    "attributes": {
      "stocks": [
        {
          "id": 1,
          "sku": "PRD-001",
          "amount": 150,
          "minimumStockAmount": 10,
          "overallAvailableAmount": 150,
          "warehouse": { "id": 1, "name": "Main Warehouse", "country": "CH" },
          "variant": null,
          "binLocation": { "id": 5, "code": "A-1-1", "barcode": "BIN-A-1-1" }
        }
      ]
    }
  },
  "meta": {
    "page": { "currentPage": 1, "total": 25, "perPage": 100 }
  }
}

Product Stock Changes

Endpoint: GET /api/aiconeo/products/{id}/stock-changes

Get the history of all stock changes for a product.

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 50 200 Items per page
sort string -createdAt - Sort field (id, createdAt, changeAmount, newAmount)
filter[warehouseId] string - - Filter by warehouse ID(s)
filter[variantId] string - - Filter by variant ID(s)
filter[changeTypeId] string - - Filter by change type ID(s)
filter[createdAt] mixed - - Filter by date (supports dateBetween, before, after)

Stock Change Fields

Field Type Description
id int Change record ID
oldAmount int Stock amount before change
changeAmount int Amount changed (+/-)
newAmount int Stock amount after change
author string Who made the change
note string Change note/reason
changeType object Type of change (Sale, Restock, Correction, etc.)
warehouse object Warehouse info
variant object Variant info
user object User who made the change
shopifyShop object Shopify shop (for online sales)
createdAt datetime When the change occurred

Example Request

# Get recent changes
GET /api/aiconeo/products/123/stock-changes

# Filter by date range
GET /api/aiconeo/products/123/stock-changes?filter[createdAt][dateBetween]=2025-01-01,2025-12-31

# Filter by warehouse and sort by change amount
GET /api/aiconeo/products/123/stock-changes?filter[warehouseId]=1&sort=-changeAmount

Example Response

{
  "data": {
    "id": 123,
    "type": "product-stock-changes",
    "attributes": {
      "stockChanges": [
        {
          "id": 500,
          "oldAmount": 150,
          "changeAmount": -5,
          "newAmount": 145,
          "author": "System",
          "note": "Online sale #12345",
          "changeType": { "id": 1, "name": "Shopify Online Sale" },
          "warehouse": { "id": 1, "name": "Main Warehouse" },
          "variant": null,
          "shopifyShop": { "id": 1, "name": "My Store" },
          "createdAt": "2025-01-03 14:30:00"
        }
      ]
    }
  },
  "meta": {
    "page": { "currentPage": 1, "total": 1250, "perPage": 50 }
  }
}

Articles

Endpoint: GET /api/aiconeo/articles

Returns a combined view of products and variants as "articles": - Products with variants: only the variants show up as individual articles (product itself is hidden) - Products without variants: the product shows up as an article

This provides a unified SKU-level view for inventory and sales purposes.

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 25 100 Items per page
sort string -productUpdatedAt - Sort field(s), prefix with - for descending

Sortable Fields

Field Description
id Article ID (variant ID or product ID)
variantId Variant ID
productId Product ID
sku SKU (uses variant SKU if available, otherwise product SKU)
variantSku Variant SKU only
productSku Product SKU only
barcode Barcode (uses variant if available)
name Product name
productName Product name
variantName Variant name
brandName Brand name
categoryName Category name
price Price (uses variant price if available)
productPrice Product price
variantPrice Variant price
createdAt / productCreatedAt Product creation date
updatedAt / productUpdatedAt Product update date
stock{WarehouseKey} Stock amount for specific warehouse

Filters

Filter Description
filter[sku] Search by SKU (both product and variant)
filter[barcode] Search by barcode (both product and variant)
filter[name] Search by name (both product and variant)
filter[productId] Filter by product ID(s), comma-separated
filter[variantId] Filter by variant ID(s), comma-separated
filter[brandId] Filter by brand ID(s), comma-separated
filter[brandName] Filter by brand name
filter[categoryId] Filter by category ID(s), includes all category levels
filter[hasVariants] true = only variants, false = only products without variants
filter[search] Global search (SKU, barcode, name)
filter[createdAt] Date filter (supports operators)
filter[updatedAt] Date filter (supports operators)
filter[stock{WarehouseKey}] Stock amount filter per warehouse

Stock Filter Operators

Operator Example Description
eq filter[stockWarehouse1][eq]=10 Exact amount
gt filter[stockWarehouse1][gt]=0 Greater than
gte filter[stockWarehouse1][gte]=5 Greater than or equal
lt filter[stockWarehouse1][lt]=10 Less than
lte filter[stockWarehouse1][lte]=5 Less than or equal
inStock filter[stockWarehouse1][inStock]=true Has stock (> 0)
outOfStock filter[stockWarehouse1][outOfStock]=true Out of stock (≤ 0 or null)

Includes

Include Description
shopifyShops Shopify shop connections
tags Product tags

URL Fields (Full Storage URLs)

Field Description
image Article image (variant image if available, otherwise product image)
productImage Product image
variantImage Variant image

Response Structure

{
  "data": [
    {
      "id": "v13922",
      "type": "articles",
      "attributes": {
        "articleType": "variant",
        "productId": 1066,
        "variantId": 13922,
        "sku": "2408007",
        "barcode": "8056009571906",
        "manufacturerSku": null,
        "name": "Team Polo 24/25, 4XL",
        "productName": "Team Polo 24/25",
        "productWebName": "Team Polo 24/25",
        "variantName": "Team Polo 24/25, 4XL",
        "image": "http://example.com/storage/images/2408.png",
        "productImage": "http://example.com/storage/images/2408.png",
        "variantImage": null,
        "brand": { "id": 1, "name": "Nike" },
        "category": { "id": 20739, "name": "Trikot & Training" },
        "price": 63.83,
        "discountPrice": 18.50,
        "productPrice": 63.83,
        "productDiscountPrice": 18.50,
        "variantPrice": 63.83,
        "variantDiscountPrice": 18.50,
        "createdAt": "2024-06-21 11:01:18",
        "updatedAt": "2025-12-19 14:02:23",
        "stocks": {
          "warehouse_1_main": {
            "warehouseId": 1,
            "warehouseName": "Main Warehouse",
            "amount": 25,
            "availableAmount": 20
          },
          "warehouse_2_outlet": {
            "warehouseId": 2,
            "warehouseName": "Outlet",
            "amount": 5,
            "availableAmount": 5
          }
        },
        "shopifyShops": [
          { "id": 4, "name": "FCB Fanshop", "isActive": true }
        ],
        "tags": ["new", "sale"]
      }
    },
    {
      "id": "p1649",
      "type": "articles",
      "attributes": {
        "articleType": "product",
        "productId": 1649,
        "variantId": null,
        "sku": "801",
        "barcode": "4055134004505",
        "name": "Halskette mit Logoanhänger",
        "productName": "Halskette mit Logoanhänger",
        "variantName": null,
        "price": 23.13,
        "stocks": { ... }
      }
    }
  ],
  "meta": {
    "page": { "currentPage": 1, "total": 5000, "perPage": 25 },
    "warehouses": [
      { "id": 1, "key": "warehouse_1_main", "name": "Main Warehouse" },
      { "id": 2, "key": "warehouse_2_outlet", "name": "Outlet" }
    ]
  },
  "links": { "first": "...", "last": "...", "prev": null, "next": "..." }
}

Notes

  • Article id is prefixed with v for variants (e.g., v13922) and p for products (e.g., p1649)
  • articleType is either "variant" or "product"
  • Unified fields (sku, barcode, name, price, image) use variant values when available, otherwise product values
  • Stock amounts are included for all warehouses, listed in the meta.warehouses array
  • The default currency for prices is CHF

Categories

Endpoint: GET /api/aiconeo/categories

Returns categories in a hierarchical structure by default, with main categories (parent_id = null) containing nested subcategories.

Query Parameters

Parameter Type Default Max Description
nested bool true - Return hierarchical structure (true) or flat list (false)
maxDepth int 3 5 Maximum nesting depth for hierarchical structure
page[number] int 1 - Page number (only for flat list)
page[size] int 100 500 Items per page (only for flat list)

Filters

Filter Description
filter[id] Category ID(s), comma-separated
filter[parentId] Parent category ID(s), use null for main categories
filter[isAvailableInAppShop] Filter by app shop availability
filter[isFeaturedInBanner] Filter by featured in banner
filter[search] Search by name or web name

Includes

Include Description
shopifyShops Shopify shop connections with shopify ID and handle
productCount Number of products in category (includes all category levels)

URL Fields (Full Storage URLs)

Field Description
image Category main image
video Category video file
itemImage Category item image

Response Structure (Hierarchical)

{
  "data": [
    {
      "id": 10210,
      "type": "categories",
      "attributes": {
        "parentId": null,
        "name": "Bekleidung",
        "webName": "Bekleidung",
        "description": "<p>...</p>",
        "smallDescription": null,
        "seoText": "<p>...</p>",
        "urlHandle": "bekleidung",
        "pageTitle": null,
        "image": "http://example.com/storage/images/category.png",
        "video": null,
        "videoUrl": null,
        "itemImage": null,
        "color": null,
        "hasQuickOfferVisibility": true,
        "isOverridingProductDescriptionAllowed": false,
        "shouldRandomizeColorSort": false,
        "landingPageId": null,
        "hasDropInterval": false,
        "dropStartDate": null,
        "dropEndDate": null,
        "isAvailableInAppShop": false,
        "isFeaturedInBanner": false,
        "createdAt": "2022-04-08 08:11:58",
        "updatedAt": "2025-09-23 09:39:28",
        "subCategories": [
          {
            "id": 20744,
            "type": "categories",
            "attributes": {
              "parentId": 10210,
              "name": "Männer",
              "webName": "Männer",
              "urlHandle": "bekleidung-männer",
              "subCategories": [...]
            }
          }
        ],
        "productCount": 179
      }
    }
  ],
  "meta": {
    "totalCategories": 84,
    "mainCategories": 6
  }
}

Response Structure (Flat List)

{
  "data": [
    {
      "id": 10210,
      "type": "categories",
      "attributes": {
        "parentId": null,
        "name": "Bekleidung",
        "productCount": 179
      }
    }
  ],
  "meta": {
    "page": { "currentPage": 1, "total": 84, "perPage": 100 }
  },
  "links": { "first": "...", "last": "...", "prev": null, "next": "..." }
}

Product Types

Endpoint: GET /api/aiconeo/product-types

Returns all available product types. This is a static list based on the ProductType enum.

Response Structure

{
  "data": [
    { "name": "PHYSICAL", "label": "Default" },
    { "name": "SERVICE", "label": "Service" },
    { "name": "INGREDIENT", "label": "Ingredient" },
    { "name": "OPERATING_RESOURCE", "label": "Operating resource" },
    { "name": "PACKAGING", "label": "Packaging" }
  ]
}

Response Fields

Field Type Description
name string The enum case name (e.g., "PHYSICAL", "SERVICE") - use this value for API requests
label string Human-readable label for display in UI

Example Request

curl -X GET "http://127.0.0.1/api/aiconeo/product-types" \
  -H "Accept: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN"

Product Units

Endpoint: GET /api/aiconeo/product-units

Returns product units with pagination, filtering, and sorting following the JSON:API specification.

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 25 100 Items per page
sort string id - Sort field(s), prefix with - for descending
include string - - Comma-separated list of relationships to include

Sortable Fields

Field Description
id Product unit ID
name Translated name
shortName Translated short name
isDefault Whether this is the default unit
createdAt Creation timestamp
updatedAt Last update timestamp

Filter Parameters

Filter Type Description
filter[id] int/string Filter by ID(s), comma-separated
filter[name] string Filter by name (contains)
filter[shortName] string Filter by short name (contains)
filter[isDefault] bool Filter by default status
filter[hasUnitContents] bool Filter by unit contents flag
filter[search] string Global search on name and short name

Include Options

Include Description
translations All available translations for the product unit

Response Structure

{
  "data": [
    {
      "id": 1,
      "type": "product-units",
      "attributes": {
        "name": "Std",
        "shortName": "Std",
        "isDefault": false,
        "hasUnitContents": false,
        "createdAt": "2025-02-01 19:49:48",
        "updatedAt": "2025-02-01 19:49:48",
        "translations": [
          {
            "id": 1,
            "locale": "en",
            "name": "Hours",
            "shortName": "Hours"
          },
          {
            "id": 2,
            "locale": "de_CH",
            "name": "Std",
            "shortName": "Std"
          }
        ]
      }
    }
  ],
  "meta": {
    "page": {
      "currentPage": 1,
      "from": 1,
      "lastPage": 1,
      "perPage": 25,
      "to": 7,
      "total": 7
    }
  },
  "links": {
    "first": "...",
    "last": "...",
    "prev": null,
    "next": null
  }
}

Product Unit Attributes

Field Type Description
name string Translated name (based on Accept-Language header)
shortName string Translated short name
isDefault bool Whether this is the default unit
hasUnitContents bool Whether the unit has contents
createdAt datetime Creation timestamp
updatedAt datetime Last update timestamp
translations array All translations (only with include=translations)

Example Requests

Basic request:

curl -X GET "http://127.0.0.1/api/aiconeo/product-units" \
  -H "Accept: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN"

With search filter and translations:

curl -X GET "http://127.0.0.1/api/aiconeo/product-units?filter%5Bsearch%5D=St&include=translations" \
  -H "Accept: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN"

Filter by default status:

curl -X GET "http://127.0.0.1/api/aiconeo/product-units?filter%5BisDefault%5D=true" \
  -H "Accept: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN"


Product Statuses

Endpoints for retrieving product statuses used to track product workflow states.

Product Statuses List

Endpoint: GET /api/aiconeo/product-statuses

Returns a paginated list of product statuses.

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 25 100 Items per page
sort string id - Sort field(s), prefix with - for descending
filter[id] int/string - - Filter by ID (comma-separated for multiple)
filter[name] string - - Filter by name (supports text operators)
filter[color] string - - Filter by color
filter[isComplete] boolean - - Filter by completion status
filter[isInProgress] boolean - - Filter by in-progress status
filter[search] string - - Global search in name and web_name

Sortable Fields

Field Description
id Status ID
name Status name
color Status color
groupOrderNumber Order within the status group
isComplete Completion flag
isInProgress In-progress flag
createdAt Creation timestamp
updatedAt Last update timestamp

Response Structure

{
  "data": [
    {
      "id": 49,
      "type": "product-statuses",
      "attributes": {
        "name": "Draft",
        "webName": "Draft",
        "color": "#cccccc",
        "groupOrderNumber": 27,
        "isComplete": false,
        "isInProgress": false,
        "createdAt": "2022-04-06 09:22:13",
        "updatedAt": "2022-04-06 09:22:13"
      }
    }
  ],
  "meta": {
    "page": {
      "currentPage": 1,
      "from": 1,
      "lastPage": 1,
      "perPage": 25,
      "to": 5,
      "total": 5
    }
  },
  "links": {
    "first": "...",
    "last": "...",
    "prev": null,
    "next": null
  }
}

Response Fields

Field Type Description
id integer Status ID
name string Internal status name
webName string/null Display name for web interfaces
color string Hex color code (e.g., "#cccccc")
groupOrderNumber integer Order position within the status group
isComplete boolean Whether this status indicates completion
isInProgress boolean Whether this status indicates work in progress
createdAt string Creation timestamp
updatedAt string Last update timestamp

Example Requests

Get all product statuses:

curl -X GET "http://127.0.0.1/api/aiconeo/product-statuses" \
  -H "Accept: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN"

Filter by name:

curl -X GET "http://127.0.0.1/api/aiconeo/product-statuses?filter%5Bname%5D=Draft" \
  -H "Accept: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN"

Sort by name descending:

curl -X GET "http://127.0.0.1/api/aiconeo/product-statuses?sort=-name" \
  -H "Accept: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN"

Product Status Detail

Endpoint: GET /api/aiconeo/product-statuses/{id}

Returns a single product status by ID.

Path Parameters

Parameter Type Description
id integer The product status ID

Response Structure

{
  "data": {
    "id": 49,
    "type": "product-statuses",
    "attributes": {
      "name": "Draft",
      "webName": "Draft",
      "color": "#cccccc",
      "groupOrderNumber": 27,
      "isComplete": false,
      "isInProgress": false,
      "createdAt": "2022-04-06 09:22:13",
      "updatedAt": "2022-04-06 09:22:13"
    }
  }
}

Example Request

curl -X GET "http://127.0.0.1/api/aiconeo/product-statuses/49" \
  -H "Accept: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN"

Error Response (404)

{
  "errors": [
    {
      "status": "404",
      "title": "Not Found",
      "detail": "Product status with ID 999 not found."
    }
  ]
}

Collections

Endpoint: GET /api/aiconeo/collections

Returns product collections with optional includes for Shopify shops, conditions, and product count.

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 25 100 Items per page
sort string -id - Sort field(s), prefix with - for descending

Sortable Fields

Field Description
id Collection ID
name Collection name
webName Web name
conditionType Condition type
sortOrder Sort order
createdAt Creation date
updatedAt Last update date

Filters

Filter Description
filter[id] Collection ID(s), comma-separated
filter[name] Filter by name (supports operators)
filter[webName] Filter by web name (supports operators)
filter[urlHandle] Filter by URL handle (supports operators)
filter[conditionType] Filter by condition type (AND/OR)
filter[isPimMaster] Filter by PIM master status
filter[shopifyShopId] Filter by Shopify shop ID(s)
filter[search] Search by name, web name, or description
filter[createdAt] Filter by creation date (supports date operators)
filter[updatedAt] Filter by update date (supports date operators)

Includes

Include Description
shopifyShops Shopify shop connections with sync status
conditions Collection conditions (type, operator, value)
productCount Number of products in collection

URL Fields (Full Storage URLs)

Field Description
image Collection main image

Response Structure

{
  "data": [
    {
      "id": 2,
      "type": "collections",
      "attributes": {
        "name": "Meisterkollektion",
        "webName": "Meisterkollektion",
        "description": null,
        "internalDescription": null,
        "pageTitle": "Meisterkollektion",
        "metaDescription": null,
        "urlHandle": "meisterkollektion",
        "image": "http://example.com/storage/images/collection.png",
        "isNameTranslatable": false,
        "conditionType": "AND",
        "sortOrder": "NONE",
        "isPimMaster": false,
        "createdAt": "2022-02-15 13:50:08",
        "updatedAt": "2022-03-03 15:17:29",
        "shopifyShops": [
          {
            "id": 4,
            "name": "FCB Fanshop",
            "shopifyId": "gid://shopify/Collection/282260045964",
            "shopifyHandle": "meisterkollektion",
            "isActive": true,
            "existsInShop": true,
            "lastSyncStatus": "SYNCED"
          }
        ],
        "conditions": [
          {
            "id": 1,
            "type": "tag",
            "operator": "equals",
            "value": "meister"
          }
        ],
        "productCount": 15
      }
    }
  ],
  "meta": {
    "page": { "currentPage": 1, "total": 10, "perPage": 25 }
  },
  "links": { "first": "...", "last": "...", "prev": null, "next": "..." }
}

B2B Customers

Endpoint: GET /api/aiconeo/b2b

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 25 100 Items per page
sort string -id - Sort field(s)
include string - - Comma-separated relationships
filter[field] mixed - - Filter by field

Includes

Include Description Default
customerGroups Customer groups with translated names
crmTags CRM tags with translated names
customerTags Customer tags with title and color
sellers Associated sellers
brands Associated brands
customFields Custom fields

Sortable Fields

Field Description
id B2B ID
customerNumber Customer number
company Company name
firstName First name
lastName Last name
fullName Full name
email Email address
telephoneNumber Phone number
plz Postal code
address Address
location Location
website Website
createdAt Creation date
updatedAt Last update date
countryName Country name
customerReferenceNumber Reference number

Text Filters

Supports all text filter operators.

Filter Column
filter[customerNumber] Customer number
filter[company] Company name
filter[firstName] First name
filter[lastName] Last name
filter[email] Email
filter[telephoneNumber] Phone
filter[plz] Postal code
filter[address] Address
filter[location] Location
filter[website] Website
filter[customerReferenceNumber] Reference number
filter[countryName] Country name

DateTime Filters

Filter Column
filter[createdAt] Creation date
filter[updatedAt] Last update date

Relationship Filters

Filter Description Example
filter[id] B2B ID(s) filter[id]=1,2,3
filter[countryId] Country ID(s) filter[countryId]=1,2
filter[customerGroupId] Customer group ID(s) filter[customerGroupId]=5
filter[crmTagId] CRM tag ID(s) filter[crmTagId]=2,3
filter[customerTagId] Customer tag ID(s) filter[customerTagId]=1
filter[sellerId] Seller ID(s) filter[sellerId]=4

Global Search

Filter Description
filter[search] Searches: customer number, company, first name, last name, email, phone

Example Requests

# Basic request
GET /api/aiconeo/b2b

# With includes
GET /api/aiconeo/b2b?include=customerGroups,crmTags,sellers

# Filter by customer group
GET /api/aiconeo/b2b?filter[customerGroupId]=5&sort=company

Response

{
  "data": [
    {
      "id": 1,
      "type": "b2b",
      "attributes": {
        "customerNumber": 1234567,
        "company": "Example Company",
        "firstName": "John",
        "lastName": "Doe",
        "fullName": "John Doe",
        "email": "john@example.com",
        "telephoneNumber": "+41 12 345 67 89",
        "plz": "8000",
        "address": "Main Street 1",
        "location": "Zurich",
        "website": "https://example.com",
        "customerReferenceNumber": "REF-001",
        "createdAt": "2026-01-15 10:30:00",
        "updatedAt": "2026-01-15 11:00:00",
        "version": 1,
        "image": null,
        "country": {
          "id": 1,
          "name": "Switzerland"
        },
        "customerGroups": [
          { "id": 1, "name": "Retail" }
        ],
        "crmTags": [
          { "id": 1, "name": "VIP" }
        ],
        "customerTags": [
          { "id": 1, "title": "Priority", "color": "#FF0000" }
        ]
      }
    }
  ],
  "meta": {
    "page": {
      "currentPage": 1,
      "from": 1,
      "lastPage": 10,
      "perPage": 25,
      "to": 25,
      "total": 250
    }
  },
  "links": {
    "first": "...",
    "last": "...",
    "prev": null,
    "next": "..."
  }
}

B2B Detail

Endpoint: GET /api/aiconeo/b2b/{id}

Returns comprehensive details for a single B2B customer.

Path Parameters

Parameter Type Description
id int B2B ID

Response

Success (200):

{
  "data": {
    "id": 1,
    "type": "b2b",
    "attributes": {
      "customerNumber": 1234567,
      "customerReferenceNumber": "REF-001",
      "formOfAddress": "Mr",
      "firstName": "John",
      "lastName": "Doe",
      "fullName": "John Doe",
      "company": "Example Company",
      "addressSupplement": null,
      "plz": "8000",
      "location": "Zurich",
      "canton": "ZH",
      "address": "Main Street 1",
      "poBox": null,
      "coAddress": null,
      "email": "john@example.com",
      "emailExtended": null,
      "telephoneNumber": "+41 12 345 67 89",
      "telephoneNumberExtended": null,
      "secondaryPhoneNr": null,
      "fax": null,
      "website": "https://example.com",
      "secondWebsite": null,
      "vatNumber": "CHE-123.456.789",
      "companyNumber": "CH-123.4.567.890-1",
      "bankDetails": null,
      "businessCenterNumber": null,
      "collectiveAccount": null,
      "proceedsAccount": null,
      "proposalKst": null,
      "paymentConditionNumber": null,
      "birthDate": null,
      "generalInterest": null,
      "keywords": null,
      "description": "Company description",
      "communicationFormality": "formal",
      "invoicingMethod": "NORMAL",
      "productReturnMethod": "TRUST_BASIS",
      "priority": null,
      "latitude": 47.3769,
      "longitude": 8.5417,
      "isLead": false,
      "isSupplier": false,
      "isActive": true,
      "shouldStopAutomaticFlow": false,
      "image": "https://storage.example.com/images/b2b/1.jpg",
      "highResImage": null,
      "coverPhoto": null,
      "highResCoverPhoto": null,
      "createdAt": "2026-01-15 10:30:00",
      "updatedAt": "2026-01-15 11:00:00",
      "archivedAt": null,
      "lastMovementDate": null,
      "version": 5,
      "country": {
        "id": 1,
        "name": "Switzerland",
        "code": "CH"
      },
      "currency": {
        "id": 1,
        "name": "Swiss Franc",
        "key": "CHF"
      },
      "mainContact": {
        "id": 10,
        "firstName": "Jane",
        "lastName": "Doe",
        "email": "jane@example.com",
        "telephoneNumber": "+41 12 345 67 90"
      },
      "documentLanguage": {
        "id": 1,
        "name": "German",
        "locale": "de_CH"
      },
      "emailLanguage": {
        "id": 1,
        "name": "German",
        "locale": "de_CH"
      },
      "customerGroups": [
        { "id": 1, "name": "Retail" }
      ],
      "crmTags": [
        { "id": 1, "name": "VIP" }
      ],
      "customerTags": [
        { "id": 1, "title": "Priority", "color": "#FF0000" }
      ],
      "sellers": [
        { "id": 1, "name": "John Sales", "logo": null, "isActive": true }
      ],
      "brands": [
        { "id": 1, "name": "Brand A" }
      ],
      "contacts": [
        {
          "id": 10,
          "firstName": "Jane",
          "lastName": "Doe",
          "email": "jane@example.com",
          "phone": "+41 12 345 67 90",
          "image": null
        }
      ],
      "customFields": {
        "custom_field_1": "value1"
      }
    }
  }
}

Not Found (404):

{
  "errors": [
    {
      "status": "404",
      "title": "Not Found",
      "detail": "B2B with ID 999 not found."
    }
  ]
}

Forbidden (403):

{
  "errors": [
    {
      "status": "403",
      "title": "Forbidden",
      "detail": "You do not have permission to access this B2B."
    }
  ]
}

Example Request

curl -X GET "http://127.0.0.1/api/aiconeo/b2b/1" \
  -H "Accept: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN"

B2B Create

Endpoint: POST /api/aiconeo/b2b

Creates a new B2B entity with minimal data.

Request Body (JSON:API format)

{
  "data": {
    "type": "b2b",
    "attributes": {
      "company": "Company Name",
      "firstName": "John",
      "lastName": "Doe",
      "email": "john@example.com"
    }
  }
}
Field Type Required Description
data.type string Yes Resource type, must be "b2b"
data.attributes.company string No Company name
data.attributes.firstName string No First name
data.attributes.lastName string No Last name
data.attributes.email string No Email address

Note: The customerNumber is auto-generated and cannot be set via the API.

Response

Success (201 Created):

{
  "data": {
    "type": "b2b",
    "id": "1",
    "attributes": {
      "company": "Company Name",
      "customerNumber": 1234567,
      "version": 1,
      "createdAt": "2026-01-15T10:30:00+00:00"
    }
  }
}

Validation Error (400) - Invalid Type:

{
  "errors": [
    {
      "status": "400",
      "title": "Bad Request",
      "detail": "Invalid or missing resource type. Expected \"b2b\".",
      "source": {
        "pointer": "/data/type"
      }
    }
  ]
}

Example Request

curl -X POST "http://127.0.0.1/api/aiconeo/b2b" \
  -H "Accept: application/vnd.api+json" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"data": {"type": "b2b", "attributes": {"company": "New Company", "email": "contact@company.com"}}}'

B2B Update (Collaborative Editing)

Endpoint: PATCH /api/aiconeo/b2b/{id}

Updates B2B fields with optimistic locking (version checking) to prevent conflicts during collaborative editing.

Path Parameters

Parameter Type Description
id int B2B ID

Request Body (JSON:API format)

{
  "data": {
    "type": "b2b",
    "id": "123",
    "attributes": {
      "company": "Updated Company Name",
      "email": "newemail@example.com",
      "version": 5
    }
  }
}
Field Type Required Description
data.type string Yes Resource type, must be "b2b"
data.id string No Resource ID (if provided, must match URL)
data.attributes.version int Yes Current version of the record (for conflict detection)
data.attributes.company string No Company name
data.attributes.customerNumber string No Customer number
data.attributes.firstName string No First name
data.attributes.lastName string No Last name
data.attributes.email string No Email address
...other attributes mixed No Any other B2B fields

Response

Success (200 OK):

{
  "data": {
    "type": "b2b",
    "id": "123",
    "attributes": {
      "company": "Updated Company Name",
      "customerNumber": 1234567,
      "version": 6,
      "updatedAt": "2026-01-15T11:00:00+00:00"
    }
  }
}

Not Found (404):

{
  "errors": [
    {
      "status": "404",
      "title": "Not Found",
      "detail": "B2B not found."
    }
  ]
}

Validation Error (400) - Invalid Type:

{
  "errors": [
    {
      "status": "400",
      "title": "Bad Request",
      "detail": "Invalid or missing resource type. Expected \"b2b\".",
      "source": {
        "pointer": "/data/type"
      }
    }
  ]
}

Validation Error (400) - ID Mismatch:

{
  "errors": [
    {
      "status": "400",
      "title": "Bad Request",
      "detail": "Resource ID in body does not match URL.",
      "source": {
        "pointer": "/data/id"
      }
    }
  ]
}

Validation Error (400) - Missing Version:

{
  "errors": [
    {
      "status": "400",
      "title": "Bad Request",
      "detail": "Version is required for updates.",
      "source": {
        "pointer": "/data/attributes/version"
      }
    }
  ]
}

Conflict (409):

When another user has modified the record since you last loaded it:

{
  "errors": [
    {
      "status": "409",
      "title": "Conflict",
      "detail": "This record was modified by another user.",
      "meta": {
        "currentVersion": 6,
        "currentData": {
          "company": "Other User's Update",
          "email": "other@example.com"
        }
      }
    }
  ]
}

Example Request

curl -X PATCH "http://127.0.0.1/api/aiconeo/b2b/123" \
  -H "Accept: application/vnd.api+json" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"data": {"type": "b2b", "id": "123", "attributes": {"company": "Updated Name", "version": 5}}}'

B2C Customers

Endpoint: GET /api/aiconeo/b2c

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 25 100 Items per page
sort string -id - Sort field(s)
include string - - Comma-separated relationships
filter[field] mixed - - Filter by field

Includes

Include Description Default
companies Associated B2B companies
customerGroups Customer groups with translated names
crmTags CRM tags with translated names
customerTags Customer tags with title and color

Sortable Fields

Field Description
id B2C ID
contactNumber Contact number
firstName First name
lastName Last name
fullName Full name
email Email address
phone Phone number
plz Postal code
address Address
city City
createdAt Creation date
updatedAt Last update date
customerReferenceNumber Reference number

Text Filters

Supports all text filter operators.

Filter Column
filter[contactNumber] Contact number
filter[firstName] First name
filter[lastName] Last name
filter[email] Email
filter[phone] Phone
filter[plz] Postal code
filter[address] Address
filter[city] City
filter[customerReferenceNumber] Reference number

DateTime Filters

Filter Column
filter[createdAt] Creation date
filter[updatedAt] Last update date

Relationship Filters

Filter Description Example
filter[id] B2C ID(s) filter[id]=1,2,3
filter[customerGroupId] Customer group ID(s) filter[customerGroupId]=5
filter[crmTagId] CRM tag ID(s) filter[crmTagId]=2,3
filter[customerTagId] Customer tag ID(s) filter[customerTagId]=1
filter[companyId] B2B company ID(s) filter[companyId]=10,20

Global Search

Filter Description
filter[search] Searches: contact number, email, first name, last name, phone, full name

Example Requests

# Basic request
GET /api/aiconeo/b2c

# With includes
GET /api/aiconeo/b2c?include=companies,customerGroups

# Filter by company
GET /api/aiconeo/b2c?filter[companyId]=123

B2C Detail

Endpoint: GET /api/aiconeo/b2c/{id}

Returns comprehensive details for a single B2C contact.

Path Parameters

Parameter Type Description
id int B2C ID

Response

Success (200):

{
  "data": {
    "id": 1,
    "type": "b2c",
    "attributes": {
      "contactNumber": "CNT-001",
      "customerReferenceNumber": "REF-001",
      "title": "Mr",
      "formOfAddress": "Mr",
      "firstName": "John",
      "lastName": "Doe",
      "fullName": "John Doe",
      "company": "Example Company",
      "addressSupplement": null,
      "plz": "8000",
      "city": "Zurich",
      "canton": "ZH",
      "address": "Main Street 1",
      "poBox": null,
      "coAddress": null,
      "email": "john@example.com",
      "phone": "+41 12 345 67 89",
      "secondaryPhoneNr": null,
      "fax": null,
      "fatherMobile": null,
      "fatherEmail": null,
      "motherMobile": null,
      "motherEmail": null,
      "website": "https://example.com",
      "secondWebsite": null,
      "facebook": null,
      "youtube": null,
      "xing": null,
      "instagram": null,
      "linkedin": "https://linkedin.com/in/johndoe",
      "twitter": null,
      "whatsapp": null,
      "birthDate": "1985-05-15",
      "birthplace": "Zurich",
      "nationality": "Swiss",
      "height": null,
      "weight": null,
      "hobbies": null,
      "ahvNumber": null,
      "sflId": null,
      "jsNumber": null,
      "playerPassNumber": null,
      "parentClub": null,
      "currentClub": null,
      "soccerNation": null,
      "collectiveAccount": null,
      "proceedsAccount": null,
      "proposalKst": null,
      "paymentConditionNumber": null,
      "position": "Manager",
      "description": "Contact description",
      "keywords": null,
      "language": "de_CH",
      "generalInterest": 3,
      "communicationFormality": "ALWAYS_FORMAL_YOU",
      "invoicingMethod": "NORMAL",
      "author": null,
      "latitude": "47.3769",
      "longitude": "8.5417",
      "isLead": false,
      "isCustomer": true,
      "isActive": true,
      "isSpamContact": false,
      "isNewsletterActive": true,
      "isRegistrationRequest": false,
      "shouldDisplayOnWebsite": false,
      "isEmailVisibleOnLandingpageShopify": false,
      "shouldStopAutomaticFlow": false,
      "isUsingOwnPopUps": false,
      "isDefaultPosCustomer": false,
      "image": "https://storage.example.com/images/b2c/1.jpg",
      "coverPhoto": null,
      "gif": null,
      "createdAt": "2026-01-15 10:30:00",
      "updatedAt": "2026-01-15 11:00:00",
      "archivedAt": null,
      "lastMovementDate": null,
      "entryDate": null,
      "exitDate": null,
      "activeFrom": null,
      "activeUntil": null,
      "version": 5,
      "country": {
        "id": 1,
        "name": "Switzerland",
        "code": "CH"
      },
      "customerGroups": [
        { "id": 1, "name": "Retail" }
      ],
      "crmTags": [
        { "id": 1, "name": "VIP" }
      ],
      "customerTags": [
        { "id": 1, "title": "Priority", "color": "#FF0000" }
      ],
      "companies": [
        {
          "id": 10,
          "company": "Example Company",
          "customerNumber": 1234567,
          "email": "info@example.com",
          "telephoneNumber": "+41 12 345 67 00"
        }
      ]
    }
  }
}

Not Found (404):

{
  "errors": [
    {
      "status": "404",
      "title": "Not Found",
      "detail": "B2C with ID 999 not found."
    }
  ]
}

Forbidden (403):

{
  "errors": [
    {
      "status": "403",
      "title": "Forbidden",
      "detail": "You do not have permission to access this B2C."
    }
  ]
}

Example Request

curl -X GET "http://127.0.0.1/api/aiconeo/b2c/1" \
  -H "Accept: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN"

B2C Create

Endpoint: POST /api/aiconeo/b2c

Creates a new B2C entity with minimal data.

Request Body (JSON:API format)

{
  "data": {
    "type": "b2c",
    "attributes": {
      "firstName": "Jane",
      "lastName": "Smith",
      "email": "jane@example.com",
      "phone": "+41 79 123 45 67"
    }
  }
}
Field Type Required Description
data.type string Yes Resource type, must be "b2c"
data.attributes.firstName string No First name
data.attributes.lastName string No Last name
data.attributes.email string No Email address
data.attributes.phone string No Phone number

Note: The contactNumber is auto-generated and cannot be set via the API.

Response

Success (201 Created):

{
  "data": {
    "type": "b2c",
    "id": "1",
    "attributes": {
      "firstName": "Jane",
      "lastName": "Smith",
      "contactNumber": "CNT-001",
      "version": 1,
      "createdAt": "2026-01-15T10:30:00+00:00"
    }
  }
}

Bad Request (400):

{
  "errors": [{
    "status": "400",
    "title": "Bad Request",
    "detail": "Invalid or missing resource type. Expected \"b2c\".",
    "source": { "pointer": "/data/type" }
  }]
}

Example Request

curl -X POST "http://127.0.0.1/api/aiconeo/b2c" \
  -H "Accept: application/vnd.api+json" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"data": {"type": "b2c", "attributes": {"firstName": "Jane", "lastName": "Smith", "email": "jane@example.com"}}}'

B2C Update (Collaborative Editing)

Endpoint: PATCH /api/aiconeo/b2c/{id}

Updates B2C fields with optimistic locking (version checking) to prevent conflicts during collaborative editing.

Path Parameters

Parameter Type Description
id int B2C ID

Request Body (JSON:API format)

{
  "data": {
    "type": "b2c",
    "id": "456",
    "attributes": {
      "firstName": "Janet",
      "email": "janet.new@example.com",
      "version": 3
    }
  }
}
Field Type Required Description
data.type string Yes Resource type, must be "b2c"
data.id string No Resource ID (if provided, must match URL)
data.attributes.version int Yes Current version of the record (for conflict detection)
data.attributes.firstName string No First name
data.attributes.lastName string No Last name
data.attributes.email string No Email address
data.attributes.phone string No Phone number
...other attributes mixed No Any other B2C fields

Response

Success (200 OK):

{
  "data": {
    "type": "b2c",
    "id": "456",
    "attributes": {
      "version": 4,
      "updatedAt": "2026-01-15T11:00:00+00:00",
      "firstName": "Janet",
      "email": "janet.new@example.com"
    }
  }
}

Not Found (404):

{
  "errors": [{
    "status": "404",
    "title": "Not Found",
    "detail": "B2C not found."
  }]
}

Bad Request (400):

{
  "errors": [{
    "status": "400",
    "title": "Bad Request",
    "detail": "Version is required for updates.",
    "source": { "pointer": "/data/attributes/version" }
  }]
}

Conflict (409):

When another user has modified the record since you last loaded it:

{
  "errors": [{
    "status": "409",
    "title": "Conflict",
    "detail": "This record was modified by another user.",
    "meta": {
      "currentVersion": 4,
      "currentData": {
        "firstName": "Other User's Update",
        "email": "other@example.com"
      }
    }
  }]
}

Example Request

curl -X PATCH "http://127.0.0.1/api/aiconeo/b2c/456" \
  -H "Accept: application/vnd.api+json" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"data": {"type": "b2c", "id": "456", "attributes": {"firstName": "Janet", "version": 3}}}'

Orders

Endpoint: GET /api/aiconeo/orders

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 25 100 Items per page
sort string -createdAt - Sort field(s), prefix with - for descending
include string - - Comma-separated list of relationships
filter[field] mixed - - Filter by field

Includes

Include Description Default
salesArea Sales area
salesChannel Sales channel/workflow
project Associated project with deal status
createdByUser User who created the order
warehouse Warehouse
shopifyShop Shopify shop (via ecommerce order)
preorder Preorder info with welcome message
orderDocuments Count of order documents

Sortable Fields

Field Description
id Order ID
orderNumber Order number
externalOrderNumber External order number
title Order title
date Order date
preorderDate Preorder date
buyerName Buyer name
openAmount Open amount
createdAt Creation date
updatedAt Last update date
statusName Status name
substatusName Substatus name
salesAreaName Sales area name
salesChannelName Sales channel name
warehouseName Warehouse name
sellerName Seller name

Text Filters

Supports all text filter operators.

Filter Column
filter[orderNumber] Order number
filter[externalOrderNumber] External order number
filter[title] Order title
filter[buyerName] Buyer name
filter[statusName] Status name
filter[substatusName] Substatus name
filter[salesAreaName] Sales area name
filter[salesChannelName] Sales channel name
filter[warehouseName] Warehouse name
filter[currencyName] Currency name

DateTime Filters

Supports all datetime filter operators.

Filter Column
filter[createdAt] Creation date
filter[updatedAt] Last update date
filter[date] Order date
filter[preorderDate] Preorder date
filter[archivedAt] Archived date

Relationship Filters

Filter Description Example
filter[id] Order ID(s) filter[id]=1,2,3
filter[statusId] Status ID(s) filter[statusId]=5,10
filter[substatusId] Substatus ID(s) filter[substatusId]=3
filter[salesAreaId] Sales area ID(s) filter[salesAreaId]=1,2
filter[salesChannelId] Sales channel ID(s) filter[salesChannelId]=1
filter[warehouseId] Warehouse ID(s) filter[warehouseId]=3
filter[projectId] Project ID(s) filter[projectId]=100
filter[currencyId] Currency ID(s) filter[currencyId]=1
filter[ecommerceOrderId] Ecommerce order ID(s) filter[ecommerceOrderId]=50

Boolean Filters

Filter Description Example
filter[isChecked] Is checked filter[isChecked]=true
filter[isFlowRunning] Is flow running filter[isFlowRunning]=false
filter[isArchived] Is archived filter[isArchived]=false

Numeric Filters

Filter Description Example
filter[openAmountMin] Minimum open amount filter[openAmountMin]=100
filter[openAmountMax] Maximum open amount filter[openAmountMax]=1000

Global Search

Filter Description
filter[search] Searches: order number, external order number, title, buyer name

Example Requests

# Basic request
GET /api/aiconeo/orders

# With includes and pagination
GET /api/aiconeo/orders?include=salesArea,project,warehouse&page[number]=1&page[size]=50

# Filter by status
GET /api/aiconeo/orders?filter[statusId]=5&sort=-createdAt

# Filter by date range
GET /api/aiconeo/orders?filter[date][dateBetween]=2024-01-01,2024-12-31

# Active orders only
GET /api/aiconeo/orders?filter[isArchived]=false&filter[statusId]=1,2,3

# Complex query
GET /api/aiconeo/orders?include=project,shopifyShop&filter[salesAreaId]=2&filter[openAmountMin]=500&sort=-openAmount

Order Documents

Endpoint: GET /api/aiconeo/order-documents

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 25 100 Items per page
sort string -createdAt - Sort field(s), prefix with - for descending
include string - - Comma-separated list of relationships
filter[field] mixed - - Filter by field

Includes

Include Description Default
warehouse Warehouse information
createdByUser User who created the document
currency Currency (id, name)
positionGroup Position group totals
trackingNumbers Tracking numbers
payments Payment matchings
childDocuments Count of child documents

Sortable Fields

Field Description
id Document ID
documentNumber Document number
type Document type
date Document date
validUntil Valid until date
totalExclVat Total excl. VAT
totalInclVat Total incl. VAT
deliveryDate Delivery date
dueDate Due date
createdAt Creation date
updatedAt Last update date
orderNumber Order number
buyerName Buyer name
orderTitle Order title
documentTypeName Document type name
documentStatusName Document status name
warehouseName Warehouse name

Text Filters

Supports all text filter operators.

Filter Column
filter[documentNumber] Document number
filter[type] Document type
filter[referenceNumber] Reference number
filter[titleText] Title text
filter[orderNumber] Order number
filter[orderTitle] Order title
filter[buyerName] Buyer name
filter[externalOrderNumber] External order number
filter[documentTypeName] Document type name
filter[documentStatusName] Document status name
filter[warehouseName] Warehouse name

DateTime Filters

Supports all datetime filter operators.

Filter Column
filter[createdAt] Creation date
filter[updatedAt] Last update date
filter[date] Document date
filter[validUntil] Valid until
filter[deliveryDate] Delivery date
filter[dueDate] Due date

Relationship Filters

Filter Description Example
filter[id] Document ID(s) filter[id]=1,2,3
filter[orderId] Order ID(s) filter[orderId]=10,20
filter[orderDocumentTypeId] Document type ID(s) filter[orderDocumentTypeId]=1,2
filter[documentStatusId] Status ID(s) filter[documentStatusId]=5
filter[warehouseId] Warehouse ID(s) filter[warehouseId]=3
filter[parentDocumentId] Parent document ID(s) filter[parentDocumentId]=100

Boolean Filters

Filter Description Example
filter[isCancelled] Is cancelled filter[isCancelled]=true
filter[isActive] Is active filter[isActive]=true
filter[isArchived] Is archived filter[isArchived]=false

Numeric Filters

Filter Description Example
filter[totalExclVatMin] Minimum total excl. VAT filter[totalExclVatMin]=100
filter[totalExclVatMax] Maximum total excl. VAT filter[totalExclVatMax]=1000
filter[totalInclVatMin] Minimum total incl. VAT filter[totalInclVatMin]=100
filter[totalInclVatMax] Maximum total incl. VAT filter[totalInclVatMax]=1000

Global Search

Filter Description
filter[search] Searches: document number, reference number, order number, buyer name, order title

Example Requests

# Basic request
GET /api/aiconeo/order-documents

# With includes and pagination
GET /api/aiconeo/order-documents?include=warehouse,positionGroup,trackingNumbers&page[number]=1&page[size]=50

# Filter by order
GET /api/aiconeo/order-documents?filter[orderId]=123

# Filter invoices by date range
GET /api/aiconeo/order-documents?filter[orderDocumentTypeId]=3&filter[date][dateBetween]=2024-01-01,2024-12-31

# Active non-cancelled documents only
GET /api/aiconeo/order-documents?filter[isActive]=true&filter[isCancelled]=false&filter[isArchived]=false

Ecommerce Orders

Endpoint: GET /api/aiconeo/ecommerce-orders

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 25 100 Items per page
sort string -createdAt - Sort field(s), prefix with - for descending
include string - - Comma-separated list of relationships
filter[field] mixed - - Filter by field

Includes

Include Description Default
seller Seller via debtor
currency Currency (id, name)
aicoShopOrder Aico shop order with payment session status
shippingCountry Shipping country
billingCountry Billing country
lineItems Order line items
order Associated AICO order

Sortable Fields

Field Description
id Order ID
orderNumber Order number
externalOrderNumber External order number
sageOrderNumber Sage order number
date Order date
totalPrice Total price
financialStatus Financial status
fulfillmentStatus Fulfillment status
shipmentStatus Shipment status
createdAt Creation date
updatedAt Last update date
customerName Customer name
organisationName Organisation/company name
shopifyShopName Shopify shop name
sellerName Seller name

Text Filters

Supports all text filter operators.

Filter Column
filter[orderNumber] Order number
filter[externalOrderNumber] External order number
filter[sageOrderNumber] Sage order number
filter[sageExternalNumber] Sage external number
filter[customerName] Customer name (computed)
filter[organisationName] B2B company name
filter[shopifyShopName] Shopify shop name
filter[sellerName] Seller name
filter[financialStatus] Financial status
filter[fulfillmentStatus] Fulfillment status
filter[shipmentStatus] Shipment status
filter[tags] Tags
filter[note] Note
filter[paymentType] Payment type
filter[cancelReason] Cancel reason
filter[shippingCity] Shipping city
filter[shippingZip] Shipping zip
filter[billingCity] Billing city
filter[billingZip] Billing zip

DateTime Filters

Supports all datetime filter operators.

Filter Column
filter[createdAt] Creation date
filter[updatedAt] Last update date
filter[date] Order date
filter[deliveryDate] Delivery date
filter[cancelledAt] Cancelled date

Relationship Filters

Filter Description Example
filter[id] Order ID(s) filter[id]=1,2,3
filter[shopId] Shop ID(s) filter[shopId]=5,10
filter[customerId] Customer ID(s) filter[customerId]=100
filter[aicoPosOrderId] AICO POS order ID(s) filter[aicoPosOrderId]=50

Boolean Filters

Filter Description Example
filter[isCancelled] Is cancelled filter[isCancelled]=false
filter[isPos] Is POS order filter[isPos]=true
filter[hasTransactionNumber] Has transaction number filter[hasTransactionNumber]=true

Numeric Filters

Filter Description Example
filter[totalPriceMin] Minimum total price filter[totalPriceMin]=50
filter[totalPriceMax] Maximum total price filter[totalPriceMax]=500

Global Search

Filter Description
filter[search] Searches: order number, external order number, sage order number, customer name, organisation name

Example Requests

# Basic request
GET /api/aiconeo/ecommerce-orders

# With includes and pagination
GET /api/aiconeo/ecommerce-orders?include=seller,currency,lineItems&page[number]=1&page[size]=50

# Filter by shop
GET /api/aiconeo/ecommerce-orders?filter[shopId]=5&sort=-createdAt

# Filter by status
GET /api/aiconeo/ecommerce-orders?filter[fulfillmentStatus][equals]=fulfilled&filter[isCancelled]=false

# Filter by date range
GET /api/aiconeo/ecommerce-orders?filter[date][dateBetween]=2024-01-01,2024-12-31

# POS orders only
GET /api/aiconeo/ecommerce-orders?filter[isPos]=true

# Complex query
GET /api/aiconeo/ecommerce-orders?include=lineItems,order&filter[shopId]=3&filter[financialStatus][equals]=paid&filter[totalPriceMin]=100

Tasks

Endpoint: GET /api/aiconeo/tasks

High-performance task list endpoint with advanced filtering, sorting, and relationship loading.

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 25 100 Items per page
sort string -id - Sort field(s), prefix with - for descending
include string - - Comma-separated list of relationships
filter[field] mixed - - Filter by field

Includes

Include Description Default
mainAssignee Main assignee user with profile photo
creator User who created the task
project Associated project
type Task type with color
customer B2B customer
assignees All assignees (JSON aggregation)
watchers Task watchers (JSON aggregation)
tags Task tags (JSON aggregation)
subTasks Subtasks count
timer Timer information

Sortable Fields

Field Description
id Task ID
title Task title
startDate Start date
dueDate Due date
deadline Deadline
priority Priority
estimation Time estimation
billingStatus Billing status
createdAt Creation date
updatedAt Last update date
statusName Status name
projectName Project name
taskTypeName Task type name

Text Filters

Supports all text filter operators.

Filter Column
filter[title] Task title
filter[description] Description
filter[website] Website
filter[notes] Notes

DateTime Filters

Supports all datetime filter operators.

Filter Column
filter[startDate] Start date
filter[dueDate] Due date
filter[deadline] Deadline
filter[createdAt] Creation date
filter[updatedAt] Last update date

Relationship Filters

Filter Description Example
filter[id] Task ID(s) filter[id]=1,2,3
filter[statusId] Status ID(s) filter[statusId]=5,10
filter[projectId] Project ID(s) filter[projectId]=3
filter[taskTypeId] Task type ID(s) filter[taskTypeId]=1,2
filter[assigneeId] Main assignee ID(s) filter[assigneeId]=5
filter[creatorId] Creator ID(s) filter[creatorId]=10
filter[parentId] Parent task ID(s), use null for top-level filter[parentId]=null

Other Filters

Filter Description Example
filter[priority] Priority value(s) filter[priority]=High,Critical
filter[billingStatus] Billing status value(s) filter[billingStatus]=Billable
filter[isPrivate] Is private task filter[isPrivate]=false
filter[isHardDeadline] Has hard deadline filter[isHardDeadline]=true
filter[isCompleted] Is completed filter[isCompleted]=false
filter[search] Global search (title, description, website) filter[search]=urgent

Visibility & Permissions

  • Respects user permissions (CAN_SEE_ALL_TASKS, CAN_SEE_OWN_TASKS)
  • Filters private tasks (only visible to creator and main assignee)
  • Guest users see only tasks for their associated organizations
  • Users with CAN_SEE_OWN_TASKS see tasks where they are:
  • Main assignee
  • Creator
  • Additional assignee
  • Watcher

Example Requests

# Basic request
GET /api/aiconeo/tasks

# With includes and pagination
GET /api/aiconeo/tasks?include=mainAssignee,project,type,assignees&page[number]=1&page[size]=50

# Filter by status and project
GET /api/aiconeo/tasks?filter[statusId]=1,2,3&filter[projectId]=10&sort=-dueDate

# Filter by date range
GET /api/aiconeo/tasks?filter[dueDate][dateBetween]=2024-01-01,2024-12-31

# Active tasks only (not completed)
GET /api/aiconeo/tasks?filter[isCompleted]=false&filter[priority]=High,Critical

# My tasks
GET /api/aiconeo/tasks?filter[assigneeId]=5&filter[isCompleted]=false&sort=-priority

# Search tasks
GET /api/aiconeo/tasks?filter[search]=urgent&include=mainAssignee,project

Response Structure

{
  "data": [
    {
      "id": 123,
      "type": "tasks",
      "attributes": {
        "title": "Implement new feature",
        "description": "Add user authentication",
        "startDate": "2024-01-15",
        "dueDate": "2024-01-30",
        "deadline": null,
        "priority": "High",
        "estimation": 8,
        "url": null,
        "website": "https://example.com/task/123",
        "billingStatus": "Billable",
        "isPrivate": false,
        "isHardDeadline": false,
        "notes": null,
        "createdAt": "2024-01-10 09:00:00",
        "updatedAt": "2024-01-15 14:30:00",
        "status": {
          "id": 2,
          "name": "In Progress",
          "color": "#3B82F6",
          "webName": "In Progress"
        },
        "mainAssignee": {
          "id": 5,
          "firstName": "John",
          "lastName": "Doe",
          "email": "john@example.com",
          "profilePhotoPath": "/storage/photos/john.jpg"
        },
        "creator": {
          "id": 10,
          "firstName": "Jane",
          "lastName": "Smith",
          "email": "jane@example.com"
        },
        "project": {
          "id": 3,
          "name": "Website Redesign",
          "statusId": 1
        },
        "type": {
          "id": 1,
          "name": "Development",
          "color": "#10B981"
        },
        "assignees": [
          {
            "id": 5,
            "firstName": "John",
            "lastName": "Doe",
            "email": "john@example.com",
            "profilePhotoPath": "/storage/photos/john.jpg"
          }
        ],
        "subTasksCount": 3,
        "timer": {
          "id": 50,
          "totalTime": 14400,
          "isRunning": false
        }
      }
    }
  ],
  "meta": {
    "page": {
      "currentPage": 1,
      "from": 1,
      "lastPage": 5,
      "perPage": 25,
      "to": 25,
      "total": 125
    }
  },
  "links": {
    "first": "http://example.com/api/aiconeo/tasks?page[number]=1&page[size]=25",
    "last": "http://example.com/api/aiconeo/tasks?page[number]=5&page[size]=25",
    "prev": null,
    "next": "http://example.com/api/aiconeo/tasks?page[number]=2&page[size]=25"
  }
}

Task Detail

Endpoint: GET /api/aiconeo/tasks/{id}

Get comprehensive details for a single task with all related data.

Response Includes

Data Description
Basic fields Title, description, dates, priority, estimation, billing status, flags
Status Status with color and web name
Main assignee User with profile photo
Creator User who created the task
Project Project details with status
Task type Type with color
Customer B2B customer information
Timer Timer with all time logs
Assignees All assigned users
Watchers All watchers
Attachments Task attachments with file info
Sub-tasks All subtasks with their status

Example Request

GET /api/aiconeo/tasks/123
Accept-Language: de_CH

Example Response

{
  "data": {
    "id": 123,
    "type": "tasks",
    "attributes": {
      "title": "Implement new feature",
      "description": "Add user authentication with OAuth2",
      "startDate": "2024-01-15",
      "dueDate": "2024-01-30",
      "deadline": null,
      "startTime": "09:00:00",
      "dueTime": "17:00:00",
      "priority": "High",
      "estimation": 8,
      "url": "https://jira.example.com/PROJ-123",
      "website": "https://example.com/task/123",
      "billingStatus": "Billable",
      "isPrivate": false,
      "isHardDeadline": false,
      "notes": "Remember to update documentation",
      "createdAt": "2024-01-10 09:00:00",
      "updatedAt": "2024-01-15 14:30:00",
      "status": {
        "id": 2,
        "name": "In Progress",
        "color": "#3B82F6",
        "webName": "In Progress"
      },
      "mainAssignee": {
        "id": 5,
        "firstName": "John",
        "lastName": "Doe",
        "email": "john@example.com",
        "profilePhotoPath": "/storage/photos/john.jpg"
      },
      "creator": {
        "id": 10,
        "firstName": "Jane",
        "lastName": "Smith",
        "email": "jane@example.com"
      },
      "project": {
        "id": 3,
        "name": "Website Redesign",
        "statusId": 1
      },
      "type": {
        "id": 1,
        "name": "Development",
        "color": "#10B981"
      },
      "customer": {
        "id": 50,
        "company": "Acme Corp"
      },
      "timer": {
        "id": 50,
        "logs": [
          {
            "id": 100,
            "userId": 5,
            "startTime": "2024-01-15 09:00:00",
            "endTime": "2024-01-15 12:00:00",
            "trackedTime": 10800,
            "createdAt": "2024-01-15 12:00:00"
          }
        ]
      },
      "assignees": [
        {
          "id": 5,
          "firstName": "John",
          "lastName": "Doe",
          "email": "john@example.com",
          "profilePhotoPath": "/storage/photos/john.jpg"
        },
        {
          "id": 7,
          "firstName": "Bob",
          "lastName": "Wilson",
          "email": "bob@example.com",
          "profilePhotoPath": "/storage/photos/bob.jpg"
        }
      ],
      "watchers": [
        {
          "id": 10,
          "firstName": "Jane",
          "lastName": "Smith",
          "email": "jane@example.com"
        }
      ],
      "attachments": [
        {
          "id": 1,
          "fileName": "requirements.pdf",
          "filePath": "/storage/attachments/requirements.pdf",
          "fileSize": 1024000,
          "mimeType": "application/pdf",
          "createdAt": "2024-01-10 10:00:00"
        }
      ],
      "subTasks": [
        {
          "id": 124,
          "title": "Setup OAuth provider",
          "dueDate": "2024-01-20",
          "priority": "High",
          "status": {
            "id": 3,
            "name": "Complete",
            "color": "#10B981"
          }
        },
        {
          "id": 125,
          "title": "Implement login flow",
          "dueDate": "2024-01-25",
          "priority": "High",
          "status": {
            "id": 2,
            "name": "In Progress",
            "color": "#3B82F6"
          }
        }
      ]
    }
  }
}

Task Logs

Endpoint: GET /api/aiconeo/tasks/{id}/logs

Get the change history for a task, showing all modifications made to the task.

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 50 200 Items per page

Log Fields

Field Type Description
id int Log ID
changeType string Type of change (new, edit, delete, reminder, etc.)
modelId int Task ID
emailPreview string Email preview text (for reminders)
createdAt datetime When the change occurred
logHistory object History record with user information
logChanges array Detailed field changes

Change Types

Type Description
new Task created
edit Task edited
delete Task deleted
reminder Reminder sent
slack_reminder Slack reminder sent
email_reminder Email reminder sent
stop_timer Timer stopped
edit_timer Timer edited
delete_timer Timer deleted

Example Request

GET /api/aiconeo/tasks/123/logs?page[number]=1&page[size]=50

Example Response

{
  "data": [
    {
      "id": 500,
      "type": "logs",
      "attributes": {
        "changeType": "edit",
        "modelId": 123,
        "emailPreview": null,
        "createdAt": "2024-01-15 14:30:00",
        "updatedAt": "2024-01-15 14:30:00",
        "logHistory": {
          "id": 450,
          "userId": 5,
          "user": {
            "id": 5,
            "firstName": "John",
            "lastName": "Doe",
            "email": "john@example.com",
            "profilePhotoPath": "/storage/photos/john.jpg"
          }
        },
        "logChanges": [
          {
            "id": 1000,
            "fieldChanged": "status",
            "oldValue": "To Do",
            "newValue": "In Progress",
            "createdAt": "2024-01-15 14:30:00",
            "updatedAt": "2024-01-15 14:30:00"
          },
          {
            "id": 1001,
            "fieldChanged": "priority",
            "oldValue": "Medium",
            "newValue": "High",
            "createdAt": "2024-01-15 14:30:00",
            "updatedAt": "2024-01-15 14:30:00"
          }
        ]
      }
    }
  ],
  "meta": {
    "page": {
      "currentPage": 1,
      "from": 1,
      "lastPage": 3,
      "perPage": 50,
      "to": 50,
      "total": 150
    }
  },
  "links": {
    "first": "http://example.com/api/aiconeo/tasks/123/logs?page[number]=1&page[size]=50",
    "last": "http://example.com/api/aiconeo/tasks/123/logs?page[number]=3&page[size]=50",
    "prev": null,
    "next": "http://example.com/api/aiconeo/tasks/123/logs?page[number]=2&page[size]=50"
  }
}

Task Comments

Endpoint: GET /api/aiconeo/tasks/{id}/comments

Get all comments for a task with attachments and reactions.

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 50 200 Items per page

Comment Fields

Field Type Description
id int Comment ID
text string Comment text
author string Author name (for external comments)
chatBoxId int Chat box ID
createdAt datetime When comment was created
updatedAt datetime Last update time
user object User who created the comment
attachments array File attachments
reactions array Emoji reactions

Example Request

GET /api/aiconeo/tasks/123/comments?page[number]=1&page[size]=50

Example Response

{
  "data": [
    {
      "id": 1000,
      "type": "chatMessages",
      "attributes": {
        "text": "I've completed the OAuth setup. Ready for review.",
        "author": null,
        "chatBoxId": 50,
        "createdAt": "2024-01-20 15:30:00",
        "updatedAt": "2024-01-20 15:30:00",
        "user": {
          "id": 5,
          "firstName": "John",
          "lastName": "Doe",
          "email": "john@example.com",
          "profilePhotoPath": "/storage/photos/john.jpg"
        },
        "attachments": [
          {
            "id": 10,
            "fileName": "oauth-config.json",
            "filePath": "/storage/chat/oauth-config.json",
            "fileSize": 2048,
            "mimeType": "application/json",
            "createdAt": "2024-01-20 15:30:00"
          }
        ],
        "reactions": [
          {
            "id": 1,
            "reaction": "👍",
            "userId": 10,
            "userName": "Jane Smith"
          },
          {
            "id": 2,
            "reaction": "🎉",
            "userId": 7,
            "userName": "Bob Wilson"
          }
        ]
      }
    },
    {
      "id": 999,
      "type": "chatMessages",
      "attributes": {
        "text": "Great! I'll review it today.",
        "author": null,
        "chatBoxId": 50,
        "createdAt": "2024-01-20 16:00:00",
        "updatedAt": "2024-01-20 16:00:00",
        "user": {
          "id": 10,
          "firstName": "Jane",
          "lastName": "Smith",
          "email": "jane@example.com",
          "profilePhotoPath": "/storage/photos/jane.jpg"
        },
        "attachments": [],
        "reactions": []
      }
    }
  ],
  "meta": {
    "page": {
      "currentPage": 1,
      "from": 1,
      "lastPage": 2,
      "perPage": 50,
      "to": 50,
      "total": 75
    }
  },
  "links": {
    "first": "http://example.com/api/aiconeo/tasks/123/comments?page[number]=1&page[size]=50",
    "last": "http://example.com/api/aiconeo/tasks/123/comments?page[number]=2&page[size]=50",
    "prev": null,
    "next": "http://example.com/api/aiconeo/tasks/123/comments?page[number]=2&page[size]=50"
  }
}

Warehouses

Base Endpoint: GET /api/v1/warehouses

Standard JSON:API endpoint for warehouse management.

Endpoints

Method Endpoint Description
GET /api/v1/warehouses List all warehouses
GET /api/v1/warehouses/{id} Get single warehouse
GET /api/v1/warehouses/virtual-warehouse-availability Get virtual warehouse availability
GET /api/v1/warehouses/virtual-warehouse-availability-display Get availability display message
GET /api/v1/warehouses/virtual-warehouse-next-stock-increases Get next stock increases
GET /api/v1/warehouses/{id}/bin-locations Get bin locations by warehouse
GET /api/v1/warehouses/{id}/articles Get articles by warehouse
GET /api/v1/warehouses/articles/{articleId}/stocks Get warehouses with stocks by article
GET /api/v1/warehouses/articles/stocks Get warehouses with article stocks
GET /api/v1/warehouses/{id}/products/{productId}/stocks Get product stocks for warehouse
GET /api/v1/warehouses/products/{productId}/stocks Get product stocks for all warehouses
GET /api/v1/warehouses/{id}/inventories Get inventory for warehouse

Query Parameters

Parameter Type Default Description
page[number] int 1 Page number
page[size] int - Items per page

Filters

Filter Description Example
filter[id] Warehouse ID(s) filter[id]=1,2,3
filter[shopifyShop.id] Shopify shop ID filter[shopifyShop.id]=5
filter[wmsVisibility] WMS visibility filter[wmsVisibility]=true

Sortable Fields

Field Description
createdAt Creation date
updatedAt Last update date

Response Fields

Field Type Description
id integer Warehouse ID
name string Warehouse name
country string Country
address string Address
createdAt datetime Creation timestamp
updatedAt datetime Last update timestamp

Example Requests

# List all warehouses
GET /api/v1/warehouses

# Get single warehouse
GET /api/v1/warehouses/5

# With pagination
GET /api/v1/warehouses?page[number]=1&page[size]=25

# Get product stocks
GET /api/v1/warehouses/products/123/stocks

Time Tracking

Base URL: https://app.aico.swiss/{workspaceId}/api/v1

Time tracking endpoints for managing timer logs, weekly views, and team time sheets.

Timer Logs Endpoints

Method Endpoint Description
GET /timer-logs/results-for-date Get user timer results for a specific date
GET /timer-logs/results-for-interval Get user timer results for a date interval
GET /timer-logs/projects Get projects list for timer selection
GET /timer-logs/weekly Get weekly timer logs with daily aggregations
GET /timer-logs/team Get team timer logs (Admin only)
GET /timer-logs/staff-search Search staff members for dropdown (Admin only)
GET /timer-logs/reports Get time tracking reports (day/week/month/year)
POST /timer-logs/create Create a new timer log entry
POST /timer-logs/manual-entry Create a manual time entry
POST /timer-logs/{id}/start Start a timer for existing timer log
POST /timer-logs/{id}/restart Restart a stopped timer
POST /timer-logs/{id}/stop Stop an active timer
PUT /timer-logs/{id} Update a timer log entry
DELETE /timer-logs/{id} Delete a timer log entry

Staff Member Timer Endpoints

Method Endpoint Description
GET /staff-members/{id}/timer-results-for-interval Get staff member's timer results for interval
GET /staff-members/{id}/active-timer Get staff member's active timer
POST /staff-members/{id}/start Start timer for staff member
POST /staff-members/{id}/stop Stop timer for staff member

GET /timer-logs/results-for-date

Get user timer results for a specific date.

Request Parameters

Parameter Type Required Description
userId number No AICO user ID (defaults to current user)
date number Yes Unix timestamp for the date
page[number] number No Page number for pagination
page[size] number No Page size for pagination

Response

{
  "data": [
    {
      "id": 123,
      "timerId": 456,
      "userId": 789,
      "startTime": "2026-01-14T09:00:00+00:00",
      "endTime": "2026-01-14T11:30:00+00:00",
      "trackedTime": "02:30:00",
      "isActive": false,
      "notes": "Work description"
    }
  ],
  "paginator": { ... }
}

GET /timer-logs/results-for-interval

Get user timer results for a date interval.

Request Parameters

Parameter Type Required Description
userId number No AICO user ID (defaults to current user)
startDate number Yes Unix timestamp for start date
endDate number No Unix timestamp for end date (defaults to now)
page[number] number No Page number for pagination
page[size] number No Page size for pagination

Response

{
  "data": [
    {
      "id": 123,
      "timerId": 456,
      "userId": 789,
      "startTime": "2026-01-14T09:00:00+00:00",
      "endTime": "2026-01-14T11:30:00+00:00",
      "trackedTime": "02:30:00",
      "isActive": false,
      "notes": "Work description"
    }
  ],
  "paginator": { ... }
}

GET /timer-logs/weekly

Fetch timer logs for a specific week with daily aggregations.

Request Parameters

Parameter Type Required Description
userId number No AICO user ID (defaults to current user)
weekStart number Yes Unix timestamp (Monday 00:00:00)
weekEnd number Yes Unix timestamp (Sunday 23:59:59)

Response

{
  "data": {
    "userId": 123,
    "staffMemberId": 456,
    "dailyTotals": {
      "mon": 28800,
      "tue": 32400,
      "wed": 25200,
      "thu": 28800,
      "fri": 21600,
      "sat": 0,
      "sun": 0
    },
    "weekTotal": 136800,
    "trackedHours": 38.0,
    "isTracking": true,
    "activeEntry": {
      "id": 456,
      "taskId": 789,
      "taskName": "Task Title",
      "startTime": "2026-01-14T09:00:00+00:00"
    },
    "entries": [
      {
        "id": 123,
        "date": "2026-01-14",
        "startTime": "09:03",
        "endTime": "11:05",
        "durationSeconds": 7320,
        "isActive": false,
        "description": "Work notes",
        "task": {
          "id": 456,
          "name": "Task Title",
          "urlKey": "task-123",
          "url": "https://app.aico.swiss/tasks/task-123",
          "estimatedHours": "8h",
          "totalTrackedSeconds": 28800
        },
        "project": {
          "id": 789,
          "name": "Project Name",
          "projectType": "CLIENT",
          "clientName": "Customer Inc"
        }
      }
    ],
    "report": {
      "internalCost": 2850.00,
      "billableCost": 3800.00,
      "billableTotal": 3800.00,
      "margin": 25.0,
      "marginAmount": 950.00
    }
  }
}

Notes: - dailyTotals values are in seconds (calculated from timer_logs) - weekTotal is the total tracked seconds (from cached staff_member_reports if available) - trackedHours is the tracked hours as decimal (from cached report) - activeEntry is null if no timer is currently running - Active entries have endTime: null and isActive: true - report contains cached financial data from staff_member_reports table (null if no cached report exists)


GET /timer-logs/team (Admin Only)

Fetch timer logs for all team members.

Request Parameters

Parameter Type Required Description
weekStart number Yes Unix timestamp
weekEnd number Yes Unix timestamp
page number No Page number (default: 1)
pageSize number No Items per page (default: 50)
searchTerm string No Filter by staff name

Response

{
  "data": [
    {
      "userId": 123,
      "staffMemberId": 456,
      "userName": "John Doe",
      "userAvatar": "https://ui-avatars.com/api/?name=John+Doe",
      "dailyTotals": {
        "mon": 28800,
        "tue": 32400,
        "wed": 25200,
        "thu": 28800,
        "fri": 21600,
        "sat": 0,
        "sun": 0
      },
      "weekTotal": 136800,
      "trackedHours": 38.0,
      "isTracking": true,
      "currentTaskName": "Current Task Title",
      "entries": [],
      "report": {
        "internalCost": 2850.00,
        "billableCost": 3800.00,
        "billableTotal": 3800.00,
        "margin": 25.0,
        "marginAmount": 950.00
      }
    }
  ],
  "meta": {
    "total": 25,
    "page": 1,
    "pageSize": 50
  },
  "permissions": {
    "canViewTeam": true,
    "canEditTeam": true
  }
}

Permission Check: - Returns empty data with permissions.canViewTeam: false if user lacks admin permissions - Requires can_view_team_time_tracking or can_read_staff_members_tracker_results permission - report contains cached financial data from staff_member_reports table (null if no cached report exists)


GET /timer-logs/staff-search (Admin Only)

Search staff members for the teammate selector dropdown.

Request Parameters

Parameter Type Required Description
searchTerm string No Search by name or email
weekStart number No Include week stats if provided
weekEnd number No Include week stats if provided

Response

{
  "data": [
    {
      "staffMemberId": "550e8400-e29b-41d4-a716-446655440000",
      "externalId": 123,
      "name": "John Doe",
      "avatar": "https://ui-avatars.com/api/?name=John+Doe",
      "email": "john.doe@example.com",
      "weekTotal": 136800,
      "isTracking": true
    }
  ]
}

Notes: - weekTotal and isTracking are only included if weekStart and weekEnd are provided - Returns 403 Forbidden if user lacks admin permissions - Limited to 50 results


GET /timer-logs/reports

Get time tracking reports for day, week, month, or year periods. Uses cached data from staff_member_reports table when available.

Request Parameters

Parameter Type Required Description
userId number No AICO user ID (defaults to current user)
period string Yes Report period: day, week, month, or year
date number Yes Unix timestamp for the reference date

Response

{
  "data": {
    "userId": 123,
    "staffMemberId": 456,
    "userName": "John Doe",
    "period": "week",
    "requestedDate": "2026-01-14",
    "totalTrackedHours": 152.5,
    "totalTrackedSeconds": 549000,
    "reports": [
      {
        "startDate": "2026-01-06",
        "endDate": "2026-01-12",
        "label": "W2",
        "trackedHours": 38.0,
        "trackedSeconds": 136800,
        "internalCost": 2850.00,
        "billableCost": 3800.00,
        "billableTotal": 3800.00,
        "margin": 25.0,
        "marginAmount": 950.00,
        "isCached": true
      },
      {
        "startDate": "2026-01-13",
        "endDate": "2026-01-19",
        "label": "W3",
        "trackedHours": 40.0,
        "trackedSeconds": 144000,
        "isCached": false
      }
    ]
  }
}

Period Behaviors:

Period Returns Label Format
day Each day of the week containing the date Mon, Tue, etc.
week Each week of the month containing the date W1, W2, etc.
month Each month of the year containing the date Jan, Feb, etc.
year Last 5 years including the current year 2022, 2023, etc.

Notes: - isCached: true indicates data is from staff_member_reports table (includes financial data) - isCached: false indicates data is calculated on-the-fly from timer_logs (no financial data) - Financial fields (internalCost, billableCost, etc.) are only included when isCached: true - Returns 404 if staff member not found for user


POST /timer-logs/create

Create a new timer log entry and start tracking.

Request Body

Parameter Type Required Description
taskId number Conditional Required if no projectId
projectId number Conditional Required if no taskId
userId number No AICO user ID (defaults to current user)
notes string No Description/notes
trackedTime string No Pre-tracked time (format: HH:mm:ss)
createdAt number No Unix timestamp for entry date

Response

{
  "data": {
    "id": 123,
    "timerId": 456,
    "userId": 789,
    "startTime": "2026-01-14T09:00:00+00:00",
    "endTime": null,
    "trackedTime": "00:00:00",
    "isActive": true,
    "notes": null
  }
}

POST /timer-logs/manual-entry

Create a manual time entry with specific start and end times.

Request Body

Parameter Type Required Description
userId number No AICO user ID (defaults to current user)
taskId number Yes AICO task ID
date string Yes Date in YYYY-MM-DD format
startTime string Yes Start time in HH:mm format
endTime string Yes End time in HH:mm format
description string No Work description

Response

{
  "data": {
    "id": 123,
    "date": "2026-01-14",
    "startTime": "09:00",
    "endTime": "11:30",
    "durationSeconds": 9000,
    "isActive": false,
    "description": "Work description",
    "task": {
      "id": 456,
      "name": "Task Title",
      "urlKey": "task-123",
      "url": "https://app.aico.swiss/tasks/task-123",
      "estimatedHours": "8h",
      "totalTrackedSeconds": 28800
    },
    "project": {
      "id": 789,
      "name": "Project Name",
      "projectType": "CLIENT",
      "clientName": "Customer Inc"
    }
  }
}

Validation: - endTime must be after startTime - Task must exist and be accessible - Returns 409 Conflict if entry overlaps with existing entries on the same day


POST /timer-logs/{id}/start

Start a new timer based on an existing timer log entry.

URL Parameters

Parameter Type Required Description
id number Yes Timer log ID

Request Body

Parameter Type Required Description
userId number No AICO user ID (defaults to current user)

Response

{
  "data": {
    "id": 124,
    "timerId": 456,
    "userId": 789,
    "startTime": "2026-01-14T14:00:00+00:00",
    "endTime": null,
    "trackedTime": "00:00:00",
    "isActive": true
  }
}

Notes: - Creates a new timer log entry with the same timer_id - Automatically stops any other active timer for the user


POST /timer-logs/{id}/restart

Restart a stopped timer log entry.

URL Parameters

Parameter Type Required Description
id number Yes Timer log ID

Request Body

Parameter Type Required Description
userId number No AICO user ID (defaults to current user)

Response

{
  "data": {
    "id": 123,
    "timerId": 456,
    "userId": 789,
    "startTime": "2026-01-14T14:00:00+00:00",
    "endTime": null,
    "trackedTime": "02:30:00",
    "isActive": true
  }
}

Notes: - Resumes tracking on the same timer log entry - Preserves previously tracked time - Automatically stops any other active timer for the user


POST /timer-logs/{id}/stop

Stop an active timer.

URL Parameters

Parameter Type Required Description
id number Yes Timer log ID

Response

{
  "data": {
    "id": 123,
    "timerId": 456,
    "userId": 789,
    "startTime": "2026-01-14T09:00:00+00:00",
    "endTime": "2026-01-14T11:30:00+00:00",
    "trackedTime": "02:30:00",
    "isActive": false
  }
}

PUT /timer-logs/{id}

Update a timer log entry.

URL Parameters

Parameter Type Required Description
id number Yes Timer log ID

Request Body

Parameter Type Required Description
notes string No Description/notes
trackedTime string No Tracked time (format: HH:mm:ss)
createdAt number No Unix timestamp for entry date

Response

{
  "data": {
    "id": 123,
    "timerId": 456,
    "userId": 789,
    "startTime": "2026-01-14T09:00:00+00:00",
    "endTime": "2026-01-14T11:30:00+00:00",
    "trackedTime": "02:30:00",
    "isActive": false,
    "notes": "Updated notes"
  }
}

Authorization: - User can edit their own entries - Admins with can_edit_team_time_tracking permission can edit any entry


DELETE /timer-logs/{id}

Delete a timer log entry.

URL Parameters

Parameter Type Required Description
id number Yes Timer log ID

Response

{
  "data": {
    "success": true
  }
}

Authorization: - User can delete their own entries - Admins with can_edit_team_time_tracking permission can delete any entry


Time Tracking Data Types

TimerLogEntry

interface TimerLogEntry {
  id: number;
  date: string;                    // "2026-01-14"
  startTime: string;               // "09:03"
  endTime: string | null;          // "11:05" or null if currently tracking
  durationSeconds: number;
  isActive: boolean;
  description: string | null;

  task: {
    id: number;
    name: string;
    urlKey: string;
    url: string;                   // Full URL to task
    estimatedHours: string | null;
    totalTrackedSeconds: number;   // Total tracked on this task (all users)
  } | null;

  project: {
    id: number;
    name: string;
    projectType: string;           // "INTERNAL", "CLIENT", etc.
    clientName: string | null;
  } | null;
}

DailyTotals

interface DailyTotals {
  mon: number;  // seconds
  tue: number;
  wed: number;
  thu: number;
  fri: number;
  sat: number;
  sun: number;
}

TeamMemberTimeLogs

interface TeamMemberTimeLogs {
  userId: number;
  staffMemberId: string;           // UUID from staff_members table
  userName: string;
  userAvatar: string | null;
  dailyTotals: DailyTotals;
  weekTotal: number;               // seconds
  isTracking: boolean;
  currentTaskName: string | null;
  entries: TimerLogEntry[];
}

Time Tracking Permissions

Required Permissions for Admin Endpoints

Permission Description
can_view_team_time_tracking View team time tracking data
can_edit_team_time_tracking Edit team time tracking entries
can_read_staff_members_tracker_results Alternative permission for viewing team data

Authorization Rules

  1. Own Entries: Users can always view, create, edit, and delete their own timer entries
  2. Team View: Requires can_view_team_time_tracking or can_read_staff_members_tracker_results
  3. Team Edit: Requires can_edit_team_time_tracking
  4. Staff Search: Same permissions as Team View

Time Tracking Error Responses

Status Code Description
400 Bad Request - Invalid parameters
401 Unauthorized - Authentication required
403 Forbidden - Insufficient permissions
404 Not Found - Resource not found
409 Conflict - Overlapping time entries
422 Unprocessable Entity - Validation error

Shopify Shops

List Shopify Shops

Endpoint: GET /api/aiconeo/shopify-shops

Returns a paginated list of all Shopify shops.

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 25 100 Items per page
sort string name - Sort field

Filters

Filter Description Example
filter[id] Shop ID(s), comma-separated filter[id]=1,2
filter[isActive] Filter by active status filter[isActive]=true
filter[isPosShop] Filter by POS shop status filter[isPosShop]=false
filter[currencyId] Filter by currency ID(s) filter[currencyId]=1
filter[countryId] Filter by country ID(s) filter[countryId]=216
filter[search] Search by name, short name, or domain filter[search]=fanshop

Response Structure

{
  "data": [
    {
      "id": 4,
      "type": "shopify-shops",
      "attributes": {
        "name": "FCB Fanshop",
        "shortName": null,
        "isActive": true,
        "locale": "de_CH",
        "apiDomain": "fcb-fanshop.myshopify.com",
        "isTaxable": true,
        "isPosShop": false,
        "lastSyncStatus": "SYNCED",
        "lastSyncStartedAt": "2026-01-06 23:30:18",
        "lastSyncCompletedAt": "2026-01-07 00:01:40",
        "createdAt": "2022-08-18 08:43:23",
        "updatedAt": "2026-01-07 00:01:40",
        "currency": {
          "id": 1,
          "name": "CHF"
        },
        "country": {
          "id": 216,
          "name": "Switzerland"
        }
      }
    }
  ],
  "meta": {
    "page": {
      "currentPage": 1,
      "from": 1,
      "lastPage": 1,
      "perPage": 25,
      "to": 3,
      "total": 3
    }
  }
}

Trigger Bulk Shopify Sync

Endpoint: POST /api/aiconeo/products/bulk-shopify-sync

Triggers a bulk Shopify sync for all products. By default, syncs all shops. Optionally sync only a specific shop by providing shopifyShopId.

Request Body (Optional)

{
  "data": {
    "shopifyShopId": 4
  }
}
Field Type Required Description
data.shopifyShopId int No ID of a specific Shopify shop to sync. If omitted, syncs all shops.

Response (All Shops)

{
  "data": {
    "type": "bulk-shopify-sync",
    "attributes": {
      "message": "Bulk Shopify sync has been queued for all shops",
      "shopCount": 3
    }
  }
}

Response (Single Shop)

{
  "data": {
    "type": "bulk-shopify-sync",
    "attributes": {
      "message": "Bulk Shopify sync has been queued for shop 'FCB Fanshop'",
      "shopCount": 1,
      "shopifyShopId": 4,
      "shopifyShopName": "FCB Fanshop"
    }
  }
}

Error Response (Shop Not Found)

{
  "errors": [
    {
      "status": "404",
      "title": "Not Found",
      "detail": "Shopify shop with ID 99999 not found."
    }
  ]
}

Languages

High-performance endpoints for managing languages.

Languages List

Endpoint: GET /api/aiconeo/languages

Returns a paginated list of languages.

Query Parameters

Parameter Type Default Max Description
page[number] int 1 - Page number
page[size] int 25 100 Items per page
sort string id - Sort field(s), prefix with - for descending
filter[field] mixed - - Filter by field
include string - - Comma-separated list of includes

Sortable Fields

Field Description
id Language ID
name Language name
locale Locale code
fallbackLanguageId Fallback language ID
createdAt Creation timestamp
updatedAt Last update timestamp

Filter Parameters

Parameter Type Description Operators
filter[id] string Filter by ID(s), comma-separated -
filter[name] string Filter by language name Text operators
filter[locale] string Filter by locale code Text operators
filter[fallbackLanguageId] string Filter by fallback language ID (use 'null' for no fallback) -
filter[search] string Global search in name and locale -
filter[createdAt] mixed Filter by creation date DateTime operators
filter[updatedAt] mixed Filter by update date DateTime operators

Includes

Include Description
fallbackLanguage Fallback language details (id, name, locale)
aicoShopsCount Count of associated aico shops
warehousesCount Count of associated warehouses

Example Request

curl -X GET "http://127.0.0.1/api/aiconeo/languages?page[size]=10&include=fallbackLanguage,aicoShopsCount" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/vnd.api+json"

Example Response

{
  "data": [
    {
      "id": 1,
      "type": "languages",
      "attributes": {
        "name": "German (Switzerland)",
        "locale": "de_CH",
        "fallbackLanguageId": null,
        "createdAt": "2024-01-15T10:30:00.000000Z",
        "updatedAt": "2024-01-15T10:30:00.000000Z",
        "aicoShopsCount": 3
      }
    },
    {
      "id": 2,
      "type": "languages",
      "attributes": {
        "name": "French (Switzerland)",
        "locale": "fr_CH",
        "fallbackLanguageId": 1,
        "createdAt": "2024-01-15T10:30:00.000000Z",
        "updatedAt": "2024-01-15T10:30:00.000000Z",
        "fallbackLanguage": {
          "id": 1,
          "name": "German (Switzerland)",
          "locale": "de_CH"
        },
        "aicoShopsCount": 2
      }
    }
  ],
  "meta": {
    "page": {
      "currentPage": 1,
      "from": 1,
      "lastPage": 1,
      "perPage": 25,
      "to": 2,
      "total": 2
    }
  },
  "links": {
    "first": "http://example.com/api/aiconeo/languages?page[number]=1&page[size]=25",
    "last": "http://example.com/api/aiconeo/languages?page[number]=1&page[size]=25",
    "prev": null,
    "next": null
  }
}

Language Detail

Endpoint: GET /api/aiconeo/languages/{id}

Returns a single language by ID.

Path Parameters

Parameter Type Description
id integer Language ID

Query Parameters

Parameter Type Description
include string Comma-separated list of includes

Includes

Include Description
fallbackLanguage Fallback language details (id, name, locale)
aicoShopsCount Count of associated aico shops
warehousesCount Count of associated warehouses

Example Request

curl -X GET "http://127.0.0.1/api/aiconeo/languages/1?include=fallbackLanguage,aicoShopsCount,warehousesCount" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/vnd.api+json"

Example Response

{
  "data": {
    "id": 1,
    "type": "languages",
    "attributes": {
      "name": "German (Switzerland)",
      "locale": "de_CH",
      "fallbackLanguageId": null,
      "createdAt": "2024-01-15T10:30:00.000000Z",
      "updatedAt": "2024-01-15T10:30:00.000000Z",
      "aicoShopsCount": 3,
      "warehousesCount": 5
    }
  }
}

Error Response (404)

{
  "error": "not_found",
  "message": "Language not found."
}

Filter Operators Reference

Text Filter Operators

Available for all text fields in AicoNeo endpoints.

Operator Description Example
(simple) Contains (default) filter[name]=shirt
contains Contains value filter[name][contains]=shirt
notContains Does not contain filter[name][notContains]=test
equals / eq Exact match filter[sku][equals]=ABC-123
notEquals / neq Not equal to filter[sku][notEquals]=OLD
startsWith Starts with filter[sku][startsWith]=PRD-
endsWith Ends with filter[sku][endsWith]=-2024
blank / empty Is empty or null filter[description][blank]=1
notBlank / notEmpty Is not empty filter[description][notBlank]=1

DateTime Filter Operators

Available for all datetime fields in AicoNeo endpoints.

Full DateTime Operators

Operator Description Example
(simple) Date equals filter[createdAt]=2024-06-15
equals / eq Exact datetime match filter[createdAt][eq]=2024-06-15 10:30:00
before / lt Before datetime filter[createdAt][before]=2024-06-15
after / gt After datetime filter[createdAt][after]=2024-01-01
between DateTime range filter[createdAt][between]=2024-01-01,2024-12-31
blank / empty Is null filter[updatedAt][blank]=1
notBlank / notEmpty Is not null filter[updatedAt][notBlank]=1

Date-Only Operators

Operator Description Example
dateEquals Date matches (ignores time) filter[createdAt][dateEquals]=2024-06-15
dateBefore Before date filter[createdAt][dateBefore]=2024-06-15
dateAfter After date filter[createdAt][dateAfter]=2024-01-01
dateBetween Date range filter[createdAt][dateBetween]=2024-01-01,2024-12-31

Response Format

All AicoNeo endpoints return JSON:API style responses with pagination metadata:

{
  "data": [
    {
      "id": 123,
      "type": "orders",
      "attributes": {
        "orderNumber": "ORD-2024-001",
        "buyerName": "Customer Name",
        ...
      }
    }
  ],
  "meta": {
    "page": {
      "currentPage": 1,
      "from": 1,
      "lastPage": 10,
      "perPage": 25,
      "to": 25,
      "total": 250
    }
  },
  "links": {
    "first": "http://example.com/api/aiconeo/orders?page[number]=1&page[size]=25",
    "last": "http://example.com/api/aiconeo/orders?page[number]=10&page[size]=25",
    "prev": null,
    "next": "http://example.com/api/aiconeo/orders?page[number]=2&page[size]=25"
  }
}

Notes

  • Authentication: All endpoints require auth:sanctum authentication
  • Access Control: Endpoints respect user permissions and role-based access
  • Soft Deletes: Only non-deleted records are returned
  • Performance: AicoNeo endpoints use optimized raw SQL queries
  • Translations: Set Accept-Language header for localized content
  • File/Image URLs: All file and image URLs are returned as full URLs generated via Laravel Storage (e.g., http://example.com/storage/path/to/image.jpg), not relative paths

File URL Fields by Endpoint

Endpoint Field Description
/api/aiconeo/products translations[].image Product translation images
/api/aiconeo/products/{id} translations[].image Product translation images
/api/aiconeo/products/{id} variants[].image Variant images
/api/aiconeo/products/{id} variationOptions[].values[].image Variation option value images
/api/aiconeo/products/{id} categories.*.image Category images
/api/aiconeo/products/{id} fileGroups.*.files[].translations[].fileUrl File URLs
/api/aiconeo/products/{id} fileGroups.*.files[].translations[].optimizedFileUrl Optimized file URLs
/api/aiconeo/articles image Article image (variant or product)
/api/aiconeo/articles productImage Product image
/api/aiconeo/articles variantImage Variant image
/api/aiconeo/categories image Category main image
/api/aiconeo/categories video Category video file
/api/aiconeo/categories itemImage Category item image
/api/aiconeo/collections image Collection main image
/api/aiconeo/b2b image B2B customer/company image
/api/aiconeo/b2c image B2C customer image
/api/aiconeo/order-documents documentUrl Document PDF URL