Products

Called with brand account keys. A key only sees its own account's data. Every example uses the same sample brand, order and item, so you can follow one item from call to call.

Base URL http://62.169.23.239:8001/api/v1Auth Bearer token7 endpoints

List categories#

GET/brands/categories

Requires scope products:read

Return the full category → product_types taxonomy.

Returns

HTTP 200

A application/json file.

GET /brands/categories
curl
curl http://62.169.23.239:8001/api/v1/brands/categories \
  -H "Authorization: Bearer $SKANNIT_TOKEN"
200Response
json
// 200 — body is application/json

Create a product#

POST/brands/create-product

Requires scope products:write

Request body

multipart/form-data
  • productstringRequired

    JSON BrandProductCreatePayload

  • image_frontstring | null
  • image_backstring | null
  • image_sidestring | null
  • image_detailstring | null
  • image_lifestylestring | null
  • tech_packstring | null
  • spec_sheetstring | null
  • certificatesarray of strings

Returns

HTTP 201
  • product_idstringRequired

    Skannit's id for the product.

  • brand_idstringRequired

    The brand that owns the product.

  • product_namestringRequired
  • statusstringRequired

    Current state of the object. See the allowed values.

  • base_skustringRequired

    SKU of the product. Variant SKUs are built from it.

  • media_urlsobjectRequired
  • spec_sheet_urlstring | nullRequired
  • certificate_urlsarray of stringsRequired
  • variants_countintegerRequired
  • created_atstringRequired

    When the object was created (ISO 8601, UTC).

  • updated_atstring | null

    When the object last changed (ISO 8601, UTC).

POST /brands/create-product
curl
curl -X POST http://62.169.23.239:8001/api/v1/brands/create-product \
  -H "Authorization: Bearer $SKANNIT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F 'product={"product_name": "Trail Tee", "status": "active", "base_sku": "TT-100", "category": "Tops", "price": 19.0, "sizes": ["S", "M"], "colors": ["White"], "variants": [{"color": "White", "size": "S", "volume_l": 0.5}, {"color": "White", "size": "M", "volume_l": 0.5}]}' \
  -F image_front=@front.jpg
201Response
json
{
  "product_id": "f6abe715-90aa-5908-a0a6-12951a3a21cc",
  "brand_id": "8ea3462c-426c-5cc1-84d7-db99868bca1c",
  "product_name": "Trail Tee",
  "status": "active",
  "base_sku": "TT-100",
  "media_urls": {},
  "spec_sheet_url": "https://files.skannit.example/spec_sheet.png",
  "certificate_urls": [
    "https://files.skannit.example/certificate.png"
  ],
  "variants_count": 24,
  "created_at": "2026-10-09T09:30:00Z",
  "updated_at": "2026-10-09T09:30:00Z"
}

Retrieve a product#

GET/brands/products/{product_id}

Requires scope products:read

Full product detail — same shape as PUT response (create-product + catalogue fields).

Parameters

  • product_idstringpathRequired

    Skannit's id for the product.

Returns

