Cikada

Cikada Vision API

Developers

Everything the interface does, a robot can do. Authenticate with a bearer key from Settings, send images, receive typed attributes with a confidence per field. Base URL https://vision.cikadasolutions.com/api/v1. Rate limit 60 requests per minute per key. Errors always look like { "error": { "code", "message", "details?" } }.

Quickstart

curl

curl -X POST https://vision.cikadasolutions.com/api/v1/items \
  -H "Authorization: Bearer ck_live_…" \
  -H "Idempotency-Key: run-42-item-7" \
  -F "image[]=@front.jpg" -F "view[]=front" \
  -F "image[]=@label.jpg" -F "view[]=label" \
  -F "external_ref=run-42-item-7"

# → 202 {"item_id":"…","status":"queued"}
curl https://vision.cikadasolutions.com/api/v1/items/ITEM_ID -H "Authorization: Bearer ck_live_…"

Python

import requests

API = "https://vision.cikadasolutions.com/api/v1"
HEADERS = {"Authorization": "Bearer ck_live_…"}

# Synchronous: image in, classification out (p95 < 15 s)
with open("front.jpg", "rb") as front:
    r = requests.post(f"{API}/classify/sync", headers=HEADERS,
                      files=[("image[]", ("front.jpg", front, "image/jpeg"))],
                      data={"view[]": "front", "external_ref": "run-42-item-7"})
r.raise_for_status()
item = r.json()
print(item["attributes"]["garment_type"])   # {'value': 'jeans', 'confidence': 0.97, 'source': 'model'}
print(item["attributes"]["condition_grade"]["value"], item["classification"]["recommended_route"]["value"])

# Correct a value from the operator panel
requests.patch(f"{API}/items/{item['id']}/attributes", headers=HEADERS,
               json={"attributes": {"condition_grade": "C"}, "reviewer": "line-3-operator"}).raise_for_status()

Endpoints

Items

Garments and their classifications

post/itemsCreate an item and queue classification

Multipart: fields image[] (1–8 files, JPEG/PNG/WebP/HEIC, 20 MB each), view[] (front|back|label|detail), external_ref, robot_id, batch_id. Or JSON with image_urls for pre-hosted HTTPS images. Send an Idempotency-Key header to make retries safe for 24 hours.

ParameterInTypeRequired
Idempotency-Keyheaderstringno
Request body
{
  "multipart/form-data": {
    "schema": {
      "type": "object",
      "required": [
        "image[]"
      ],
      "properties": {
        "image[]": {
          "type": "array",
          "items": {
            "type": "string",
            "format": "binary"
          }
        },
        "view[]": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "front",
              "back",
              "label",
              "detail",
              "unknown"
            ]
          }
        },
        "external_ref": {
          "type": "string",
          "maxLength": 120
        },
        "robot_id": {
          "type": "string",
          "format": "uuid"
        },
        "batch_id": {
          "type": "string",
          "format": "uuid"
        }
      }
    }
  },
  "application/json": {
    "schema": {
      "$ref": "#/components/schemas/CreateItemJson"
    }
  }
}
Responses
{
  "202": {
    "description": "Item queued",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/ItemAccepted"
        }
      }
    }
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "403": {
    "description": "API key lacks the required scope",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "422": {
    "description": "Request failed validation",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}
get/itemsList items
ParameterInTypeRequired
statusquerystring (queued | processing | classified | reviewed | failed)no
batch_idquerystringno
sincequerystringno
cursorquerystringno
limitqueryintegerno
Responses
{
  "200": {
    "description": "Page of items, newest first",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/ItemList"
        }
      }
    }
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "422": {
    "description": "Request failed validation",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}
get/items/{id}Get an item
ParameterInTypeRequired
idpathstringyes
Responses
{
  "200": {
    "description": "Item with images, current attributes and prediction history",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Item"
        }
      }
    }
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "404": {
    "description": "Not found in this organization",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}
post/items/{id}/classifyRe-run classification

Queues a new prediction, for example after adding a label image. Existing predictions and corrections are kept.