HTTP 200
  • product_namestringRequired
  • statusstring

    Current state of the object. See the allowed values.

    One ofdraftactive

    Defaults to draft

  • base_skustring | null

    SKU of the product. Variant SKUs are built from it.

  • categorystring | null
  • genderstring | null
  • pricestring | null
  • msrpstring | null
  • descriptionstring | null
  • seasonstring | null
  • gtin_statusstring | null
    One ofhas_ownreseller_partnerskannit_native
  • gtin_or_uuidstring | null
  • organicboolean

    Defaults to false

  • recycled_contentboolean

    Defaults to false

  • fabric_weight_gsminteger | null
  • texture_finishstring | null
  • sizesarray of strings
  • colorsarray of strings
  • variantsarray of objects
    +Show 6 child attributes
    • colorstringRequired

      Colour name, exactly as on the product's variants.

    • sizestringRequired

      Size name, exactly as on the product's variants.

    • variant_skustring | null

      SKU of one colour/size variant. Generated as <base SKU>-<colour>-<size> when you don't send one.

    • upc_eanstring | null
    • volume_lstring | null

      Packaged volume per unit, in liters

    • stockinteger

      Defaults to 0

  • id_assignmentstring

    Defaults to unique

  • authentication_hardwareobject
    +Show 2 child attributes
    • nfc_chipboolean

      Defaults to false

    • anti_counterfeit_threadboolean

      Defaults to false

  • internal_tagsarray of strings
  • approval_statusstring

    Defaults to pending_design_lead

  • private_notesstring | null
  • media_urlsobject | null
  • spec_sheet_urlstring | null
  • certificate_urlsarray of strings
  • product_idstringRequired

    Skannit's id for the product.

  • brand_idstringRequired

    The brand that owns the product.

  • updated_atstringRequired

    When the object last changed (ISO 8601, UTC).

  • created_atstringRequired

    When the object was created (ISO 8601, UTC).

  • carbon_co2string | null
  • water_used_lstring | null
  • circular_scoreinteger | null
  • end_of_life_instructionsstring | null
  • repair_difficultystring | null
  • spare_parts_includedarray of strings
  • repair_instructions_urlstring | null
  • country_of_originstring | null
  • main_supplierstring | null
  • compliance_standardsobject
    +Show 3 child attributes
    • eu_dpp_readyboolean

      Defaults to false

    • esg_certifiedboolean

      Defaults to false

    • reach_compliantboolean

      Defaults to false

  • stock_statusstring
    One ofIN STOCKLOW STOCKOUT OF STOCK

    Defaults to OUT OF STOCK

  • category_pathstring

    Defaults to

  • specificationsobject
    +Show 5 child attributes
    • primary_materialstring | null
    • membrane_typestring | null
    • weightstring | null
    • sustainabilitystring | null
    • warrantystring | null
  • imagesarray of strings
  • stock_distributionarray of objects
    +Show 2 child attributes
    • location_namestringRequired
    • unitsintegerRequired
  • material_specifications_compliancestring | null
  • active_catalogboolean

    Defaults to true

  • lifecycle_stagestring | null
  • inventory_on_handinteger

    Defaults to 0

  • inventory_reservedinteger

    Defaults to 0

  • low_stock_thresholdinteger

    Defaults to 0

  • last_modified_atstring | null
  • last_modified_bystring | null
  • tech_pack_urlstring | null
  • protection_tierstring

    Anti-counterfeit scanToken rotation tier (see ProductProtectionPayload). Read-only here — set via PUT /brands/products/{id}/protection, not via the product update payload, so a full product save can never silently reset a brand's protection level.

    One ofstandardpremiumelite

    Defaults to standard

  • geo_lock_rulesobject | null

    Elite-tier geographic restrictions, checked at scan time (Layer 2). Both forms are optional and independent — a brand can allow-list countries, fence a radius around a point, or do both.

    +Show 4 child attributes
    • allowed_countriesarray of strings | null

      ISO country codes this item may legitimately be scanned in

    • radius_kmnumber | null

      Allowed distance from the anchor point

    • anchor_latnumber | null
    • anchor_lonnumber | null
GET /brands/products/{product_id}
curl
curl http://62.169.23.239:8001/api/v1/brands/products/f6abe715-90aa-5908-a0a6-12951a3a21cc \
  -H "Authorization: Bearer $SKANNIT_TOKEN"
200Response
json
{
  "product_name": "Trail Tee",
  "status": "draft",
  "base_sku": "TT-100",
  "category": "Tops",
  "gender": "unisex",
  "price": 19,
  "msrp": 29,
  "description": "Organic cotton tee",
  "season": "AW26",
  "gtin_status": "has_own",
  "gtin_or_uuid": "08712345678906",
  "organic": false,
  "recycled_content": false,
  "fabric_weight_gsm": 1,
  "texture_finish": "brushed",
  "sizes": [
    "S",
    "M",
    "L"
  ],
  "colors": [
    "White",
    "Black"
  ],
  "variants": [
    "White / M"
  ],
  "id_assignment": "unique",
  "authentication_hardware": {
    "nfc_chip": false,
    "anti_counterfeit_thread": false
  },
  "internal_tags": [
    "internal tag"
  ],
  "approval_status": "pending_design_lead",
  "private_notes": null,
  "media_urls": {},
  "spec_sheet_url": "https://files.skannit.example/spec_sheet.png",
  "certificate_urls": [
    "https://files.skannit.example/certificate.png"
  ],
  "product_id": "f6abe715-90aa-5908-a0a6-12951a3a21cc",
  "brand_id": "8ea3462c-426c-5cc1-84d7-db99868bca1c",
  "updated_at": "2026-10-09T09:30:00Z",
  "created_at": "2026-10-09T09:30:00Z",
  "carbon_co2": 3.2,
  "water_used_l": 2700,
  "circular_score": 72,
  "end_of_life_instructions": "Return to any Northwind store for recycling",
  "repair_difficulty": "easy",
  "spare_parts_included": [
    "buttons"
  ],
  "repair_instructions_url": "https://files.skannit.example/repair_instructions.png",
  "country_of_origin": "PT",
  "main_supplier": "Tecidos do Norte",
  "compliance_standards": {
    "eu_dpp_ready": true,
    "esg_certified": true,
    "reach_compliant": true
  },
  "stock_status": "IN STOCK",
  "category_path": "Apparel / Tops",
  "specifications": {
    "primary_material": "Organic cotton",
    "membrane_type": "none",
    "weight": "180 g",
    "sustainability": "GOTS certified",
    "warranty": "2 years"
  },
  "images": [
    "image"
  ],
  "stock_distribution": [
    {
      "location_name": "Rotterdam DC",
      "units": 24
    }
  ],
  "material_specifications_compliance": "REACH compliant",
  "active_catalog": true,
  "lifecycle_stage": "in_production",
  "inventory_on_hand": 1,
  "inventory_reserved": 1,
  "low_stock_threshold": 1,
  "last_modified_at": null,
  "last_modified_by": "ops@northwind.example",
  "tech_pack_url": "https://files.skannit.example/tech_pack.png",
  "protection_tier": "standard",
  "geo_lock_rules": {
    "allowed_countries": [
      "allowed country"
    ],
    "radius_km": 1,
    "anchor_lat": 51.9244,
    "anchor_lon": 4.4777
  }
}

Update a product#

PUT/brands/products/{product_id}

Requires scope products:write

Parameters

  • product_idstringpathRequired

    Skannit's id for the product.

Request body

multipart/form-data
  • productstringRequired

    JSON BrandProductUpdatePayload

  • image_frontstring | null
  • image_backstring | null
  • image_sidestring | null
  • image_detailstring | null
  • image_lifestylestring | null
  • tech_packstring | null
  • spec_sheetstring | null
  • certificatesarray of strings

Returns