ParameterInTypeRequired
idpathstringyes
Responses
{
  "202": {
    "description": "Item queued",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/ItemAccepted"
        }
      }
    }
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "403": {
    "description": "API key lacks the required scope",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "404": {
    "description": "Not found in this organization",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}
patch/items/{id}/attributesSubmit human corrections

Each changed attribute becomes a new human row beside the model row. Emits item.reviewed.

ParameterInTypeRequired
idpathstringyes
Request body
{
  "application/json": {
    "schema": {
      "$ref": "#/components/schemas/PatchAttributes"
    }
  }
}
Responses
{
  "200": {
    "description": "Corrections written",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/PatchResult"
        }
      }
    }
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "403": {
    "description": "API key lacks the required scope",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "404": {
    "description": "Not found in this organization",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "422": {
    "description": "Request failed validation",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}

Classification

Synchronous classification for low-latency robot use

post/classify/syncClassify synchronously

Same body as POST /items. The item is stored, classified inline and returned in full. Target p95 under 15 s.

Request body
{
  "multipart/form-data": {
    "schema": {
      "type": "object",
      "required": [
        "image[]"
      ],
      "properties": {
        "image[]": {
          "type": "array",
          "items": {
            "type": "string",
            "format": "binary"
          }
        },
        "view[]": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "external_ref": {
          "type": "string"
        },
        "robot_id": {
          "type": "string",
          "format": "uuid"
        },
        "batch_id": {
          "type": "string",
          "format": "uuid"
        }
      }
    }
  },
  "application/json": {
    "schema": {
      "$ref": "#/components/schemas/CreateItemJson"
    }
  }
}
Responses
{
  "200": {
    "description": "Classified item",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Item"
        }
      }
    }
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "403": {
    "description": "API key lacks the required scope",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "422": {
    "description": "Request failed validation",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "502": {
    "description": "The model failed",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}

Batches

Groups of items, such as one robot run

post/batchesCreate a batch
Request body
{
  "application/json": {
    "schema": {
      "$ref": "#/components/schemas/CreateBatch"
    }
  }
}
Responses
{
  "201": {
    "description": "Batch created",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Batch"
        }
      }
    }
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "403": {
    "description": "API key lacks the required scope",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "422": {
    "description": "Request failed validation",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}
get/batches/{id}Get a batch
ParameterInTypeRequired
idpathstringyes
Responses
{
  "200": {
    "description": "Batch",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Batch"
        }
      }
    }
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "404": {
    "description": "Not found in this organization",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}
get/batches/{id}/exportExport a batch
ParameterInTypeRequired
idpathstringyes
formatquerystring (csv | json)no
Responses
{
  "200": {
    "description": "All items in the batch",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Item"
              }
            }
          }
        }
      },
      "text/csv": {
        "schema": {
          "type": "string"
        },
        "example": "id,status,...\r\n"
      }
    }
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "404": {
    "description": "Not found in this organization",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}

Webhooks

Signed event deliveries with retries

post/webhooksCreate a webhook

Deliveries are JSON POSTs `{ event, item_id, org_id, occurred_at, data }` with `X-Cikada-Signature: sha256=HMAC-SHA256(secret, body)`. Failed deliveries retry after 1 m, 5 m, 30 m, 2 h and 12 h. The secret is returned once.

Request body
{
  "application/json": {
    "schema": {
      "$ref": "#/components/schemas/CreateWebhook"
    }
  }
}
Responses
{
  "201": {
    "description": "Webhook created; includes the secret",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Webhook"
        }
      }
    }
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "403": {
    "description": "API key lacks the required scope",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "422": {
    "description": "Request failed validation",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}
get/webhooksList webhooks
Responses
{
  "200": {
    "description": "Webhooks without secrets",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Webhook"
              }
            }
          }
        }
      }
    }
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "403": {
    "description": "API key lacks the required scope",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}
get/webhooks/{id}Get a webhook
ParameterInTypeRequired
idpathstringyes
Responses
{
  "200": {
    "description": "Webhook",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Webhook"
        }
      }
    }
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "403": {
    "description": "API key lacks the required scope",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "404": {
    "description": "Not found in this organization",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}
delete/webhooks/{id}Delete a webhook
ParameterInTypeRequired
idpathstringyes
Responses
{
  "204": {
    "description": "Deleted"
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "403": {
    "description": "API key lacks the required scope",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "404": {
    "description": "Not found in this organization",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}
get/webhooks/{id}/deliveriesRecent deliveries
ParameterInTypeRequired
idpathstringyes
limitqueryintegerno
Responses
{
  "200": {
    "description": "Newest first",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/WebhookDelivery"
              }
            }
          }
        }
      }
    }
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "403": {
    "description": "API key lacks the required scope",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "404": {
    "description": "Not found in this organization",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}

Reference

Taxonomy and health

get/healthService healthno auth
Responses
{
  "200": {
    "description": "Component status",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Health"
        }
      }
    }
  }
}
get/taxonomyCurrent enums and grading rubric

Fetch once and validate locally before sending corrections.

Responses
{
  "200": {
    "description": "Taxonomy",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Taxonomy"
        }
      }
    }
  },
  "401": {
    "description": "Missing, malformed or revoked API key",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  },
  "429": {
    "description": "Rate limit exceeded; see Retry-After",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Error"
        }
      }
    }
  }
}

Schemas

Generated from the same Zod contracts the service validates with.

Error
{
  "type": "object",
  "properties": {
    "error": {
      "type": "object",
      "properties": {
        "code": {
          "type": "string",
          "enum": [
            "validation_error",
            "unauthorized",
            "forbidden",
            "not_found",
            "rate_limited",
            "model_error",
            "internal"
          ]
        },
        "message": {
          "type": "string"
        },
        "details": {}
      },
      "required": [
        "code",
        "message"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "error"
  ],
  "additionalProperties": false,
  "description": "Every failure, on every endpoint."
}
Health
{
  "type": "object",
  "properties": {
    "status": {
      "type": "string",
      "enum": [
        "ok",
        "degraded"
      ]
    },
    "db": {
      "type": "string",
      "enum": [
        "ok",
        "error"
      ]
    },
    "storage": {
      "type": "string",
      "enum": [
        "ok",
        "error"
      ]
    },
    "model": {
      "type": "string"
    },
    "version": {
      "type": "string"
    }
  },
  "required": [
    "status",
    "db",
    "storage",
    "model",
    "version"
  ],
  "additionalProperties": false
}
Taxonomy
{
  "type": "object",
  "properties": {
    "version": {
      "type": "string"
    },
    "enums": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {
          "type": "string"
        }
      }
    },
    "rubric": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {
        "type": "string"
      }
    },
    "attributes": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "version",
    "enums",
    "rubric",
    "attributes"
  ],
  "additionalProperties": false
}
Item
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "status": {
      "type": "string",
      "enum": [
        "queued",
        "processing",
        "classified",
        "reviewed",
        "failed"
      ]
    },
    "source": {
      "type": "string",
      "enum": [
        "upload",
        "camera",
        "robot",
        "api"
      ]
    },
    "external_ref": {
      "type": [
        "string",
        "null"
      ]
    },
    "batch_id": {
      "anyOf": [
        {
          "type": "string",
          "format": "uuid",
          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
        },
        {
          "type": "null"
        }
      ]
    },
    "robot_id": {
      "anyOf": [
        {
          "type": "string",
          "format": "uuid",
          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
        },
        {
          "type": "null"
        }
      ]
    },
    "site_id": {
      "anyOf": [
        {
          "type": "string",
          "format": "uuid",
          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
        },
        {
          "type": "null"
        }
      ]
    },
    "error": {
      "type": [
        "string",
        "null"
      ]
    },
    "created_at": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
    },
    "classified_at": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
        },
        {
          "type": "null"
        }
      ]
    },
    "reviewed_at": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
        },
        {
          "type": "null"
        }
      ]
    },
    "images": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ItemImage"
      }
    },
    "attributes": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {
        "$ref": "#/components/schemas/Attribute"
      }
    },
    "classification": {
      "anyOf": [
        {
          "$ref": "#/components/schemas/GarmentClassification"
        },
        {
          "type": "null"
        }
      ],
      "description": "Latest full model output, validated against the contract"
    },
    "predictions": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Prediction"
      }
    }
  },
  "required": [
    "id",
    "status",
    "source",
    "external_ref",
    "batch_id",
    "robot_id",
    "site_id",
    "error",
    "created_at",
    "classified_at",
    "reviewed_at",
    "images",
    "attributes",
    "classification",
    "predictions"
  ],
  "additionalProperties": false
}
ItemAccepted
{
  "type": "object",
  "properties": {
    "item_id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "status": {
      "type": "string",
      "const": "queued"
    }
  },
  "required": [
    "item_id",
    "status"
  ],
  "additionalProperties": false
}
ItemList
{
  "type": "object",
  "properties": {
    "data": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Item"
      }
    },
    "next_cursor": {
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "data",
    "next_cursor"
  ],
  "additionalProperties": false
}
CreateItemJson
{
  "type": "object",
  "properties": {
    "image_urls": {
      "minItems": 1,
      "maxItems": 8,
      "type": "array",
      "items": {
        "type": "string",
        "format": "uri"
      }
    },
    "views": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "front",
          "back",
          "label",
          "detail",
          "unknown"
        ]
      }
    },
    "external_ref": {
      "type": "string",
      "maxLength": 120
    },
    "robot_id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "batch_id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    }
  },
  "required": [
    "image_urls"
  ],
  "additionalProperties": false,
  "description": "Alternative to multipart: images already hosted at HTTPS URLs."
}
PatchAttributes
{
  "type": "object",
  "properties": {
    "attributes": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {
        "type": "string",
        "minLength": 1,
        "maxLength": 60
      }
    },
    "reviewer": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80
    }
  },
  "required": [
    "attributes",
    "reviewer"
  ],
  "additionalProperties": false
}
PatchResult
{
  "type": "object",
  "properties": {
    "item_id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "corrections": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "item_id",
    "corrections"
  ],
  "additionalProperties": false
}
CreateBatch
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 120
    },
    "source": {
      "default": "robot",
      "type": "string",
      "enum": [
        "upload",
        "camera",
        "robot",
        "api"
      ]
    }
  },
  "required": [
    "name",
    "source"
  ],
  "additionalProperties": false
}
Batch
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "name": {
      "type": "string"
    },
    "source": {
      "type": "string",
      "enum": [
        "upload",
        "camera",
        "robot",
        "api"
      ]
    },
    "item_count": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "created_at": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
    }
  },
  "required": [
    "id",
    "name",
    "source",
    "item_count",
    "created_at"
  ],
  "additionalProperties": false
}
Attribute
{
  "type": "object",
  "properties": {
    "value": {
      "type": "string"
    },
    "confidence": {
      "type": "number",
      "minimum": 0,
      "maximum": 1
    },
    "source": {
      "type": "string",
      "enum": [
        "model",
        "human"
      ]
    },
    "evidence": {
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "value",
    "confidence",
    "source"
  ],
  "additionalProperties": false,
  "description": "Current value of one attribute: latest human correction if any, else the latest model value."
}
ItemImage
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "view": {
      "type": "string",
      "enum": [
        "front",
        "back",
        "label",
        "detail",
        "unknown"
      ]
    },
    "position": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "width": {
      "anyOf": [
        {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        {
          "type": "null"
        }
      ]
    },
    "height": {
      "anyOf": [
        {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        {
          "type": "null"
        }
      ]
    },
    "urls": {
      "type": "object",
      "properties": {
        "original": {
          "anyOf": [
            {
              "type": "string",
              "format": "uri"
            },
            {
              "type": "null"
            }
          ]
        },
        "model_input": {
          "anyOf": [
            {
              "type": "string",
              "format": "uri"
            },
            {
              "type": "null"
            }
          ]
        },
        "thumbnail": {
          "anyOf": [
            {
              "type": "string",
              "format": "uri"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "original",
        "model_input",
        "thumbnail"
      ],
      "additionalProperties": false,
      "description": "Signed URLs valid for one hour"
    }
  },
  "required": [
    "id",
    "view",
    "position",
    "width",
    "height",
    "urls"
  ],
  "additionalProperties": false
}
Prediction
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "model": {
      "type": "string"
    },
    "prompt_version": {
      "type": "string"
    },
    "latency_ms": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "input_tokens": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "output_tokens": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "attempts": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "created_at": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
    }
  },
  "required": [
    "id",
    "model",
    "prompt_version",
    "latency_ms",
    "input_tokens",
    "output_tokens",
    "attempts",
    "created_at"
  ],
  "additionalProperties": false
}
CreateWebhook
{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "maxLength": 2000,
      "format": "uri"
    },
    "events": {
      "minItems": 1,
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "item.classified",
          "item.reviewed"
        ]
      }
    },
    "description": {
      "type": "string",
      "maxLength": 200
    }
  },
  "required": [
    "url",
    "events"
  ],
  "additionalProperties": false
}
Webhook
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "url": {
      "type": "string"
    },
    "events": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "active": {
      "type": "boolean"
    },
    "description": {
      "type": [
        "string",
        "null"
      ]
    },
    "failure_count": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "last_delivery_at": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
        },
        {
          "type": "null"
        }
      ]
    },
    "created_at": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
    },
    "secret": {
      "description": "Only present in the creation response",
      "type": "string"
    }
  },
  "required": [
    "id",
    "url",
    "events",
    "active",
    "description",
    "failure_count",
    "last_delivery_at",
    "created_at"
  ],
  "additionalProperties": false
}
WebhookDelivery
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "event": {
      "type": "string"
    },
    "status": {
      "type": "string",
      "enum": [
        "pending",
        "delivered",
        "failed",
        "exhausted"
      ]
    },
    "status_code": {
      "anyOf": [
        {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        {
          "type": "null"
        }
      ]
    },
    "attempts": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "next_retry_at": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
    },
    "delivered_at": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
        },
        {
          "type": "null"
        }
      ]
    },
    "last_error": {
      "type": [
        "string",
        "null"
      ]
    },
    "created_at": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
    }
  },
  "required": [
    "id",
    "event",
    "status",
    "status_code",
    "attempts",
    "next_retry_at",
    "delivered_at",
    "last_error",
    "created_at"
  ],
  "additionalProperties": false
}
GarmentClassification
{
  "type": "object",
  "properties": {
    "garment_type": {
      "type": "object",
      "properties": {
        "value": {
          "type": "string",
          "enum": [
            "t_shirt",
            "shirt",
            "blouse",
            "polo",
            "tank_top",
            "sweater",
            "cardigan",
            "hoodie",
            "sweatshirt",
            "vest",
            "jeans",
            "trousers",
            "shorts",
            "skirt",
            "leggings",
            "joggers",
            "dress",
            "jumpsuit",
            "overalls",
            "jacket",
            "coat",
            "blazer",
            "parka",
            "raincoat",
            "underwear",
            "socks",
            "swimwear",
            "sportswear",
            "nightwear",
            "scarf",
            "hat",
            "gloves",
            "bag",
            "shoes",
            "home_textile",
            "workwear",
            "other",
            "unknown"
          ],
          "description": "Type of garment"
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "garment_category": {
      "type": "object",
      "properties": {
        "value": {
          "type": "string",
          "enum": [
            "tops",
            "bottoms",
            "full_body",
            "outerwear",
            "underwear",
            "accessories",
            "footwear",
            "home_textile",
            "other",
            "unknown"
          ],
          "description": "Category the garment type belongs to"
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "primary_color": {
      "type": "object",
      "properties": {
        "value": {
          "type": "string",
          "enum": [
            "black",
            "white",
            "grey",
            "navy",
            "blue",
            "light_blue",
            "green",
            "olive",
            "red",
            "burgundy",
            "pink",
            "purple",
            "orange",
            "yellow",
            "beige",
            "brown",
            "cream",
            "multicolor",
            "unknown"
          ],
          "description": "Colour covering most of the garment"
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "secondary_color": {
      "type": "object",
      "properties": {
        "value": {
          "type": "string",
          "enum": [
            "black",
            "white",
            "grey",
            "navy",
            "blue",
            "light_blue",
            "green",
            "olive",
            "red",
            "burgundy",
            "pink",
            "purple",
            "orange",
            "yellow",
            "beige",
            "brown",
            "cream",
            "multicolor",
            "none",
            "unknown"
          ],
          "description": "Second most prominent colour, or none"
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "color_hex": {
      "type": "object",
      "properties": {
        "value": {
          "anyOf": [
            {
              "type": "string",
              "pattern": "^#[0-9A-F]{6}$",
              "description": "Dominant colour as an uppercase hex value like #1B4D3E"
            },
            {
              "type": "string",
              "const": "unknown"
            }
          ]
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "pattern": {
      "type": "object",
      "properties": {
        "value": {
          "type": "string",
          "enum": [
            "solid",
            "striped",
            "checked",
            "floral",
            "graphic_print",
            "logo_print",
            "dotted",
            "camouflage",
            "animal",
            "abstract",
            "other",
            "unknown"
          ],
          "description": "Surface pattern"
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "condition_grade": {
      "type": "object",
      "properties": {
        "value": {
          "type": "string",
          "enum": [
            "A",
            "B",
            "C",
            "D",
            "unknown"
          ],
          "description": "Condition grade A to D per the rubric"
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "defects": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "stain",
              "hole",
              "tear",
              "pilling",
              "fading",
              "missing_button",
              "broken_zipper",
              "stretched",
              "odor_visible_signs",
              "discoloration",
              "other"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "minor",
              "moderate",
              "severe"
            ]
          },
          "location": {
            "type": "string",
            "maxLength": 60,
            "description": "Where on the garment, at most 60 characters"
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Confidence between 0 and 1"
          }
        },
        "required": [
          "type",
          "severity",
          "location",
          "confidence"
        ],
        "additionalProperties": false
      }
    },
    "recommended_route": {
      "type": "object",
      "properties": {
        "value": {
          "type": "string",
          "enum": [
            "reuse",
            "repair",
            "recycle_fiber",
            "waste",
            "unknown"
          ],
          "description": "Recommended handling route"
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "material_primary": {
      "type": "object",
      "properties": {
        "value": {
          "type": "string",
          "enum": [
            "cotton",
            "polyester",
            "wool",
            "viscose",
            "linen",
            "nylon",
            "acrylic",
            "elastane",
            "silk",
            "cashmere",
            "denim_cotton",
            "leather",
            "blend",
            "unknown"
          ],
          "description": "Main fibre"
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "material_composition": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "fiber": {
            "type": "string",
            "description": "Fibre name as printed on the care label"
          },
          "percent": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Percentage 0 to 100"
          }
        },
        "required": [
          "fiber",
          "percent"
        ],
        "additionalProperties": false
      },
      "description": "Only from a visible care label. Empty when no label is readable."
    },
    "material_source": {
      "type": "string",
      "enum": [
        "care_label_ocr",
        "visual_estimate",
        "unknown"
      ]
    },
    "brand": {
      "type": "object",
      "properties": {
        "value": {
          "type": "string",
          "maxLength": 60,
          "description": "Brand from a visible label or unmistakable logo. Use \"unknown\" when not visible."
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "size": {
      "type": "object",
      "properties": {
        "value": {
          "type": "string",
          "maxLength": 12,
          "description": "Size from a visible label. Use \"unknown\" when not visible."
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "gender_segment": {
      "type": "object",
      "properties": {
        "value": {
          "type": "string",
          "enum": [
            "women",
            "men",
            "unisex",
            "kids",
            "baby",
            "unknown"
          ],
          "description": "Intended wearer"
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "sleeve_length": {
      "type": "object",
      "properties": {
        "value": {
          "type": "string",
          "enum": [
            "sleeveless",
            "short",
            "three_quarter",
            "long",
            "not_applicable",
            "unknown"
          ],
          "description": "Sleeve length"
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "closure": {
      "type": "object",
      "properties": {
        "value": {
          "type": "string",
          "enum": [
            "none",
            "buttons",
            "zipper",
            "hook",
            "drawstring",
            "velcro",
            "other",
            "unknown"
          ],
          "description": "Main closure"
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "season": {
      "type": "object",
      "properties": {
        "value": {
          "type": "string",
          "enum": [
            "summer",
            "winter",
            "all_season",
            "unknown"
          ],
          "description": "Season the garment is made for"
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "description": "Confidence between 0 and 1"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "value",
        "confidence"
      ],
      "additionalProperties": false
    },
    "care_label_text": {
      "description": "Verbatim text of a visible care label, or null",
      "anyOf": [
        {
          "type": "string",
          "maxLength": 500
        },
        {
          "type": "null"
        }
      ]
    },
    "notes": {
      "description": "Anything an operator should know, or null",
      "anyOf": [
        {
          "type": "string",
          "maxLength": 300
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "garment_type",
    "garment_category",
    "primary_color",
    "secondary_color",
    "color_hex",
    "pattern",
    "condition_grade",
    "defects",
    "recommended_route",
    "material_primary",
    "material_composition",
    "material_source",
    "brand",
    "size",
    "gender_segment",
    "sleeve_length",
    "closure",
    "season"
  ],
  "additionalProperties": false,
  "description": "Full model output: every attribute with a 0–1 confidence, defects, composition and label text."
}

Webhooks

Subscribe in Settings to item.classified and item.reviewed. Each delivery is a JSON POST { event, item_id, org_id, occurred_at, data } signed with your endpoint secret. Verify before acting on it. Failed deliveries retry five times over twelve hours.

import hashlib, hmac

def verify(secret: str, body: bytes, signature_header: str) -> bool:
    # X-Cikada-Signature: sha256=<hex HMAC of the raw body>
    expected = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature_header)
Developers · Cikada Vision