HTTP 200
  • product_namestringRequired
  • statusstring

    Current state of the object. See the allowed values.

    One ofdraftactive

    Defaults to draft

  • base_skustring | null

    SKU of the product. Variant SKUs are built from it.

  • categorystring | null
  • genderstring | null
  • pricestring | null
  • msrpstring | null
  • descriptionstring | null
  • seasonstring | null
  • gtin_statusstring | null
    One ofhas_ownreseller_partnerskannit_native
  • gtin_or_uuidstring | null
  • organicboolean

    Defaults to false

  • recycled_contentboolean

    Defaults to false

  • fabric_weight_gsminteger | null
  • texture_finishstring | null
  • sizesarray of strings
  • colorsarray of strings
  • variantsarray of objects
    +Show 6 child attributes
    • colorstringRequired

      Colour name, exactly as on the product's variants.

    • sizestringRequired

      Size name, exactly as on the product's variants.

    • variant_skustring | null

      SKU of one colour/size variant. Generated as <base SKU>-<colour>-<size> when you don't send one.

    • upc_eanstring | null
    • volume_lstring | null

      Packaged volume per unit, in liters

    • stockinteger

      Defaults to 0

  • id_assignmentstring

    Defaults to unique

  • authentication_hardwareobject
    +Show 2 child attributes
    • nfc_chipboolean

      Defaults to false

    • anti_counterfeit_threadboolean

      Defaults to false

  • internal_tagsarray of strings
  • approval_statusstring

    Defaults to pending_design_lead

  • private_notesstring | null
  • media_urlsobject | null
  • spec_sheet_urlstring | null
  • certificate_urlsarray of strings
  • product_idstringRequired

    Skannit's id for the product.

  • brand_idstringRequired

    The brand that owns the product.

  • updated_atstringRequired

    When the object last changed (ISO 8601, UTC).

  • created_atstringRequired

    When the object was created (ISO 8601, UTC).

  • carbon_co2string | null
  • water_used_lstring | null
  • circular_scoreinteger | null
  • end_of_life_instructionsstring | null
  • repair_difficultystring | null
  • spare_parts_includedarray of strings
  • repair_instructions_urlstring | null
  • country_of_originstring | null
  • main_supplierstring | null
  • compliance_standardsobject
    +Show 3 child attributes
    • eu_dpp_readyboolean

      Defaults to false

    • esg_certifiedboolean

      Defaults to false

    • reach_compliantboolean

      Defaults to false

  • stock_statusstring
    One ofIN STOCKLOW STOCKOUT OF STOCK

    Defaults to OUT OF STOCK

  • category_pathstring

    Defaults to

  • specificationsobject
    +Show 5 child attributes
    • primary_materialstring | null
    • membrane_typestring | null
    • weightstring | null
    • sustainabilitystring | null
    • warrantystring | null
  • imagesarray of strings
  • stock_distributionarray of objects
    +Show 2 child attributes
    • location_namestringRequired
    • unitsintegerRequired
  • material_specifications_compliancestring | null
  • active_catalogboolean

    Defaults to true

  • lifecycle_stagestring | null
  • inventory_on_handinteger

    Defaults to 0

  • inventory_reservedinteger

    Defaults to 0

  • low_stock_thresholdinteger

    Defaults to 0

  • last_modified_atstring | null
  • last_modified_bystring | null
  • tech_pack_urlstring | null
  • protection_tierstring

    Anti-counterfeit scanToken rotation tier (see ProductProtectionPayload). Read-only here — set via PUT /brands/products/{id}/protection, not via the product update payload, so a full product save can never silently reset a brand's protection level.

    One ofstandardpremiumelite

    Defaults to standard

  • geo_lock_rulesobject | null

    Elite-tier geographic restrictions, checked at scan time (Layer 2). Both forms are optional and independent — a brand can allow-list countries, fence a radius around a point, or do both.

    +Show 4 child attributes
    • allowed_countriesarray of strings | null

      ISO country codes this item may legitimately be scanned in

    • radius_kmnumber | null

      Allowed distance from the anchor point

    • anchor_latnumber | null
    • anchor_lonnumber | null
PUT /brands/products/{product_id}
curl
curl -X PUT http://62.169.23.239:8001/api/v1/brands/products/f6abe715-90aa-5908-a0a6-12951a3a21cc \
  -H "Authorization: Bearer $SKANNIT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F 'product={"product_name": "Trail Tee", "status": "active", "base_sku": "TT-100", "category": "Tops", "price": 19.0, "sizes": ["S", "M"], "colors": ["White"], "variants": [{"color": "White", "size": "S", "volume_l": 0.5}, {"color": "White", "size": "M", "volume_l": 0.5}]}' \
  -F image_front=@front.jpg
200Response
json
{
  "product_name": "Trail Tee",
  "status": "draft",
  "base_sku": "TT-100",
  "category": "Tops",
  "gender": "unisex",
  "price": 19,
  "msrp": 29,
  "description": "Organic cotton tee",
  "season": "AW26",
  "gtin_status": "has_own",
  "gtin_or_uuid": "08712345678906",
  "organic": false,
  "recycled_content": false,
  "fabric_weight_gsm": 1,
  "texture_finish": "brushed",
  "sizes": [
    "S",
    "M",
    "L"
  ],
  "colors": [
    "White",
    "Black"
  ],
  "variants": [
    "White / M"
  ],
  "id_assignment": "unique",
  "authentication_hardware": {
    "nfc_chip": false,
    "anti_counterfeit_thread": false
  },
  "internal_tags": [
    "internal tag"
  ],
  "approval_status": "pending_design_lead",
  "private_notes": null,
  "media_urls": {},
  "spec_sheet_url": "https://files.skannit.example/spec_sheet.png",
  "certificate_urls": [
    "https://files.skannit.example/certificate.png"
  ],
  "product_id": "f6abe715-90aa-5908-a0a6-12951a3a21cc",
  "brand_id": "8ea3462c-426c-5cc1-84d7-db99868bca1c",
  "updated_at": "2026-10-09T09:30:00Z",
  "created_at": "2026-10-09T09:30:00Z",
  "carbon_co2": 3.2,
  "water_used_l": 2700,
  "circular_score": 72,
  "end_of_life_instructions": "Return to any Northwind store for recycling",
  "repair_difficulty": "easy",
  "spare_parts_included": [
    "buttons"
  ],
  "repair_instructions_url": "https://files.skannit.example/repair_instructions.png",
  "country_of_origin": "PT",
  "main_supplier": "Tecidos do Norte",
  "compliance_standards": {
    "eu_dpp_ready": true,
    "esg_certified": true,
    "reach_compliant": true
  },
  "stock_status": "IN STOCK",
  "category_path": "Apparel / Tops",
  "specifications": {
    "primary_material": "Organic cotton",
    "membrane_type": "none",
    "weight": "180 g",
    "sustainability": "GOTS certified",
    "warranty": "2 years"
  },
  "images": [
    "image"
  ],
  "stock_distribution": [
    {
      "location_name": "Rotterdam DC",
      "units": 24
    }
  ],
  "material_specifications_compliance": "REACH compliant",
  "active_catalog": true,
  "lifecycle_stage": "in_production",
  "inventory_on_hand": 1,
  "inventory_reserved": 1,
  "low_stock_threshold": 1,
  "last_modified_at": null,
  "last_modified_by": "ops@northwind.example",
  "tech_pack_url": "https://files.skannit.example/tech_pack.png",
  "protection_tier": "standard",
  "geo_lock_rules": {
    "allowed_countries": [
      "allowed country"
    ],
    "radius_km": 1,
    "anchor_lat": 51.9244,
    "anchor_lon": 4.4777
  }
}

List products#

GET/products

Requires scope products:read

Parameters

  • searchstringquery

    Search name, SKU, category, or tier

  • product_tierstringquery

    Filter by product tier (maps to category)

  • origin_countrystringquery

    Filter by country of origin

  • statusstringquery

    Stock status: Active, Low Stock, or Inactive

    One ofDraftActiveInactive
  • pageintegerquery

    Defaults to 1

  • limitintegerquery

    Defaults to 5

Returns

HTTP 200
  • itemsarray of objectsRequired

    The objects on this page.

    +Show 8 child attributes
    • idstringRequired
    • namestringRequired
    • categorystringRequired
    • image_urlstring | null
    • skustringRequired

      Stock-keeping unit of the variant.

    • variantsarray of strings
    • product_tierstringRequired
    • statusstringRequired

      Current state of the object. See the allowed values.

      One ofDraftActiveInactive
  • paginationobjectRequired

    Page position: current_page, limit, total_pages, total_results.

    +Show 6 child attributes
    • total_resultsintegerRequired
    • total_pagesintegerRequired
    • current_pageintegerRequired
    • limitintegerRequired
    • showing_fromintegerRequired
    • showing_tointegerRequired
GET /products
curl
curl http://62.169.23.239:8001/api/v1/products \
  -H "Authorization: Bearer $SKANNIT_TOKEN"
200Response
json
{
  "items": [
    {
      "id": "SKN-CRT-00402-9A1C3E7F",
      "name": "Northwind Apparel",
      "category": "Tops",
      "image_url": "https://files.skannit.example/image.png",
      "sku": "TT-100-WHT-M",
      "variants": [
        "White / M"
      ],
      "product_tier": "standard",
      "status": "Draft"
    }
  ],
  "pagination": {
    "total_results": 24,
    "total_pages": 24,
    "current_page": 1,
    "limit": 1,
    "showing_from": 1,
    "showing_to": 1
  }
}

Set protection tier#

PUT/brands/products/{product_id}/protection

Requires scope products:write

Controls how often this product's QR scanToken rotates, which is what makes a copied QR stop working:

- **standard** — rotates when custody changes hands (factory → shop → customer). Suits food and FMCG. - **premium** — also rotates on every authenticated consumer scan. Suits fashion and electronics. - **elite** — premium, plus geo-lock rules and periodic 'tap to verify' prompts to the owner so a copy cannot keep working on an item that is never scanned. Suits luxury and pharma.

Applies forward only — units already minted are not re-issued, they are simply treated under the new tier from their next scan onward.

Parameters

  • product_idstringpathRequired

    Skannit's id for the product.

Request body

  • protection_tierstringRequired

    How strongly the product is protected against counterfeits.

    One ofstandardpremiumelite
  • geo_lock_rulesobject | null

    Elite tier only; ignored for standard/premium

    +Show 4 child attributes
    • allowed_countriesarray of strings | null

      ISO country codes this item may legitimately be scanned in

    • radius_kmnumber | null

      Allowed distance from the anchor point

    • anchor_latnumber | null
    • anchor_lonnumber | null

Returns

HTTP 200
  • product_idstringRequired

    Skannit's id for the product.

  • protection_tierstringRequired

    How strongly the product is protected against counterfeits.

    One ofstandardpremiumelite
  • geo_lock_rulesobject | null

    Elite-tier geographic restrictions, checked at scan time (Layer 2). Both forms are optional and independent — a brand can allow-list countries, fence a radius around a point, or do both.

    +Show 4 child attributes
    • allowed_countriesarray of strings | null

      ISO country codes this item may legitimately be scanned in

    • radius_kmnumber | null

      Allowed distance from the anchor point

    • anchor_latnumber | null
    • anchor_lonnumber | null
PUT /brands/products/{product_id}/protection
curl
curl -X PUT http://62.169.23.239:8001/api/v1/brands/products/f6abe715-90aa-5908-a0a6-12951a3a21cc/protection \
  -H "Authorization: Bearer $SKANNIT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "protection_tier": "standard"
  }'
200Response
json
{
  "product_id": "f6abe715-90aa-5908-a0a6-12951a3a21cc",
  "protection_tier": "standard",
  "geo_lock_rules": {
    "allowed_countries": [
      "allowed country"
    ],
    "radius_km": 1,
    "anchor_lat": 51.9244,
    "anchor_lon": 4.4777
  }
}

Delete a product#

DELETE/brands/products/{product_id}

Requires scope products:write

Removes the product from the catalogue, its filters and its summary counts, and stops it being selectable for new production orders.

The underlying row is retained, because production orders, QR batches and already-minted units reference this product_id and must keep resolving — a passport scanned by a consumer on an item made years ago still has to show what it was. The product's base_sku is released, so the brand can create a new product under the same SKU.

Returns 409 while a submitted production order still references the product; cancel those orders first.

Parameters

  • product_idstringpathRequired

    Skannit's id for the product.

Returns

HTTP 204

No body.

DELETE /brands/products/{product_id}
curl
curl -X DELETE http://62.169.23.239:8001/api/v1/brands/products/f6abe715-90aa-5908-a0a6-12951a3a21cc \
  -H "Authorization: Bearer $SKANNIT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
204Response
json
// 204 — no body
NextProduction orders→