> For the complete documentation index, see [llms.txt](https://docs.imerit.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.imerit.net/api/docs/models.md).

# Models

## The ErrorResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ErrorResponse":{"type":"object","properties":{"status":{"type":"string","enum":["fail","error"]},"message":{"type":"string"}}}}}}
```

## The MessageResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"MessageResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"message":{"type":"string"}}}}}}}}
```

## The ProjectListItem object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ProjectListItem":{"type":"object","properties":{"_id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"organization":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}}}}}
```

## The Project object

````json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"Project":{"type":"object","properties":{"_id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"type":{"type":"string","enum":["ango","pct","iframe"],"default":"ango"},"owner":{"type":"string","description":"Owner email"},"organization":{"type":"string","description":"Organization ID"},"categorySchema":{"$ref":"#/components/schemas/CategorySchema"},"stages":{"type":"array","items":{"$ref":"#/components/schemas/Stage"}},"assignedTo":{"type":"array","items":{"$ref":"#/components/schemas/Assignee"}},"batches":{"type":"array","items":{"$ref":"#/components/schemas/Batch"}},"queueVersion":{"type":"number"},"assetCount":{"type":"integer"},"benchmarkEnabled":{"type":"boolean"},"benchmarkRatio":{"type":"number"},"instructions":{"type":"string","description":"Instructions URL"},"ocrLanguage":{"type":"string"},"idleTimeout":{"type":"number","description":"Idle timeout in seconds"},"projectPlugins":{"type":"object"},"pluginPresets":{"type":"array","items":{"type":"object"}},"errorCodesEnabled":{"type":"boolean"},"errorSchema":{"type":"object"},"exportStorageId":{"type":"string"},"exportStorageBucket":{"type":"string"},"exportStoragePath":{"type":"string"},"exportStorageEnabled":{"type":"boolean"},"taskSkipEnabled":{"type":"boolean"},"unassignSkippedTask":{"type":"boolean"},"gcSyncEnabled":{"type":"boolean"},"pctConfig":{"$ref":"#/components/schemas/PctConfig"},"role":{"type":"string","description":"Caller's role on this project (present on GET /project/{projectId} only)","enum":["Manager","Lead","Labeler","Reviewer"]},"deleted":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CategorySchema":{"type":"object","description":"Label configuration schema that defines the annotation tools, classifications, and relations for a project.\nThis schema determines what annotation types are available to users and how they can label assets.\n\n**Note:** Schema structure varies between Ango and PCT projects.\n- Ango projects: Use standard annotation tools (bounding-box, polygon, etc.)\n- PCT projects: Use PCT tool type with markingTools array and nested classifications with PCT-specific fields\n","properties":{"tools":{"type":"array","description":"Array of annotation tools (bounding box, polygon, segmentation, etc.)","items":{"$ref":"#/components/schemas/Tool"}},"classifications":{"type":"array","description":"Array of classification questions (radio, checkbox, dropdown, text, etc.)","items":{"$ref":"#/components/schemas/Classification"}},"relations":{"type":"array","description":"Array of relation types that can connect annotations","items":{"$ref":"#/components/schemas/Relation"}}}},"Tool":{"type":"object","description":"Annotation tool configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this tool"},"title":{"type":"string","description":"Display name for the tool"},"tool":{"type":"string","description":"Type of annotation tool","enum":["bounding-box","polygon","polyline","point","rotated-bounding-box","segmentation","brush","medical-brush","ner","pdf","pct","angle"]},"required":{"type":"boolean","description":"Whether this annotation is required","default":false},"color":{"type":"string","description":"Hex color code for this annotation class","pattern":"^#[0-9A-Fa-f]{6}$"},"shortcutKey":{"type":"string","description":"Keyboard shortcut for quick selection"},"classifications":{"type":"array","description":"Nested classifications specific to this tool","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow creating multiple instances of this annotation","default":false},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data (for organizing exported annotations)"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"markingTools":{"type":"array","description":"(PCT only) List of marking tools enabled for this PCT tool.\nAvailable marking tools: polygon, polygon2d, polyline, polyline2d, cuboid, rectangle\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}},"truncate":{"type":"boolean","description":"(PCT only) Enable truncation for this tool","default":false},"presetCuboidDimensions":{"type":"boolean","description":"(PCT only) Enable preset cuboid dimensions","default":false},"cuboidLength":{"type":"string","description":"(PCT only) Preset cuboid length value (stored as string)"},"cuboidWidth":{"type":"string","description":"(PCT only) Preset cuboid width value (stored as string)"},"cuboidHeight":{"type":"string","description":"(PCT only) Preset cuboid height value (stored as string)"},"keypointWithVerticalLine":{"type":"boolean","description":"(PCT only) Enable keypoint with vertical line feature","default":false},"edgeLengthConstraint":{"type":"string","description":"(PCT only) Minimum edge length constraint (stored as string)"},"hasInstance":{"type":"boolean","description":"(PCT only) Enable instance tracking for this tool","default":false}}},"Classification":{"type":"object","description":"Classification question configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this classification"},"title":{"type":"string","description":"Question or label text"},"tool":{"type":"string","description":"Type of classification input (same as 'type')","enum":["radio","checkbox","single-dropdown","multi-dropdown","tree-dropdown","single-tree-dropdown","text","boolean"]},"color":{"type":"string","description":"Hex color code for this classification","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether answering this classification is required","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut key"},"columnField":{"type":"boolean","description":"Whether to show in column view","default":false},"frameSpecific":{"type":"boolean","description":"Whether classification is frame-specific (for videos)","default":false},"showDropdown":{"type":"boolean","description":"Whether to show as dropdown in UI","default":false},"richText":{"type":"boolean","description":"Whether to enable rich text input (for text type)","default":false},"regex":{"type":"string","nullable":true,"description":"Regular expression for text validation (for text type)"},"parentOptionId":{"type":"string","nullable":true,"description":"Parent option ID for nested classifications"},"options":{"type":"array","description":"Available options (not used for text type)","items":{"$ref":"#/components/schemas/ClassificationOption"}},"treeOptions":{"type":"array","description":"Tree-structured options (for tree-dropdown types)","items":{"type":"object"}},"classifications":{"type":"array","description":"Nested child classifications","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow multiple selections","default":false},"parentToolType":{"type":"string","description":"(PCT only) Parent tool type this classification belongs to"},"parentToolId":{"type":"string","description":"(PCT only) Parent tool schemaId this classification is associated with"},"attributeLevel":{"type":"string","description":"(PCT only) Level at which this attribute applies","enum":["Class Level","Instance Level"]},"defaultValue":{"description":"Default value for this classification (type varies based on classification tool type)","oneOf":[{"type":"boolean"},{"type":"string"},{"type":"number"}]},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"attributeMode":{"type":"string","description":"(PCT only) Attribute mode determining how values persist across frames.\n- Time Varying: Value can change per frame\n- Constant: Single value for entire sequence\n- Sensor Specific: Different values per sensor\n","enum":["Time Varying","Constant","Sensor Specific"]},"associatedMarkingTool":{"type":"array","description":"(PCT only) Marking tools this classification is associated with.\nThis classification will only be available when using these marking tools.\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}}}},"ClassificationOption":{"type":"object","description":"Option for a classification question","required":["schemaId","value"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this option"},"value":{"type":"string","description":"Display text and internal value for this option"},"label":{"type":"string","description":"Alternative display text (deprecated, use 'value')"},"children":{"type":"array","description":"Nested classifications that appear when this option is selected","items":{"$ref":"#/components/schemas/Classification"}}}},"Relation":{"type":"object","description":"Relation type that can connect two annotations.\n\n**Relation Types:**\n- `one-to-one`: Direct connection between two annotations (Ango only)\n- `group`: Groups multiple annotations together (Ango only)\n- `one-to-many`: Parent-child hierarchical relationship (PCT only)\n\n**Note:** PCT projects support additional fields like `parentTool` and `childTools` for one-to-many relations.\n","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this relation"},"title":{"type":"string","description":"Display name for the relation"},"tool":{"type":"string","description":"Type of relation tool:\n- `one-to-one`: Direct 1:1 connection between annotations (Ango only)\n- `group`: Non-directional grouping of multiple annotations (Ango only)\n- `one-to-many`: Hierarchical parent-child relationship (PCT only)\n","enum":["one-to-one","group","one-to-many"]},"color":{"type":"string","description":"Hex color code for this relation","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether this relation must be used in annotations","default":false},"classifications":{"type":"array","description":"Nested classification questions attached to this relation.\nAllows adding metadata or attributes to the relationship itself.\n","default":[]},"multiple":{"type":"boolean","description":"Whether multiple instances of this relation can be created","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut to quickly create this relation"},"parentTool":{"type":"string","description":"(PCT only) For one-to-many relations, specifies the schemaId of the parent tool.\nOnly applicable when `tool: \"one-to-many\"`.\n"},"childTools":{"type":"array","description":"(PCT only) For one-to-many relations, specifies the schemaIds of allowed child tools.\nOnly applicable when `tool: \"one-to-many\"`.\n","items":{"type":"string"}}}},"Stage":{"type":"object","description":"Workflow stage configuration. Different stage types have different properties.\n- Label: Manual annotation stage\n- Review: QA/review stage\n- Logic: Conditional routing stage\n- Plugin: ML model integration stage\n- Webhook: External API callback stage\n- Consensus: Multi-annotator agreement stage\n- Hold: Pause stage\n- Start: Entry point (auto-forwards)\n- Complete: Terminal success stage\n- Archive: Terminal archive stage\n","required":["id","type"],"properties":{"id":{"type":"string","description":"Unique identifier for this stage"},"name":{"type":"string","description":"Display name for this stage"},"type":{"type":"string","description":"Type of stage","enum":["Label","Review","Logic","Plugin","Webhook","Consensus","Hold","Start","Complete","Archive"]},"next":{"type":"array","items":{"type":"string"},"description":"Array of next stage IDs (for Logic stages, order matters)"},"position":{"type":"object","description":"Position in workflow diagram UI","properties":{"x":{"type":"number"},"y":{"type":"number"}}},"assignedTo":{"type":"array","description":"Users assigned to work on this stage (Label/Review stages)","items":{"type":"string"}},"rememberAssignee":{"type":"boolean","description":"Auto-assign to previous stage's user (sticky assignment)","default":false},"logic":{"$ref":"#/components/schemas/LogicConfig","description":"Logic routing configuration (Logic stages only)"},"pluginId":{"type":"string","description":"Plugin ID to execute (Plugin stages only)"},"pluginConfig":{"type":"object","description":"Plugin-specific configuration (Plugin stages only)"},"webhook":{"$ref":"#/components/schemas/WebhookConfig","description":"Webhook configuration (Webhook stages only)"},"consensusConfig":{"$ref":"#/components/schemas/ConsensusConfig","description":"Consensus configuration (Consensus stages only)"},"readOnly":{"type":"boolean","description":"Whether this stage is read-only (users can view but not edit)","default":false},"preventRequeue":{"type":"boolean","description":"Prevent tasks from being requeued to this stage","default":false},"autoForward":{"type":"boolean","description":"Automatically advance to next stage without user action","default":true},"consensusId":{"type":"string","description":"Links stage to a consensus stage (used in consensus workflows)"},"consensusDynamic":{"type":"boolean","description":"Dynamic vs static consensus stage","default":false}}},"LogicConfig":{"type":"object","description":"Logic stage routing configuration. Routes tasks based on conditions.\nConditions are evaluated in order, first match wins.\n","properties":{"type":{"type":"string","description":"Type of logic routing","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Default value (usually empty)"},"conditions":{"type":"array","description":"Array of routing conditions","items":{"$ref":"#/components/schemas/LogicCondition"}}}},"LogicCondition":{"type":"object","description":"Individual routing condition for Logic stages","properties":{"type":{"type":"string","description":"Type of condition","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Condition-specific value","properties":{"tools":{"type":"array","description":"Tool filters (AnnotationType logic)","items":{"type":"object","properties":{"filter":{"type":"array","items":{"type":"string"},"description":"Annotation filters:\n- exists___<schemaId>: Annotation must exist\n- not-exist___<schemaId>: Annotation must NOT exist\n"}}}},"emails":{"type":"array","description":"User emails (Annotator logic)","items":{"type":"string"}},"sampleRate":{"type":"number","description":"Sample rate 0-1 (RandomSample logic)","minimum":0,"maximum":1},"duration":{"type":"number","description":"Duration in milliseconds (Duration logic)"},"operator":{"type":"string","description":"Comparison operator for Duration","enum":["lt","gt","eq"]},"batches":{"type":"array","description":"Batch names (Batch logic)","items":{"type":"string"}}}},"index":{"type":"number","description":"Index of next stage in parent's next array"}}},"WebhookConfig":{"type":"object","description":"Webhook configuration for external API integration","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"Webhook endpoint URL"},"method":{"type":"string","description":"HTTP method","enum":["POST","PUT"],"default":"POST"},"authType":{"type":"string","description":"Authentication type","enum":["no_auth","secret","bearer_token"],"default":"no_auth"},"secret":{"type":"string","description":"Secret for HMAC signature (authType=secret)"},"token":{"type":"string","description":"Bearer token (authType=bearer_token)"},"timeout":{"type":"number","description":"Timeout in milliseconds","default":30000},"retryCount":{"type":"number","description":"Number of retry attempts","default":3}}},"ConsensusConfig":{"type":"object","description":"Consensus configuration for multi-annotator agreement","properties":{"version":{"type":"number","description":"Consensus algorithm version (1 or 2, v2 recommended)","enum":[1,2],"default":2},"threshold":{"type":"number","description":"Minimum agreement score (0-1) required to pass","minimum":0,"maximum":1},"adjudicationMethod":{"type":"string","description":"Method for resolving disagreements","enum":["majority","union","intersection","first"],"default":"majority"},"dynamicConsensus":{"type":"boolean","description":"Add more copies if threshold not met","default":false},"minCopies":{"type":"number","description":"Minimum number of consensus copies","minimum":2,"default":2},"maxCopies":{"type":"number","description":"Maximum number of consensus copies (dynamic consensus)","default":5}}},"Assignee":{"type":"object","properties":{"assignee":{"type":"string","description":"User email"},"role":{"type":"string","enum":["Manager","Labeler","Reviewer","Lead"]}}},"Batch":{"type":"object","required":["name"],"properties":{"_id":{"type":"string","description":"Batch ID (auto-generated if not provided)"},"name":{"type":"string","description":"Batch name"}}},"PctConfig":{"type":"object","description":"Configuration specific to Point Cloud Tool (PCT) projects.\nThese settings control how 3D point cloud annotation behaves.\n\nExample:\n```json\n{\n  \"allowOverlapping\": false,\n  \"trackingMultipleSensors\": true,\n  \"segmentationMode\": false\n}\n```\n","properties":{"allowOverlapping":{"type":"boolean","description":"Allow overlapping 3D bounding boxes","default":false},"trackingMultipleSensors":{"type":"boolean","description":"Enable tracking across multiple LiDAR sensors","default":false},"segmentationMode":{"type":"boolean","description":"Enable segmentation mode for point clouds","default":false},"externalValidation":{"$ref":"#/components/schemas/ExternalValidation"},"segmentationValidation":{"$ref":"#/components/schemas/SegmentationValidation"}}},"ExternalValidation":{"type":"object","description":"Configuration for external API-based validation","properties":{"enabled":{"type":"boolean","description":"Enable external validation API calls","default":false},"apiUrl":{"type":"string","description":"URL of external validation API endpoint","default":""},"singleFrame":{"type":"boolean","description":"For video projects - validate only a single frame","default":false},"specificFrame":{"type":"boolean","description":"For video projects - validate specific frame(s)","default":false},"frameNumbers":{"type":"integer","nullable":true,"description":"Frame number to validate for video projects (string input is coerced to a number)"}}},"SegmentationValidation":{"type":"object","description":"Configuration for segmentation coverage validation","properties":{"enabled":{"type":"boolean","default":false},"completeCoverage":{"type":"number","default":100}}}}}}
````

## The ProjectCreateRequest object

````json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ProjectCreateRequest":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"description":"Project name"},"description":{"type":"string","description":"Project description"},"type":{"type":"string","description":"Project type","enum":["ango","pct","iframe"],"default":"ango"},"pctConfig":{"$ref":"#/components/schemas/PctConfig","description":"Point Cloud Tool configuration (required only when type is \"pct\")"}}},"PctConfig":{"type":"object","description":"Configuration specific to Point Cloud Tool (PCT) projects.\nThese settings control how 3D point cloud annotation behaves.\n\nExample:\n```json\n{\n  \"allowOverlapping\": false,\n  \"trackingMultipleSensors\": true,\n  \"segmentationMode\": false\n}\n```\n","properties":{"allowOverlapping":{"type":"boolean","description":"Allow overlapping 3D bounding boxes","default":false},"trackingMultipleSensors":{"type":"boolean","description":"Enable tracking across multiple LiDAR sensors","default":false},"segmentationMode":{"type":"boolean","description":"Enable segmentation mode for point clouds","default":false},"externalValidation":{"$ref":"#/components/schemas/ExternalValidation"},"segmentationValidation":{"$ref":"#/components/schemas/SegmentationValidation"}}},"ExternalValidation":{"type":"object","description":"Configuration for external API-based validation","properties":{"enabled":{"type":"boolean","description":"Enable external validation API calls","default":false},"apiUrl":{"type":"string","description":"URL of external validation API endpoint","default":""},"singleFrame":{"type":"boolean","description":"For video projects - validate only a single frame","default":false},"specificFrame":{"type":"boolean","description":"For video projects - validate specific frame(s)","default":false},"frameNumbers":{"type":"integer","nullable":true,"description":"Frame number to validate for video projects (string input is coerced to a number)"}}},"SegmentationValidation":{"type":"object","description":"Configuration for segmentation coverage validation","properties":{"enabled":{"type":"boolean","default":false},"completeCoverage":{"type":"number","default":100}}}}}}
````

## The ProjectUpdateRequest object

````json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ProjectUpdateRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1},"description":{"type":"string"},"categorySchema":{"$ref":"#/components/schemas/CategorySchema"},"stages":{"type":"array","items":{"$ref":"#/components/schemas/Stage"}},"batches":{"type":"array","items":{"$ref":"#/components/schemas/Batch"}},"benchmarkEnabled":{"type":"boolean"},"benchmarkRatio":{"type":"number"},"instructions":{"type":"string"},"ocrLanguage":{"type":"string"},"idleTimeout":{"type":"number"},"projectPlugins":{"type":"object"},"pluginPresets":{"type":"array","items":{"type":"object"}},"errorCodesEnabled":{"type":"boolean"},"errorSchema":{"type":"object"},"exportStorageId":{"type":"string"},"exportStorageBucket":{"type":"string"},"exportStoragePath":{"type":"string"},"exportStorageEnabled":{"type":"boolean"},"taskSkipEnabled":{"type":"boolean"},"unassignSkippedTask":{"type":"boolean"},"gcSyncEnabled":{"type":"boolean"},"benchmarkCutoff":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"queueCountsEnabled":{"type":"boolean"},"assetBuilderTemplates":{"type":"array","items":{"type":"object"}},"pctConfig":{"$ref":"#/components/schemas/PctConfig","description":"Point Cloud Tool configuration (only for pct project type)"},"deleted":{"type":"boolean"}}},"CategorySchema":{"type":"object","description":"Label configuration schema that defines the annotation tools, classifications, and relations for a project.\nThis schema determines what annotation types are available to users and how they can label assets.\n\n**Note:** Schema structure varies between Ango and PCT projects.\n- Ango projects: Use standard annotation tools (bounding-box, polygon, etc.)\n- PCT projects: Use PCT tool type with markingTools array and nested classifications with PCT-specific fields\n","properties":{"tools":{"type":"array","description":"Array of annotation tools (bounding box, polygon, segmentation, etc.)","items":{"$ref":"#/components/schemas/Tool"}},"classifications":{"type":"array","description":"Array of classification questions (radio, checkbox, dropdown, text, etc.)","items":{"$ref":"#/components/schemas/Classification"}},"relations":{"type":"array","description":"Array of relation types that can connect annotations","items":{"$ref":"#/components/schemas/Relation"}}}},"Tool":{"type":"object","description":"Annotation tool configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this tool"},"title":{"type":"string","description":"Display name for the tool"},"tool":{"type":"string","description":"Type of annotation tool","enum":["bounding-box","polygon","polyline","point","rotated-bounding-box","segmentation","brush","medical-brush","ner","pdf","pct","angle"]},"required":{"type":"boolean","description":"Whether this annotation is required","default":false},"color":{"type":"string","description":"Hex color code for this annotation class","pattern":"^#[0-9A-Fa-f]{6}$"},"shortcutKey":{"type":"string","description":"Keyboard shortcut for quick selection"},"classifications":{"type":"array","description":"Nested classifications specific to this tool","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow creating multiple instances of this annotation","default":false},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data (for organizing exported annotations)"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"markingTools":{"type":"array","description":"(PCT only) List of marking tools enabled for this PCT tool.\nAvailable marking tools: polygon, polygon2d, polyline, polyline2d, cuboid, rectangle\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}},"truncate":{"type":"boolean","description":"(PCT only) Enable truncation for this tool","default":false},"presetCuboidDimensions":{"type":"boolean","description":"(PCT only) Enable preset cuboid dimensions","default":false},"cuboidLength":{"type":"string","description":"(PCT only) Preset cuboid length value (stored as string)"},"cuboidWidth":{"type":"string","description":"(PCT only) Preset cuboid width value (stored as string)"},"cuboidHeight":{"type":"string","description":"(PCT only) Preset cuboid height value (stored as string)"},"keypointWithVerticalLine":{"type":"boolean","description":"(PCT only) Enable keypoint with vertical line feature","default":false},"edgeLengthConstraint":{"type":"string","description":"(PCT only) Minimum edge length constraint (stored as string)"},"hasInstance":{"type":"boolean","description":"(PCT only) Enable instance tracking for this tool","default":false}}},"Classification":{"type":"object","description":"Classification question configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this classification"},"title":{"type":"string","description":"Question or label text"},"tool":{"type":"string","description":"Type of classification input (same as 'type')","enum":["radio","checkbox","single-dropdown","multi-dropdown","tree-dropdown","single-tree-dropdown","text","boolean"]},"color":{"type":"string","description":"Hex color code for this classification","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether answering this classification is required","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut key"},"columnField":{"type":"boolean","description":"Whether to show in column view","default":false},"frameSpecific":{"type":"boolean","description":"Whether classification is frame-specific (for videos)","default":false},"showDropdown":{"type":"boolean","description":"Whether to show as dropdown in UI","default":false},"richText":{"type":"boolean","description":"Whether to enable rich text input (for text type)","default":false},"regex":{"type":"string","nullable":true,"description":"Regular expression for text validation (for text type)"},"parentOptionId":{"type":"string","nullable":true,"description":"Parent option ID for nested classifications"},"options":{"type":"array","description":"Available options (not used for text type)","items":{"$ref":"#/components/schemas/ClassificationOption"}},"treeOptions":{"type":"array","description":"Tree-structured options (for tree-dropdown types)","items":{"type":"object"}},"classifications":{"type":"array","description":"Nested child classifications","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow multiple selections","default":false},"parentToolType":{"type":"string","description":"(PCT only) Parent tool type this classification belongs to"},"parentToolId":{"type":"string","description":"(PCT only) Parent tool schemaId this classification is associated with"},"attributeLevel":{"type":"string","description":"(PCT only) Level at which this attribute applies","enum":["Class Level","Instance Level"]},"defaultValue":{"description":"Default value for this classification (type varies based on classification tool type)","oneOf":[{"type":"boolean"},{"type":"string"},{"type":"number"}]},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"attributeMode":{"type":"string","description":"(PCT only) Attribute mode determining how values persist across frames.\n- Time Varying: Value can change per frame\n- Constant: Single value for entire sequence\n- Sensor Specific: Different values per sensor\n","enum":["Time Varying","Constant","Sensor Specific"]},"associatedMarkingTool":{"type":"array","description":"(PCT only) Marking tools this classification is associated with.\nThis classification will only be available when using these marking tools.\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}}}},"ClassificationOption":{"type":"object","description":"Option for a classification question","required":["schemaId","value"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this option"},"value":{"type":"string","description":"Display text and internal value for this option"},"label":{"type":"string","description":"Alternative display text (deprecated, use 'value')"},"children":{"type":"array","description":"Nested classifications that appear when this option is selected","items":{"$ref":"#/components/schemas/Classification"}}}},"Relation":{"type":"object","description":"Relation type that can connect two annotations.\n\n**Relation Types:**\n- `one-to-one`: Direct connection between two annotations (Ango only)\n- `group`: Groups multiple annotations together (Ango only)\n- `one-to-many`: Parent-child hierarchical relationship (PCT only)\n\n**Note:** PCT projects support additional fields like `parentTool` and `childTools` for one-to-many relations.\n","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this relation"},"title":{"type":"string","description":"Display name for the relation"},"tool":{"type":"string","description":"Type of relation tool:\n- `one-to-one`: Direct 1:1 connection between annotations (Ango only)\n- `group`: Non-directional grouping of multiple annotations (Ango only)\n- `one-to-many`: Hierarchical parent-child relationship (PCT only)\n","enum":["one-to-one","group","one-to-many"]},"color":{"type":"string","description":"Hex color code for this relation","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether this relation must be used in annotations","default":false},"classifications":{"type":"array","description":"Nested classification questions attached to this relation.\nAllows adding metadata or attributes to the relationship itself.\n","default":[]},"multiple":{"type":"boolean","description":"Whether multiple instances of this relation can be created","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut to quickly create this relation"},"parentTool":{"type":"string","description":"(PCT only) For one-to-many relations, specifies the schemaId of the parent tool.\nOnly applicable when `tool: \"one-to-many\"`.\n"},"childTools":{"type":"array","description":"(PCT only) For one-to-many relations, specifies the schemaIds of allowed child tools.\nOnly applicable when `tool: \"one-to-many\"`.\n","items":{"type":"string"}}}},"Stage":{"type":"object","description":"Workflow stage configuration. Different stage types have different properties.\n- Label: Manual annotation stage\n- Review: QA/review stage\n- Logic: Conditional routing stage\n- Plugin: ML model integration stage\n- Webhook: External API callback stage\n- Consensus: Multi-annotator agreement stage\n- Hold: Pause stage\n- Start: Entry point (auto-forwards)\n- Complete: Terminal success stage\n- Archive: Terminal archive stage\n","required":["id","type"],"properties":{"id":{"type":"string","description":"Unique identifier for this stage"},"name":{"type":"string","description":"Display name for this stage"},"type":{"type":"string","description":"Type of stage","enum":["Label","Review","Logic","Plugin","Webhook","Consensus","Hold","Start","Complete","Archive"]},"next":{"type":"array","items":{"type":"string"},"description":"Array of next stage IDs (for Logic stages, order matters)"},"position":{"type":"object","description":"Position in workflow diagram UI","properties":{"x":{"type":"number"},"y":{"type":"number"}}},"assignedTo":{"type":"array","description":"Users assigned to work on this stage (Label/Review stages)","items":{"type":"string"}},"rememberAssignee":{"type":"boolean","description":"Auto-assign to previous stage's user (sticky assignment)","default":false},"logic":{"$ref":"#/components/schemas/LogicConfig","description":"Logic routing configuration (Logic stages only)"},"pluginId":{"type":"string","description":"Plugin ID to execute (Plugin stages only)"},"pluginConfig":{"type":"object","description":"Plugin-specific configuration (Plugin stages only)"},"webhook":{"$ref":"#/components/schemas/WebhookConfig","description":"Webhook configuration (Webhook stages only)"},"consensusConfig":{"$ref":"#/components/schemas/ConsensusConfig","description":"Consensus configuration (Consensus stages only)"},"readOnly":{"type":"boolean","description":"Whether this stage is read-only (users can view but not edit)","default":false},"preventRequeue":{"type":"boolean","description":"Prevent tasks from being requeued to this stage","default":false},"autoForward":{"type":"boolean","description":"Automatically advance to next stage without user action","default":true},"consensusId":{"type":"string","description":"Links stage to a consensus stage (used in consensus workflows)"},"consensusDynamic":{"type":"boolean","description":"Dynamic vs static consensus stage","default":false}}},"LogicConfig":{"type":"object","description":"Logic stage routing configuration. Routes tasks based on conditions.\nConditions are evaluated in order, first match wins.\n","properties":{"type":{"type":"string","description":"Type of logic routing","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Default value (usually empty)"},"conditions":{"type":"array","description":"Array of routing conditions","items":{"$ref":"#/components/schemas/LogicCondition"}}}},"LogicCondition":{"type":"object","description":"Individual routing condition for Logic stages","properties":{"type":{"type":"string","description":"Type of condition","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Condition-specific value","properties":{"tools":{"type":"array","description":"Tool filters (AnnotationType logic)","items":{"type":"object","properties":{"filter":{"type":"array","items":{"type":"string"},"description":"Annotation filters:\n- exists___<schemaId>: Annotation must exist\n- not-exist___<schemaId>: Annotation must NOT exist\n"}}}},"emails":{"type":"array","description":"User emails (Annotator logic)","items":{"type":"string"}},"sampleRate":{"type":"number","description":"Sample rate 0-1 (RandomSample logic)","minimum":0,"maximum":1},"duration":{"type":"number","description":"Duration in milliseconds (Duration logic)"},"operator":{"type":"string","description":"Comparison operator for Duration","enum":["lt","gt","eq"]},"batches":{"type":"array","description":"Batch names (Batch logic)","items":{"type":"string"}}}},"index":{"type":"number","description":"Index of next stage in parent's next array"}}},"WebhookConfig":{"type":"object","description":"Webhook configuration for external API integration","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"Webhook endpoint URL"},"method":{"type":"string","description":"HTTP method","enum":["POST","PUT"],"default":"POST"},"authType":{"type":"string","description":"Authentication type","enum":["no_auth","secret","bearer_token"],"default":"no_auth"},"secret":{"type":"string","description":"Secret for HMAC signature (authType=secret)"},"token":{"type":"string","description":"Bearer token (authType=bearer_token)"},"timeout":{"type":"number","description":"Timeout in milliseconds","default":30000},"retryCount":{"type":"number","description":"Number of retry attempts","default":3}}},"ConsensusConfig":{"type":"object","description":"Consensus configuration for multi-annotator agreement","properties":{"version":{"type":"number","description":"Consensus algorithm version (1 or 2, v2 recommended)","enum":[1,2],"default":2},"threshold":{"type":"number","description":"Minimum agreement score (0-1) required to pass","minimum":0,"maximum":1},"adjudicationMethod":{"type":"string","description":"Method for resolving disagreements","enum":["majority","union","intersection","first"],"default":"majority"},"dynamicConsensus":{"type":"boolean","description":"Add more copies if threshold not met","default":false},"minCopies":{"type":"number","description":"Minimum number of consensus copies","minimum":2,"default":2},"maxCopies":{"type":"number","description":"Maximum number of consensus copies (dynamic consensus)","default":5}}},"Batch":{"type":"object","required":["name"],"properties":{"_id":{"type":"string","description":"Batch ID (auto-generated if not provided)"},"name":{"type":"string","description":"Batch name"}}},"PctConfig":{"type":"object","description":"Configuration specific to Point Cloud Tool (PCT) projects.\nThese settings control how 3D point cloud annotation behaves.\n\nExample:\n```json\n{\n  \"allowOverlapping\": false,\n  \"trackingMultipleSensors\": true,\n  \"segmentationMode\": false\n}\n```\n","properties":{"allowOverlapping":{"type":"boolean","description":"Allow overlapping 3D bounding boxes","default":false},"trackingMultipleSensors":{"type":"boolean","description":"Enable tracking across multiple LiDAR sensors","default":false},"segmentationMode":{"type":"boolean","description":"Enable segmentation mode for point clouds","default":false},"externalValidation":{"$ref":"#/components/schemas/ExternalValidation"},"segmentationValidation":{"$ref":"#/components/schemas/SegmentationValidation"}}},"ExternalValidation":{"type":"object","description":"Configuration for external API-based validation","properties":{"enabled":{"type":"boolean","description":"Enable external validation API calls","default":false},"apiUrl":{"type":"string","description":"URL of external validation API endpoint","default":""},"singleFrame":{"type":"boolean","description":"For video projects - validate only a single frame","default":false},"specificFrame":{"type":"boolean","description":"For video projects - validate specific frame(s)","default":false},"frameNumbers":{"type":"integer","nullable":true,"description":"Frame number to validate for video projects (string input is coerced to a number)"}}},"SegmentationValidation":{"type":"object","description":"Configuration for segmentation coverage validation","properties":{"enabled":{"type":"boolean","default":false},"completeCoverage":{"type":"number","default":100}}}}}}
````

## The ProjectListResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ProjectListResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"projects":{"type":"array","items":{"$ref":"#/components/schemas/ProjectListItem"}},"total":{"type":"integer"}}}}},"ProjectListItem":{"type":"object","properties":{"_id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"organization":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}}}}}
```

## The ProjectResponse object

````json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ProjectResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"project":{"$ref":"#/components/schemas/Project"}}}}},"Project":{"type":"object","properties":{"_id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"type":{"type":"string","enum":["ango","pct","iframe"],"default":"ango"},"owner":{"type":"string","description":"Owner email"},"organization":{"type":"string","description":"Organization ID"},"categorySchema":{"$ref":"#/components/schemas/CategorySchema"},"stages":{"type":"array","items":{"$ref":"#/components/schemas/Stage"}},"assignedTo":{"type":"array","items":{"$ref":"#/components/schemas/Assignee"}},"batches":{"type":"array","items":{"$ref":"#/components/schemas/Batch"}},"queueVersion":{"type":"number"},"assetCount":{"type":"integer"},"benchmarkEnabled":{"type":"boolean"},"benchmarkRatio":{"type":"number"},"instructions":{"type":"string","description":"Instructions URL"},"ocrLanguage":{"type":"string"},"idleTimeout":{"type":"number","description":"Idle timeout in seconds"},"projectPlugins":{"type":"object"},"pluginPresets":{"type":"array","items":{"type":"object"}},"errorCodesEnabled":{"type":"boolean"},"errorSchema":{"type":"object"},"exportStorageId":{"type":"string"},"exportStorageBucket":{"type":"string"},"exportStoragePath":{"type":"string"},"exportStorageEnabled":{"type":"boolean"},"taskSkipEnabled":{"type":"boolean"},"unassignSkippedTask":{"type":"boolean"},"gcSyncEnabled":{"type":"boolean"},"pctConfig":{"$ref":"#/components/schemas/PctConfig"},"role":{"type":"string","description":"Caller's role on this project (present on GET /project/{projectId} only)","enum":["Manager","Lead","Labeler","Reviewer"]},"deleted":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CategorySchema":{"type":"object","description":"Label configuration schema that defines the annotation tools, classifications, and relations for a project.\nThis schema determines what annotation types are available to users and how they can label assets.\n\n**Note:** Schema structure varies between Ango and PCT projects.\n- Ango projects: Use standard annotation tools (bounding-box, polygon, etc.)\n- PCT projects: Use PCT tool type with markingTools array and nested classifications with PCT-specific fields\n","properties":{"tools":{"type":"array","description":"Array of annotation tools (bounding box, polygon, segmentation, etc.)","items":{"$ref":"#/components/schemas/Tool"}},"classifications":{"type":"array","description":"Array of classification questions (radio, checkbox, dropdown, text, etc.)","items":{"$ref":"#/components/schemas/Classification"}},"relations":{"type":"array","description":"Array of relation types that can connect annotations","items":{"$ref":"#/components/schemas/Relation"}}}},"Tool":{"type":"object","description":"Annotation tool configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this tool"},"title":{"type":"string","description":"Display name for the tool"},"tool":{"type":"string","description":"Type of annotation tool","enum":["bounding-box","polygon","polyline","point","rotated-bounding-box","segmentation","brush","medical-brush","ner","pdf","pct","angle"]},"required":{"type":"boolean","description":"Whether this annotation is required","default":false},"color":{"type":"string","description":"Hex color code for this annotation class","pattern":"^#[0-9A-Fa-f]{6}$"},"shortcutKey":{"type":"string","description":"Keyboard shortcut for quick selection"},"classifications":{"type":"array","description":"Nested classifications specific to this tool","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow creating multiple instances of this annotation","default":false},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data (for organizing exported annotations)"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"markingTools":{"type":"array","description":"(PCT only) List of marking tools enabled for this PCT tool.\nAvailable marking tools: polygon, polygon2d, polyline, polyline2d, cuboid, rectangle\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}},"truncate":{"type":"boolean","description":"(PCT only) Enable truncation for this tool","default":false},"presetCuboidDimensions":{"type":"boolean","description":"(PCT only) Enable preset cuboid dimensions","default":false},"cuboidLength":{"type":"string","description":"(PCT only) Preset cuboid length value (stored as string)"},"cuboidWidth":{"type":"string","description":"(PCT only) Preset cuboid width value (stored as string)"},"cuboidHeight":{"type":"string","description":"(PCT only) Preset cuboid height value (stored as string)"},"keypointWithVerticalLine":{"type":"boolean","description":"(PCT only) Enable keypoint with vertical line feature","default":false},"edgeLengthConstraint":{"type":"string","description":"(PCT only) Minimum edge length constraint (stored as string)"},"hasInstance":{"type":"boolean","description":"(PCT only) Enable instance tracking for this tool","default":false}}},"Classification":{"type":"object","description":"Classification question configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this classification"},"title":{"type":"string","description":"Question or label text"},"tool":{"type":"string","description":"Type of classification input (same as 'type')","enum":["radio","checkbox","single-dropdown","multi-dropdown","tree-dropdown","single-tree-dropdown","text","boolean"]},"color":{"type":"string","description":"Hex color code for this classification","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether answering this classification is required","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut key"},"columnField":{"type":"boolean","description":"Whether to show in column view","default":false},"frameSpecific":{"type":"boolean","description":"Whether classification is frame-specific (for videos)","default":false},"showDropdown":{"type":"boolean","description":"Whether to show as dropdown in UI","default":false},"richText":{"type":"boolean","description":"Whether to enable rich text input (for text type)","default":false},"regex":{"type":"string","nullable":true,"description":"Regular expression for text validation (for text type)"},"parentOptionId":{"type":"string","nullable":true,"description":"Parent option ID for nested classifications"},"options":{"type":"array","description":"Available options (not used for text type)","items":{"$ref":"#/components/schemas/ClassificationOption"}},"treeOptions":{"type":"array","description":"Tree-structured options (for tree-dropdown types)","items":{"type":"object"}},"classifications":{"type":"array","description":"Nested child classifications","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow multiple selections","default":false},"parentToolType":{"type":"string","description":"(PCT only) Parent tool type this classification belongs to"},"parentToolId":{"type":"string","description":"(PCT only) Parent tool schemaId this classification is associated with"},"attributeLevel":{"type":"string","description":"(PCT only) Level at which this attribute applies","enum":["Class Level","Instance Level"]},"defaultValue":{"description":"Default value for this classification (type varies based on classification tool type)","oneOf":[{"type":"boolean"},{"type":"string"},{"type":"number"}]},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"attributeMode":{"type":"string","description":"(PCT only) Attribute mode determining how values persist across frames.\n- Time Varying: Value can change per frame\n- Constant: Single value for entire sequence\n- Sensor Specific: Different values per sensor\n","enum":["Time Varying","Constant","Sensor Specific"]},"associatedMarkingTool":{"type":"array","description":"(PCT only) Marking tools this classification is associated with.\nThis classification will only be available when using these marking tools.\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}}}},"ClassificationOption":{"type":"object","description":"Option for a classification question","required":["schemaId","value"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this option"},"value":{"type":"string","description":"Display text and internal value for this option"},"label":{"type":"string","description":"Alternative display text (deprecated, use 'value')"},"children":{"type":"array","description":"Nested classifications that appear when this option is selected","items":{"$ref":"#/components/schemas/Classification"}}}},"Relation":{"type":"object","description":"Relation type that can connect two annotations.\n\n**Relation Types:**\n- `one-to-one`: Direct connection between two annotations (Ango only)\n- `group`: Groups multiple annotations together (Ango only)\n- `one-to-many`: Parent-child hierarchical relationship (PCT only)\n\n**Note:** PCT projects support additional fields like `parentTool` and `childTools` for one-to-many relations.\n","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this relation"},"title":{"type":"string","description":"Display name for the relation"},"tool":{"type":"string","description":"Type of relation tool:\n- `one-to-one`: Direct 1:1 connection between annotations (Ango only)\n- `group`: Non-directional grouping of multiple annotations (Ango only)\n- `one-to-many`: Hierarchical parent-child relationship (PCT only)\n","enum":["one-to-one","group","one-to-many"]},"color":{"type":"string","description":"Hex color code for this relation","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether this relation must be used in annotations","default":false},"classifications":{"type":"array","description":"Nested classification questions attached to this relation.\nAllows adding metadata or attributes to the relationship itself.\n","default":[]},"multiple":{"type":"boolean","description":"Whether multiple instances of this relation can be created","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut to quickly create this relation"},"parentTool":{"type":"string","description":"(PCT only) For one-to-many relations, specifies the schemaId of the parent tool.\nOnly applicable when `tool: \"one-to-many\"`.\n"},"childTools":{"type":"array","description":"(PCT only) For one-to-many relations, specifies the schemaIds of allowed child tools.\nOnly applicable when `tool: \"one-to-many\"`.\n","items":{"type":"string"}}}},"Stage":{"type":"object","description":"Workflow stage configuration. Different stage types have different properties.\n- Label: Manual annotation stage\n- Review: QA/review stage\n- Logic: Conditional routing stage\n- Plugin: ML model integration stage\n- Webhook: External API callback stage\n- Consensus: Multi-annotator agreement stage\n- Hold: Pause stage\n- Start: Entry point (auto-forwards)\n- Complete: Terminal success stage\n- Archive: Terminal archive stage\n","required":["id","type"],"properties":{"id":{"type":"string","description":"Unique identifier for this stage"},"name":{"type":"string","description":"Display name for this stage"},"type":{"type":"string","description":"Type of stage","enum":["Label","Review","Logic","Plugin","Webhook","Consensus","Hold","Start","Complete","Archive"]},"next":{"type":"array","items":{"type":"string"},"description":"Array of next stage IDs (for Logic stages, order matters)"},"position":{"type":"object","description":"Position in workflow diagram UI","properties":{"x":{"type":"number"},"y":{"type":"number"}}},"assignedTo":{"type":"array","description":"Users assigned to work on this stage (Label/Review stages)","items":{"type":"string"}},"rememberAssignee":{"type":"boolean","description":"Auto-assign to previous stage's user (sticky assignment)","default":false},"logic":{"$ref":"#/components/schemas/LogicConfig","description":"Logic routing configuration (Logic stages only)"},"pluginId":{"type":"string","description":"Plugin ID to execute (Plugin stages only)"},"pluginConfig":{"type":"object","description":"Plugin-specific configuration (Plugin stages only)"},"webhook":{"$ref":"#/components/schemas/WebhookConfig","description":"Webhook configuration (Webhook stages only)"},"consensusConfig":{"$ref":"#/components/schemas/ConsensusConfig","description":"Consensus configuration (Consensus stages only)"},"readOnly":{"type":"boolean","description":"Whether this stage is read-only (users can view but not edit)","default":false},"preventRequeue":{"type":"boolean","description":"Prevent tasks from being requeued to this stage","default":false},"autoForward":{"type":"boolean","description":"Automatically advance to next stage without user action","default":true},"consensusId":{"type":"string","description":"Links stage to a consensus stage (used in consensus workflows)"},"consensusDynamic":{"type":"boolean","description":"Dynamic vs static consensus stage","default":false}}},"LogicConfig":{"type":"object","description":"Logic stage routing configuration. Routes tasks based on conditions.\nConditions are evaluated in order, first match wins.\n","properties":{"type":{"type":"string","description":"Type of logic routing","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Default value (usually empty)"},"conditions":{"type":"array","description":"Array of routing conditions","items":{"$ref":"#/components/schemas/LogicCondition"}}}},"LogicCondition":{"type":"object","description":"Individual routing condition for Logic stages","properties":{"type":{"type":"string","description":"Type of condition","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Condition-specific value","properties":{"tools":{"type":"array","description":"Tool filters (AnnotationType logic)","items":{"type":"object","properties":{"filter":{"type":"array","items":{"type":"string"},"description":"Annotation filters:\n- exists___<schemaId>: Annotation must exist\n- not-exist___<schemaId>: Annotation must NOT exist\n"}}}},"emails":{"type":"array","description":"User emails (Annotator logic)","items":{"type":"string"}},"sampleRate":{"type":"number","description":"Sample rate 0-1 (RandomSample logic)","minimum":0,"maximum":1},"duration":{"type":"number","description":"Duration in milliseconds (Duration logic)"},"operator":{"type":"string","description":"Comparison operator for Duration","enum":["lt","gt","eq"]},"batches":{"type":"array","description":"Batch names (Batch logic)","items":{"type":"string"}}}},"index":{"type":"number","description":"Index of next stage in parent's next array"}}},"WebhookConfig":{"type":"object","description":"Webhook configuration for external API integration","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"Webhook endpoint URL"},"method":{"type":"string","description":"HTTP method","enum":["POST","PUT"],"default":"POST"},"authType":{"type":"string","description":"Authentication type","enum":["no_auth","secret","bearer_token"],"default":"no_auth"},"secret":{"type":"string","description":"Secret for HMAC signature (authType=secret)"},"token":{"type":"string","description":"Bearer token (authType=bearer_token)"},"timeout":{"type":"number","description":"Timeout in milliseconds","default":30000},"retryCount":{"type":"number","description":"Number of retry attempts","default":3}}},"ConsensusConfig":{"type":"object","description":"Consensus configuration for multi-annotator agreement","properties":{"version":{"type":"number","description":"Consensus algorithm version (1 or 2, v2 recommended)","enum":[1,2],"default":2},"threshold":{"type":"number","description":"Minimum agreement score (0-1) required to pass","minimum":0,"maximum":1},"adjudicationMethod":{"type":"string","description":"Method for resolving disagreements","enum":["majority","union","intersection","first"],"default":"majority"},"dynamicConsensus":{"type":"boolean","description":"Add more copies if threshold not met","default":false},"minCopies":{"type":"number","description":"Minimum number of consensus copies","minimum":2,"default":2},"maxCopies":{"type":"number","description":"Maximum number of consensus copies (dynamic consensus)","default":5}}},"Assignee":{"type":"object","properties":{"assignee":{"type":"string","description":"User email"},"role":{"type":"string","enum":["Manager","Labeler","Reviewer","Lead"]}}},"Batch":{"type":"object","required":["name"],"properties":{"_id":{"type":"string","description":"Batch ID (auto-generated if not provided)"},"name":{"type":"string","description":"Batch name"}}},"PctConfig":{"type":"object","description":"Configuration specific to Point Cloud Tool (PCT) projects.\nThese settings control how 3D point cloud annotation behaves.\n\nExample:\n```json\n{\n  \"allowOverlapping\": false,\n  \"trackingMultipleSensors\": true,\n  \"segmentationMode\": false\n}\n```\n","properties":{"allowOverlapping":{"type":"boolean","description":"Allow overlapping 3D bounding boxes","default":false},"trackingMultipleSensors":{"type":"boolean","description":"Enable tracking across multiple LiDAR sensors","default":false},"segmentationMode":{"type":"boolean","description":"Enable segmentation mode for point clouds","default":false},"externalValidation":{"$ref":"#/components/schemas/ExternalValidation"},"segmentationValidation":{"$ref":"#/components/schemas/SegmentationValidation"}}},"ExternalValidation":{"type":"object","description":"Configuration for external API-based validation","properties":{"enabled":{"type":"boolean","description":"Enable external validation API calls","default":false},"apiUrl":{"type":"string","description":"URL of external validation API endpoint","default":""},"singleFrame":{"type":"boolean","description":"For video projects - validate only a single frame","default":false},"specificFrame":{"type":"boolean","description":"For video projects - validate specific frame(s)","default":false},"frameNumbers":{"type":"integer","nullable":true,"description":"Frame number to validate for video projects (string input is coerced to a number)"}}},"SegmentationValidation":{"type":"object","description":"Configuration for segmentation coverage validation","properties":{"enabled":{"type":"boolean","default":false},"completeCoverage":{"type":"number","default":100}}}}}}
````

## The WorkflowUpdateRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"WorkflowUpdateRequest":{"type":"object","description":"Request body for updating project workflow stages","required":["stages"],"properties":{"stages":{"type":"array","description":"Complete array of workflow stages for the project","items":{"$ref":"#/components/schemas/Stage"},"minItems":3}}},"Stage":{"type":"object","description":"Workflow stage configuration. Different stage types have different properties.\n- Label: Manual annotation stage\n- Review: QA/review stage\n- Logic: Conditional routing stage\n- Plugin: ML model integration stage\n- Webhook: External API callback stage\n- Consensus: Multi-annotator agreement stage\n- Hold: Pause stage\n- Start: Entry point (auto-forwards)\n- Complete: Terminal success stage\n- Archive: Terminal archive stage\n","required":["id","type"],"properties":{"id":{"type":"string","description":"Unique identifier for this stage"},"name":{"type":"string","description":"Display name for this stage"},"type":{"type":"string","description":"Type of stage","enum":["Label","Review","Logic","Plugin","Webhook","Consensus","Hold","Start","Complete","Archive"]},"next":{"type":"array","items":{"type":"string"},"description":"Array of next stage IDs (for Logic stages, order matters)"},"position":{"type":"object","description":"Position in workflow diagram UI","properties":{"x":{"type":"number"},"y":{"type":"number"}}},"assignedTo":{"type":"array","description":"Users assigned to work on this stage (Label/Review stages)","items":{"type":"string"}},"rememberAssignee":{"type":"boolean","description":"Auto-assign to previous stage's user (sticky assignment)","default":false},"logic":{"$ref":"#/components/schemas/LogicConfig","description":"Logic routing configuration (Logic stages only)"},"pluginId":{"type":"string","description":"Plugin ID to execute (Plugin stages only)"},"pluginConfig":{"type":"object","description":"Plugin-specific configuration (Plugin stages only)"},"webhook":{"$ref":"#/components/schemas/WebhookConfig","description":"Webhook configuration (Webhook stages only)"},"consensusConfig":{"$ref":"#/components/schemas/ConsensusConfig","description":"Consensus configuration (Consensus stages only)"},"readOnly":{"type":"boolean","description":"Whether this stage is read-only (users can view but not edit)","default":false},"preventRequeue":{"type":"boolean","description":"Prevent tasks from being requeued to this stage","default":false},"autoForward":{"type":"boolean","description":"Automatically advance to next stage without user action","default":true},"consensusId":{"type":"string","description":"Links stage to a consensus stage (used in consensus workflows)"},"consensusDynamic":{"type":"boolean","description":"Dynamic vs static consensus stage","default":false}}},"LogicConfig":{"type":"object","description":"Logic stage routing configuration. Routes tasks based on conditions.\nConditions are evaluated in order, first match wins.\n","properties":{"type":{"type":"string","description":"Type of logic routing","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Default value (usually empty)"},"conditions":{"type":"array","description":"Array of routing conditions","items":{"$ref":"#/components/schemas/LogicCondition"}}}},"LogicCondition":{"type":"object","description":"Individual routing condition for Logic stages","properties":{"type":{"type":"string","description":"Type of condition","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Condition-specific value","properties":{"tools":{"type":"array","description":"Tool filters (AnnotationType logic)","items":{"type":"object","properties":{"filter":{"type":"array","items":{"type":"string"},"description":"Annotation filters:\n- exists___<schemaId>: Annotation must exist\n- not-exist___<schemaId>: Annotation must NOT exist\n"}}}},"emails":{"type":"array","description":"User emails (Annotator logic)","items":{"type":"string"}},"sampleRate":{"type":"number","description":"Sample rate 0-1 (RandomSample logic)","minimum":0,"maximum":1},"duration":{"type":"number","description":"Duration in milliseconds (Duration logic)"},"operator":{"type":"string","description":"Comparison operator for Duration","enum":["lt","gt","eq"]},"batches":{"type":"array","description":"Batch names (Batch logic)","items":{"type":"string"}}}},"index":{"type":"number","description":"Index of next stage in parent's next array"}}},"WebhookConfig":{"type":"object","description":"Webhook configuration for external API integration","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"Webhook endpoint URL"},"method":{"type":"string","description":"HTTP method","enum":["POST","PUT"],"default":"POST"},"authType":{"type":"string","description":"Authentication type","enum":["no_auth","secret","bearer_token"],"default":"no_auth"},"secret":{"type":"string","description":"Secret for HMAC signature (authType=secret)"},"token":{"type":"string","description":"Bearer token (authType=bearer_token)"},"timeout":{"type":"number","description":"Timeout in milliseconds","default":30000},"retryCount":{"type":"number","description":"Number of retry attempts","default":3}}},"ConsensusConfig":{"type":"object","description":"Consensus configuration for multi-annotator agreement","properties":{"version":{"type":"number","description":"Consensus algorithm version (1 or 2, v2 recommended)","enum":[1,2],"default":2},"threshold":{"type":"number","description":"Minimum agreement score (0-1) required to pass","minimum":0,"maximum":1},"adjudicationMethod":{"type":"string","description":"Method for resolving disagreements","enum":["majority","union","intersection","first"],"default":"majority"},"dynamicConsensus":{"type":"boolean","description":"Add more copies if threshold not met","default":false},"minCopies":{"type":"number","description":"Minimum number of consensus copies","minimum":2,"default":2},"maxCopies":{"type":"number","description":"Maximum number of consensus copies (dynamic consensus)","default":5}}}}}}
```

## The CategorySchemaUpdateRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"CategorySchemaUpdateRequest":{"type":"object","description":"Request body for updating project category schema (annotation tools and classifications)","required":["categorySchema"],"properties":{"categorySchema":{"$ref":"#/components/schemas/CategorySchema"}}},"CategorySchema":{"type":"object","description":"Label configuration schema that defines the annotation tools, classifications, and relations for a project.\nThis schema determines what annotation types are available to users and how they can label assets.\n\n**Note:** Schema structure varies between Ango and PCT projects.\n- Ango projects: Use standard annotation tools (bounding-box, polygon, etc.)\n- PCT projects: Use PCT tool type with markingTools array and nested classifications with PCT-specific fields\n","properties":{"tools":{"type":"array","description":"Array of annotation tools (bounding box, polygon, segmentation, etc.)","items":{"$ref":"#/components/schemas/Tool"}},"classifications":{"type":"array","description":"Array of classification questions (radio, checkbox, dropdown, text, etc.)","items":{"$ref":"#/components/schemas/Classification"}},"relations":{"type":"array","description":"Array of relation types that can connect annotations","items":{"$ref":"#/components/schemas/Relation"}}}},"Tool":{"type":"object","description":"Annotation tool configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this tool"},"title":{"type":"string","description":"Display name for the tool"},"tool":{"type":"string","description":"Type of annotation tool","enum":["bounding-box","polygon","polyline","point","rotated-bounding-box","segmentation","brush","medical-brush","ner","pdf","pct","angle"]},"required":{"type":"boolean","description":"Whether this annotation is required","default":false},"color":{"type":"string","description":"Hex color code for this annotation class","pattern":"^#[0-9A-Fa-f]{6}$"},"shortcutKey":{"type":"string","description":"Keyboard shortcut for quick selection"},"classifications":{"type":"array","description":"Nested classifications specific to this tool","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow creating multiple instances of this annotation","default":false},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data (for organizing exported annotations)"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"markingTools":{"type":"array","description":"(PCT only) List of marking tools enabled for this PCT tool.\nAvailable marking tools: polygon, polygon2d, polyline, polyline2d, cuboid, rectangle\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}},"truncate":{"type":"boolean","description":"(PCT only) Enable truncation for this tool","default":false},"presetCuboidDimensions":{"type":"boolean","description":"(PCT only) Enable preset cuboid dimensions","default":false},"cuboidLength":{"type":"string","description":"(PCT only) Preset cuboid length value (stored as string)"},"cuboidWidth":{"type":"string","description":"(PCT only) Preset cuboid width value (stored as string)"},"cuboidHeight":{"type":"string","description":"(PCT only) Preset cuboid height value (stored as string)"},"keypointWithVerticalLine":{"type":"boolean","description":"(PCT only) Enable keypoint with vertical line feature","default":false},"edgeLengthConstraint":{"type":"string","description":"(PCT only) Minimum edge length constraint (stored as string)"},"hasInstance":{"type":"boolean","description":"(PCT only) Enable instance tracking for this tool","default":false}}},"Classification":{"type":"object","description":"Classification question configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this classification"},"title":{"type":"string","description":"Question or label text"},"tool":{"type":"string","description":"Type of classification input (same as 'type')","enum":["radio","checkbox","single-dropdown","multi-dropdown","tree-dropdown","single-tree-dropdown","text","boolean"]},"color":{"type":"string","description":"Hex color code for this classification","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether answering this classification is required","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut key"},"columnField":{"type":"boolean","description":"Whether to show in column view","default":false},"frameSpecific":{"type":"boolean","description":"Whether classification is frame-specific (for videos)","default":false},"showDropdown":{"type":"boolean","description":"Whether to show as dropdown in UI","default":false},"richText":{"type":"boolean","description":"Whether to enable rich text input (for text type)","default":false},"regex":{"type":"string","nullable":true,"description":"Regular expression for text validation (for text type)"},"parentOptionId":{"type":"string","nullable":true,"description":"Parent option ID for nested classifications"},"options":{"type":"array","description":"Available options (not used for text type)","items":{"$ref":"#/components/schemas/ClassificationOption"}},"treeOptions":{"type":"array","description":"Tree-structured options (for tree-dropdown types)","items":{"type":"object"}},"classifications":{"type":"array","description":"Nested child classifications","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow multiple selections","default":false},"parentToolType":{"type":"string","description":"(PCT only) Parent tool type this classification belongs to"},"parentToolId":{"type":"string","description":"(PCT only) Parent tool schemaId this classification is associated with"},"attributeLevel":{"type":"string","description":"(PCT only) Level at which this attribute applies","enum":["Class Level","Instance Level"]},"defaultValue":{"description":"Default value for this classification (type varies based on classification tool type)","oneOf":[{"type":"boolean"},{"type":"string"},{"type":"number"}]},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"attributeMode":{"type":"string","description":"(PCT only) Attribute mode determining how values persist across frames.\n- Time Varying: Value can change per frame\n- Constant: Single value for entire sequence\n- Sensor Specific: Different values per sensor\n","enum":["Time Varying","Constant","Sensor Specific"]},"associatedMarkingTool":{"type":"array","description":"(PCT only) Marking tools this classification is associated with.\nThis classification will only be available when using these marking tools.\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}}}},"ClassificationOption":{"type":"object","description":"Option for a classification question","required":["schemaId","value"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this option"},"value":{"type":"string","description":"Display text and internal value for this option"},"label":{"type":"string","description":"Alternative display text (deprecated, use 'value')"},"children":{"type":"array","description":"Nested classifications that appear when this option is selected","items":{"$ref":"#/components/schemas/Classification"}}}},"Relation":{"type":"object","description":"Relation type that can connect two annotations.\n\n**Relation Types:**\n- `one-to-one`: Direct connection between two annotations (Ango only)\n- `group`: Groups multiple annotations together (Ango only)\n- `one-to-many`: Parent-child hierarchical relationship (PCT only)\n\n**Note:** PCT projects support additional fields like `parentTool` and `childTools` for one-to-many relations.\n","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this relation"},"title":{"type":"string","description":"Display name for the relation"},"tool":{"type":"string","description":"Type of relation tool:\n- `one-to-one`: Direct 1:1 connection between annotations (Ango only)\n- `group`: Non-directional grouping of multiple annotations (Ango only)\n- `one-to-many`: Hierarchical parent-child relationship (PCT only)\n","enum":["one-to-one","group","one-to-many"]},"color":{"type":"string","description":"Hex color code for this relation","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether this relation must be used in annotations","default":false},"classifications":{"type":"array","description":"Nested classification questions attached to this relation.\nAllows adding metadata or attributes to the relationship itself.\n","default":[]},"multiple":{"type":"boolean","description":"Whether multiple instances of this relation can be created","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut to quickly create this relation"},"parentTool":{"type":"string","description":"(PCT only) For one-to-many relations, specifies the schemaId of the parent tool.\nOnly applicable when `tool: \"one-to-many\"`.\n"},"childTools":{"type":"array","description":"(PCT only) For one-to-many relations, specifies the schemaIds of allowed child tools.\nOnly applicable when `tool: \"one-to-many\"`.\n","items":{"type":"string"}}}}}}}
```

## The LabelValidationUpdateRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"LabelValidationUpdateRequest":{"type":"object","description":"Request body for updating project label validation configuration.\nSupports custom validation functions (JavaScript for Ango, Python for PCT) and external API validation.\n","properties":{"validationFunction":{"type":"string","description":"Custom validation function as a string.\n- For Ango projects: JavaScript function that receives `answers` object\n- For PCT projects: Python function that receives `answers` dict\n- Must return array/list of error objects with: objectId, message, preventSubmission\n"},"customValidationEnabled":{"type":"boolean","description":"Enable/disable custom validation function execution"},"submissionPrevent":{"type":"boolean","description":"Globally prevent task submission when validation fails (optional)"},"externalValidation":{"$ref":"#/components/schemas/ExternalValidation"},"segmentationValidation":{"$ref":"#/components/schemas/SegmentationValidation"}}},"ExternalValidation":{"type":"object","description":"Configuration for external API-based validation","properties":{"enabled":{"type":"boolean","description":"Enable external validation API calls","default":false},"apiUrl":{"type":"string","description":"URL of external validation API endpoint","default":""},"singleFrame":{"type":"boolean","description":"For video projects - validate only a single frame","default":false},"specificFrame":{"type":"boolean","description":"For video projects - validate specific frame(s)","default":false},"frameNumbers":{"type":"integer","nullable":true,"description":"Frame number to validate for video projects (string input is coerced to a number)"}}},"SegmentationValidation":{"type":"object","description":"Configuration for segmentation coverage validation","properties":{"enabled":{"type":"boolean","default":false},"completeCoverage":{"type":"number","default":100}}}}}}
```

## The ExternalValidation object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ExternalValidation":{"type":"object","description":"Configuration for external API-based validation","properties":{"enabled":{"type":"boolean","description":"Enable external validation API calls","default":false},"apiUrl":{"type":"string","description":"URL of external validation API endpoint","default":""},"singleFrame":{"type":"boolean","description":"For video projects - validate only a single frame","default":false},"specificFrame":{"type":"boolean","description":"For video projects - validate specific frame(s)","default":false},"frameNumbers":{"type":"integer","nullable":true,"description":"Frame number to validate for video projects (string input is coerced to a number)"}}}}}}
```

## The SegmentationValidation object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"SegmentationValidation":{"type":"object","description":"Configuration for segmentation coverage validation","properties":{"enabled":{"type":"boolean","default":false},"completeCoverage":{"type":"number","default":100}}}}}}
```

## The CategorySchema object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"CategorySchema":{"type":"object","description":"Label configuration schema that defines the annotation tools, classifications, and relations for a project.\nThis schema determines what annotation types are available to users and how they can label assets.\n\n**Note:** Schema structure varies between Ango and PCT projects.\n- Ango projects: Use standard annotation tools (bounding-box, polygon, etc.)\n- PCT projects: Use PCT tool type with markingTools array and nested classifications with PCT-specific fields\n","properties":{"tools":{"type":"array","description":"Array of annotation tools (bounding box, polygon, segmentation, etc.)","items":{"$ref":"#/components/schemas/Tool"}},"classifications":{"type":"array","description":"Array of classification questions (radio, checkbox, dropdown, text, etc.)","items":{"$ref":"#/components/schemas/Classification"}},"relations":{"type":"array","description":"Array of relation types that can connect annotations","items":{"$ref":"#/components/schemas/Relation"}}}},"Tool":{"type":"object","description":"Annotation tool configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this tool"},"title":{"type":"string","description":"Display name for the tool"},"tool":{"type":"string","description":"Type of annotation tool","enum":["bounding-box","polygon","polyline","point","rotated-bounding-box","segmentation","brush","medical-brush","ner","pdf","pct","angle"]},"required":{"type":"boolean","description":"Whether this annotation is required","default":false},"color":{"type":"string","description":"Hex color code for this annotation class","pattern":"^#[0-9A-Fa-f]{6}$"},"shortcutKey":{"type":"string","description":"Keyboard shortcut for quick selection"},"classifications":{"type":"array","description":"Nested classifications specific to this tool","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow creating multiple instances of this annotation","default":false},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data (for organizing exported annotations)"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"markingTools":{"type":"array","description":"(PCT only) List of marking tools enabled for this PCT tool.\nAvailable marking tools: polygon, polygon2d, polyline, polyline2d, cuboid, rectangle\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}},"truncate":{"type":"boolean","description":"(PCT only) Enable truncation for this tool","default":false},"presetCuboidDimensions":{"type":"boolean","description":"(PCT only) Enable preset cuboid dimensions","default":false},"cuboidLength":{"type":"string","description":"(PCT only) Preset cuboid length value (stored as string)"},"cuboidWidth":{"type":"string","description":"(PCT only) Preset cuboid width value (stored as string)"},"cuboidHeight":{"type":"string","description":"(PCT only) Preset cuboid height value (stored as string)"},"keypointWithVerticalLine":{"type":"boolean","description":"(PCT only) Enable keypoint with vertical line feature","default":false},"edgeLengthConstraint":{"type":"string","description":"(PCT only) Minimum edge length constraint (stored as string)"},"hasInstance":{"type":"boolean","description":"(PCT only) Enable instance tracking for this tool","default":false}}},"Classification":{"type":"object","description":"Classification question configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this classification"},"title":{"type":"string","description":"Question or label text"},"tool":{"type":"string","description":"Type of classification input (same as 'type')","enum":["radio","checkbox","single-dropdown","multi-dropdown","tree-dropdown","single-tree-dropdown","text","boolean"]},"color":{"type":"string","description":"Hex color code for this classification","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether answering this classification is required","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut key"},"columnField":{"type":"boolean","description":"Whether to show in column view","default":false},"frameSpecific":{"type":"boolean","description":"Whether classification is frame-specific (for videos)","default":false},"showDropdown":{"type":"boolean","description":"Whether to show as dropdown in UI","default":false},"richText":{"type":"boolean","description":"Whether to enable rich text input (for text type)","default":false},"regex":{"type":"string","nullable":true,"description":"Regular expression for text validation (for text type)"},"parentOptionId":{"type":"string","nullable":true,"description":"Parent option ID for nested classifications"},"options":{"type":"array","description":"Available options (not used for text type)","items":{"$ref":"#/components/schemas/ClassificationOption"}},"treeOptions":{"type":"array","description":"Tree-structured options (for tree-dropdown types)","items":{"type":"object"}},"classifications":{"type":"array","description":"Nested child classifications","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow multiple selections","default":false},"parentToolType":{"type":"string","description":"(PCT only) Parent tool type this classification belongs to"},"parentToolId":{"type":"string","description":"(PCT only) Parent tool schemaId this classification is associated with"},"attributeLevel":{"type":"string","description":"(PCT only) Level at which this attribute applies","enum":["Class Level","Instance Level"]},"defaultValue":{"description":"Default value for this classification (type varies based on classification tool type)","oneOf":[{"type":"boolean"},{"type":"string"},{"type":"number"}]},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"attributeMode":{"type":"string","description":"(PCT only) Attribute mode determining how values persist across frames.\n- Time Varying: Value can change per frame\n- Constant: Single value for entire sequence\n- Sensor Specific: Different values per sensor\n","enum":["Time Varying","Constant","Sensor Specific"]},"associatedMarkingTool":{"type":"array","description":"(PCT only) Marking tools this classification is associated with.\nThis classification will only be available when using these marking tools.\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}}}},"ClassificationOption":{"type":"object","description":"Option for a classification question","required":["schemaId","value"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this option"},"value":{"type":"string","description":"Display text and internal value for this option"},"label":{"type":"string","description":"Alternative display text (deprecated, use 'value')"},"children":{"type":"array","description":"Nested classifications that appear when this option is selected","items":{"$ref":"#/components/schemas/Classification"}}}},"Relation":{"type":"object","description":"Relation type that can connect two annotations.\n\n**Relation Types:**\n- `one-to-one`: Direct connection between two annotations (Ango only)\n- `group`: Groups multiple annotations together (Ango only)\n- `one-to-many`: Parent-child hierarchical relationship (PCT only)\n\n**Note:** PCT projects support additional fields like `parentTool` and `childTools` for one-to-many relations.\n","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this relation"},"title":{"type":"string","description":"Display name for the relation"},"tool":{"type":"string","description":"Type of relation tool:\n- `one-to-one`: Direct 1:1 connection between annotations (Ango only)\n- `group`: Non-directional grouping of multiple annotations (Ango only)\n- `one-to-many`: Hierarchical parent-child relationship (PCT only)\n","enum":["one-to-one","group","one-to-many"]},"color":{"type":"string","description":"Hex color code for this relation","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether this relation must be used in annotations","default":false},"classifications":{"type":"array","description":"Nested classification questions attached to this relation.\nAllows adding metadata or attributes to the relationship itself.\n","default":[]},"multiple":{"type":"boolean","description":"Whether multiple instances of this relation can be created","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut to quickly create this relation"},"parentTool":{"type":"string","description":"(PCT only) For one-to-many relations, specifies the schemaId of the parent tool.\nOnly applicable when `tool: \"one-to-many\"`.\n"},"childTools":{"type":"array","description":"(PCT only) For one-to-many relations, specifies the schemaIds of allowed child tools.\nOnly applicable when `tool: \"one-to-many\"`.\n","items":{"type":"string"}}}}}}}
```

## The CategorySchemaPCTExample object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"CategorySchemaPCTExample":{"type":"object","description":"Example category schema for a PCT (Point Cloud Tool) project.\nShows complete PCT tool configuration with all available fields including marking tools,\ncuboid presets, and nested classifications with PCT-specific attributes.\n"}}}}
```

## The Tool object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"Tool":{"type":"object","description":"Annotation tool configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this tool"},"title":{"type":"string","description":"Display name for the tool"},"tool":{"type":"string","description":"Type of annotation tool","enum":["bounding-box","polygon","polyline","point","rotated-bounding-box","segmentation","brush","medical-brush","ner","pdf","pct","angle"]},"required":{"type":"boolean","description":"Whether this annotation is required","default":false},"color":{"type":"string","description":"Hex color code for this annotation class","pattern":"^#[0-9A-Fa-f]{6}$"},"shortcutKey":{"type":"string","description":"Keyboard shortcut for quick selection"},"classifications":{"type":"array","description":"Nested classifications specific to this tool","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow creating multiple instances of this annotation","default":false},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data (for organizing exported annotations)"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"markingTools":{"type":"array","description":"(PCT only) List of marking tools enabled for this PCT tool.\nAvailable marking tools: polygon, polygon2d, polyline, polyline2d, cuboid, rectangle\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}},"truncate":{"type":"boolean","description":"(PCT only) Enable truncation for this tool","default":false},"presetCuboidDimensions":{"type":"boolean","description":"(PCT only) Enable preset cuboid dimensions","default":false},"cuboidLength":{"type":"string","description":"(PCT only) Preset cuboid length value (stored as string)"},"cuboidWidth":{"type":"string","description":"(PCT only) Preset cuboid width value (stored as string)"},"cuboidHeight":{"type":"string","description":"(PCT only) Preset cuboid height value (stored as string)"},"keypointWithVerticalLine":{"type":"boolean","description":"(PCT only) Enable keypoint with vertical line feature","default":false},"edgeLengthConstraint":{"type":"string","description":"(PCT only) Minimum edge length constraint (stored as string)"},"hasInstance":{"type":"boolean","description":"(PCT only) Enable instance tracking for this tool","default":false}}},"Classification":{"type":"object","description":"Classification question configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this classification"},"title":{"type":"string","description":"Question or label text"},"tool":{"type":"string","description":"Type of classification input (same as 'type')","enum":["radio","checkbox","single-dropdown","multi-dropdown","tree-dropdown","single-tree-dropdown","text","boolean"]},"color":{"type":"string","description":"Hex color code for this classification","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether answering this classification is required","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut key"},"columnField":{"type":"boolean","description":"Whether to show in column view","default":false},"frameSpecific":{"type":"boolean","description":"Whether classification is frame-specific (for videos)","default":false},"showDropdown":{"type":"boolean","description":"Whether to show as dropdown in UI","default":false},"richText":{"type":"boolean","description":"Whether to enable rich text input (for text type)","default":false},"regex":{"type":"string","nullable":true,"description":"Regular expression for text validation (for text type)"},"parentOptionId":{"type":"string","nullable":true,"description":"Parent option ID for nested classifications"},"options":{"type":"array","description":"Available options (not used for text type)","items":{"$ref":"#/components/schemas/ClassificationOption"}},"treeOptions":{"type":"array","description":"Tree-structured options (for tree-dropdown types)","items":{"type":"object"}},"classifications":{"type":"array","description":"Nested child classifications","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow multiple selections","default":false},"parentToolType":{"type":"string","description":"(PCT only) Parent tool type this classification belongs to"},"parentToolId":{"type":"string","description":"(PCT only) Parent tool schemaId this classification is associated with"},"attributeLevel":{"type":"string","description":"(PCT only) Level at which this attribute applies","enum":["Class Level","Instance Level"]},"defaultValue":{"description":"Default value for this classification (type varies based on classification tool type)","oneOf":[{"type":"boolean"},{"type":"string"},{"type":"number"}]},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"attributeMode":{"type":"string","description":"(PCT only) Attribute mode determining how values persist across frames.\n- Time Varying: Value can change per frame\n- Constant: Single value for entire sequence\n- Sensor Specific: Different values per sensor\n","enum":["Time Varying","Constant","Sensor Specific"]},"associatedMarkingTool":{"type":"array","description":"(PCT only) Marking tools this classification is associated with.\nThis classification will only be available when using these marking tools.\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}}}},"ClassificationOption":{"type":"object","description":"Option for a classification question","required":["schemaId","value"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this option"},"value":{"type":"string","description":"Display text and internal value for this option"},"label":{"type":"string","description":"Alternative display text (deprecated, use 'value')"},"children":{"type":"array","description":"Nested classifications that appear when this option is selected","items":{"$ref":"#/components/schemas/Classification"}}}}}}}
```

## The Classification object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"Classification":{"type":"object","description":"Classification question configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this classification"},"title":{"type":"string","description":"Question or label text"},"tool":{"type":"string","description":"Type of classification input (same as 'type')","enum":["radio","checkbox","single-dropdown","multi-dropdown","tree-dropdown","single-tree-dropdown","text","boolean"]},"color":{"type":"string","description":"Hex color code for this classification","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether answering this classification is required","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut key"},"columnField":{"type":"boolean","description":"Whether to show in column view","default":false},"frameSpecific":{"type":"boolean","description":"Whether classification is frame-specific (for videos)","default":false},"showDropdown":{"type":"boolean","description":"Whether to show as dropdown in UI","default":false},"richText":{"type":"boolean","description":"Whether to enable rich text input (for text type)","default":false},"regex":{"type":"string","nullable":true,"description":"Regular expression for text validation (for text type)"},"parentOptionId":{"type":"string","nullable":true,"description":"Parent option ID for nested classifications"},"options":{"type":"array","description":"Available options (not used for text type)","items":{"$ref":"#/components/schemas/ClassificationOption"}},"treeOptions":{"type":"array","description":"Tree-structured options (for tree-dropdown types)","items":{"type":"object"}},"classifications":{"type":"array","description":"Nested child classifications","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow multiple selections","default":false},"parentToolType":{"type":"string","description":"(PCT only) Parent tool type this classification belongs to"},"parentToolId":{"type":"string","description":"(PCT only) Parent tool schemaId this classification is associated with"},"attributeLevel":{"type":"string","description":"(PCT only) Level at which this attribute applies","enum":["Class Level","Instance Level"]},"defaultValue":{"description":"Default value for this classification (type varies based on classification tool type)","oneOf":[{"type":"boolean"},{"type":"string"},{"type":"number"}]},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"attributeMode":{"type":"string","description":"(PCT only) Attribute mode determining how values persist across frames.\n- Time Varying: Value can change per frame\n- Constant: Single value for entire sequence\n- Sensor Specific: Different values per sensor\n","enum":["Time Varying","Constant","Sensor Specific"]},"associatedMarkingTool":{"type":"array","description":"(PCT only) Marking tools this classification is associated with.\nThis classification will only be available when using these marking tools.\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}}}},"ClassificationOption":{"type":"object","description":"Option for a classification question","required":["schemaId","value"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this option"},"value":{"type":"string","description":"Display text and internal value for this option"},"label":{"type":"string","description":"Alternative display text (deprecated, use 'value')"},"children":{"type":"array","description":"Nested classifications that appear when this option is selected","items":{"$ref":"#/components/schemas/Classification"}}}}}}}
```

## The ClassificationOption object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ClassificationOption":{"type":"object","description":"Option for a classification question","required":["schemaId","value"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this option"},"value":{"type":"string","description":"Display text and internal value for this option"},"label":{"type":"string","description":"Alternative display text (deprecated, use 'value')"},"children":{"type":"array","description":"Nested classifications that appear when this option is selected","items":{"$ref":"#/components/schemas/Classification"}}}},"Classification":{"type":"object","description":"Classification question configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this classification"},"title":{"type":"string","description":"Question or label text"},"tool":{"type":"string","description":"Type of classification input (same as 'type')","enum":["radio","checkbox","single-dropdown","multi-dropdown","tree-dropdown","single-tree-dropdown","text","boolean"]},"color":{"type":"string","description":"Hex color code for this classification","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether answering this classification is required","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut key"},"columnField":{"type":"boolean","description":"Whether to show in column view","default":false},"frameSpecific":{"type":"boolean","description":"Whether classification is frame-specific (for videos)","default":false},"showDropdown":{"type":"boolean","description":"Whether to show as dropdown in UI","default":false},"richText":{"type":"boolean","description":"Whether to enable rich text input (for text type)","default":false},"regex":{"type":"string","nullable":true,"description":"Regular expression for text validation (for text type)"},"parentOptionId":{"type":"string","nullable":true,"description":"Parent option ID for nested classifications"},"options":{"type":"array","description":"Available options (not used for text type)","items":{"$ref":"#/components/schemas/ClassificationOption"}},"treeOptions":{"type":"array","description":"Tree-structured options (for tree-dropdown types)","items":{"type":"object"}},"classifications":{"type":"array","description":"Nested child classifications","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow multiple selections","default":false},"parentToolType":{"type":"string","description":"(PCT only) Parent tool type this classification belongs to"},"parentToolId":{"type":"string","description":"(PCT only) Parent tool schemaId this classification is associated with"},"attributeLevel":{"type":"string","description":"(PCT only) Level at which this attribute applies","enum":["Class Level","Instance Level"]},"defaultValue":{"description":"Default value for this classification (type varies based on classification tool type)","oneOf":[{"type":"boolean"},{"type":"string"},{"type":"number"}]},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"attributeMode":{"type":"string","description":"(PCT only) Attribute mode determining how values persist across frames.\n- Time Varying: Value can change per frame\n- Constant: Single value for entire sequence\n- Sensor Specific: Different values per sensor\n","enum":["Time Varying","Constant","Sensor Specific"]},"associatedMarkingTool":{"type":"array","description":"(PCT only) Marking tools this classification is associated with.\nThis classification will only be available when using these marking tools.\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}}}}}}}
```

## The Relation object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"Relation":{"type":"object","description":"Relation type that can connect two annotations.\n\n**Relation Types:**\n- `one-to-one`: Direct connection between two annotations (Ango only)\n- `group`: Groups multiple annotations together (Ango only)\n- `one-to-many`: Parent-child hierarchical relationship (PCT only)\n\n**Note:** PCT projects support additional fields like `parentTool` and `childTools` for one-to-many relations.\n","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this relation"},"title":{"type":"string","description":"Display name for the relation"},"tool":{"type":"string","description":"Type of relation tool:\n- `one-to-one`: Direct 1:1 connection between annotations (Ango only)\n- `group`: Non-directional grouping of multiple annotations (Ango only)\n- `one-to-many`: Hierarchical parent-child relationship (PCT only)\n","enum":["one-to-one","group","one-to-many"]},"color":{"type":"string","description":"Hex color code for this relation","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether this relation must be used in annotations","default":false},"classifications":{"type":"array","description":"Nested classification questions attached to this relation.\nAllows adding metadata or attributes to the relationship itself.\n","default":[]},"multiple":{"type":"boolean","description":"Whether multiple instances of this relation can be created","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut to quickly create this relation"},"parentTool":{"type":"string","description":"(PCT only) For one-to-many relations, specifies the schemaId of the parent tool.\nOnly applicable when `tool: \"one-to-many\"`.\n"},"childTools":{"type":"array","description":"(PCT only) For one-to-many relations, specifies the schemaIds of allowed child tools.\nOnly applicable when `tool: \"one-to-many\"`.\n","items":{"type":"string"}}}}}}}
```

## The PctConfig object

````json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"PctConfig":{"type":"object","description":"Configuration specific to Point Cloud Tool (PCT) projects.\nThese settings control how 3D point cloud annotation behaves.\n\nExample:\n```json\n{\n  \"allowOverlapping\": false,\n  \"trackingMultipleSensors\": true,\n  \"segmentationMode\": false\n}\n```\n","properties":{"allowOverlapping":{"type":"boolean","description":"Allow overlapping 3D bounding boxes","default":false},"trackingMultipleSensors":{"type":"boolean","description":"Enable tracking across multiple LiDAR sensors","default":false},"segmentationMode":{"type":"boolean","description":"Enable segmentation mode for point clouds","default":false},"externalValidation":{"$ref":"#/components/schemas/ExternalValidation"},"segmentationValidation":{"$ref":"#/components/schemas/SegmentationValidation"}}},"ExternalValidation":{"type":"object","description":"Configuration for external API-based validation","properties":{"enabled":{"type":"boolean","description":"Enable external validation API calls","default":false},"apiUrl":{"type":"string","description":"URL of external validation API endpoint","default":""},"singleFrame":{"type":"boolean","description":"For video projects - validate only a single frame","default":false},"specificFrame":{"type":"boolean","description":"For video projects - validate specific frame(s)","default":false},"frameNumbers":{"type":"integer","nullable":true,"description":"Frame number to validate for video projects (string input is coerced to a number)"}}},"SegmentationValidation":{"type":"object","description":"Configuration for segmentation coverage validation","properties":{"enabled":{"type":"boolean","default":false},"completeCoverage":{"type":"number","default":100}}}}}}
````

## The Stage object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"Stage":{"type":"object","description":"Workflow stage configuration. Different stage types have different properties.\n- Label: Manual annotation stage\n- Review: QA/review stage\n- Logic: Conditional routing stage\n- Plugin: ML model integration stage\n- Webhook: External API callback stage\n- Consensus: Multi-annotator agreement stage\n- Hold: Pause stage\n- Start: Entry point (auto-forwards)\n- Complete: Terminal success stage\n- Archive: Terminal archive stage\n","required":["id","type"],"properties":{"id":{"type":"string","description":"Unique identifier for this stage"},"name":{"type":"string","description":"Display name for this stage"},"type":{"type":"string","description":"Type of stage","enum":["Label","Review","Logic","Plugin","Webhook","Consensus","Hold","Start","Complete","Archive"]},"next":{"type":"array","items":{"type":"string"},"description":"Array of next stage IDs (for Logic stages, order matters)"},"position":{"type":"object","description":"Position in workflow diagram UI","properties":{"x":{"type":"number"},"y":{"type":"number"}}},"assignedTo":{"type":"array","description":"Users assigned to work on this stage (Label/Review stages)","items":{"type":"string"}},"rememberAssignee":{"type":"boolean","description":"Auto-assign to previous stage's user (sticky assignment)","default":false},"logic":{"$ref":"#/components/schemas/LogicConfig","description":"Logic routing configuration (Logic stages only)"},"pluginId":{"type":"string","description":"Plugin ID to execute (Plugin stages only)"},"pluginConfig":{"type":"object","description":"Plugin-specific configuration (Plugin stages only)"},"webhook":{"$ref":"#/components/schemas/WebhookConfig","description":"Webhook configuration (Webhook stages only)"},"consensusConfig":{"$ref":"#/components/schemas/ConsensusConfig","description":"Consensus configuration (Consensus stages only)"},"readOnly":{"type":"boolean","description":"Whether this stage is read-only (users can view but not edit)","default":false},"preventRequeue":{"type":"boolean","description":"Prevent tasks from being requeued to this stage","default":false},"autoForward":{"type":"boolean","description":"Automatically advance to next stage without user action","default":true},"consensusId":{"type":"string","description":"Links stage to a consensus stage (used in consensus workflows)"},"consensusDynamic":{"type":"boolean","description":"Dynamic vs static consensus stage","default":false}}},"LogicConfig":{"type":"object","description":"Logic stage routing configuration. Routes tasks based on conditions.\nConditions are evaluated in order, first match wins.\n","properties":{"type":{"type":"string","description":"Type of logic routing","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Default value (usually empty)"},"conditions":{"type":"array","description":"Array of routing conditions","items":{"$ref":"#/components/schemas/LogicCondition"}}}},"LogicCondition":{"type":"object","description":"Individual routing condition for Logic stages","properties":{"type":{"type":"string","description":"Type of condition","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Condition-specific value","properties":{"tools":{"type":"array","description":"Tool filters (AnnotationType logic)","items":{"type":"object","properties":{"filter":{"type":"array","items":{"type":"string"},"description":"Annotation filters:\n- exists___<schemaId>: Annotation must exist\n- not-exist___<schemaId>: Annotation must NOT exist\n"}}}},"emails":{"type":"array","description":"User emails (Annotator logic)","items":{"type":"string"}},"sampleRate":{"type":"number","description":"Sample rate 0-1 (RandomSample logic)","minimum":0,"maximum":1},"duration":{"type":"number","description":"Duration in milliseconds (Duration logic)"},"operator":{"type":"string","description":"Comparison operator for Duration","enum":["lt","gt","eq"]},"batches":{"type":"array","description":"Batch names (Batch logic)","items":{"type":"string"}}}},"index":{"type":"number","description":"Index of next stage in parent's next array"}}},"WebhookConfig":{"type":"object","description":"Webhook configuration for external API integration","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"Webhook endpoint URL"},"method":{"type":"string","description":"HTTP method","enum":["POST","PUT"],"default":"POST"},"authType":{"type":"string","description":"Authentication type","enum":["no_auth","secret","bearer_token"],"default":"no_auth"},"secret":{"type":"string","description":"Secret for HMAC signature (authType=secret)"},"token":{"type":"string","description":"Bearer token (authType=bearer_token)"},"timeout":{"type":"number","description":"Timeout in milliseconds","default":30000},"retryCount":{"type":"number","description":"Number of retry attempts","default":3}}},"ConsensusConfig":{"type":"object","description":"Consensus configuration for multi-annotator agreement","properties":{"version":{"type":"number","description":"Consensus algorithm version (1 or 2, v2 recommended)","enum":[1,2],"default":2},"threshold":{"type":"number","description":"Minimum agreement score (0-1) required to pass","minimum":0,"maximum":1},"adjudicationMethod":{"type":"string","description":"Method for resolving disagreements","enum":["majority","union","intersection","first"],"default":"majority"},"dynamicConsensus":{"type":"boolean","description":"Add more copies if threshold not met","default":false},"minCopies":{"type":"number","description":"Minimum number of consensus copies","minimum":2,"default":2},"maxCopies":{"type":"number","description":"Maximum number of consensus copies (dynamic consensus)","default":5}}}}}}
```

## The LogicConfig object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"LogicConfig":{"type":"object","description":"Logic stage routing configuration. Routes tasks based on conditions.\nConditions are evaluated in order, first match wins.\n","properties":{"type":{"type":"string","description":"Type of logic routing","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Default value (usually empty)"},"conditions":{"type":"array","description":"Array of routing conditions","items":{"$ref":"#/components/schemas/LogicCondition"}}}},"LogicCondition":{"type":"object","description":"Individual routing condition for Logic stages","properties":{"type":{"type":"string","description":"Type of condition","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Condition-specific value","properties":{"tools":{"type":"array","description":"Tool filters (AnnotationType logic)","items":{"type":"object","properties":{"filter":{"type":"array","items":{"type":"string"},"description":"Annotation filters:\n- exists___<schemaId>: Annotation must exist\n- not-exist___<schemaId>: Annotation must NOT exist\n"}}}},"emails":{"type":"array","description":"User emails (Annotator logic)","items":{"type":"string"}},"sampleRate":{"type":"number","description":"Sample rate 0-1 (RandomSample logic)","minimum":0,"maximum":1},"duration":{"type":"number","description":"Duration in milliseconds (Duration logic)"},"operator":{"type":"string","description":"Comparison operator for Duration","enum":["lt","gt","eq"]},"batches":{"type":"array","description":"Batch names (Batch logic)","items":{"type":"string"}}}},"index":{"type":"number","description":"Index of next stage in parent's next array"}}}}}}
```

## The LogicCondition object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"LogicCondition":{"type":"object","description":"Individual routing condition for Logic stages","properties":{"type":{"type":"string","description":"Type of condition","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Condition-specific value","properties":{"tools":{"type":"array","description":"Tool filters (AnnotationType logic)","items":{"type":"object","properties":{"filter":{"type":"array","items":{"type":"string"},"description":"Annotation filters:\n- exists___<schemaId>: Annotation must exist\n- not-exist___<schemaId>: Annotation must NOT exist\n"}}}},"emails":{"type":"array","description":"User emails (Annotator logic)","items":{"type":"string"}},"sampleRate":{"type":"number","description":"Sample rate 0-1 (RandomSample logic)","minimum":0,"maximum":1},"duration":{"type":"number","description":"Duration in milliseconds (Duration logic)"},"operator":{"type":"string","description":"Comparison operator for Duration","enum":["lt","gt","eq"]},"batches":{"type":"array","description":"Batch names (Batch logic)","items":{"type":"string"}}}},"index":{"type":"number","description":"Index of next stage in parent's next array"}}}}}}
```

## The WebhookConfig object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"WebhookConfig":{"type":"object","description":"Webhook configuration for external API integration","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"Webhook endpoint URL"},"method":{"type":"string","description":"HTTP method","enum":["POST","PUT"],"default":"POST"},"authType":{"type":"string","description":"Authentication type","enum":["no_auth","secret","bearer_token"],"default":"no_auth"},"secret":{"type":"string","description":"Secret for HMAC signature (authType=secret)"},"token":{"type":"string","description":"Bearer token (authType=bearer_token)"},"timeout":{"type":"number","description":"Timeout in milliseconds","default":30000},"retryCount":{"type":"number","description":"Number of retry attempts","default":3}}}}}}
```

## The ConsensusConfig object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ConsensusConfig":{"type":"object","description":"Consensus configuration for multi-annotator agreement","properties":{"version":{"type":"number","description":"Consensus algorithm version (1 or 2, v2 recommended)","enum":[1,2],"default":2},"threshold":{"type":"number","description":"Minimum agreement score (0-1) required to pass","minimum":0,"maximum":1},"adjudicationMethod":{"type":"string","description":"Method for resolving disagreements","enum":["majority","union","intersection","first"],"default":"majority"},"dynamicConsensus":{"type":"boolean","description":"Add more copies if threshold not met","default":false},"minCopies":{"type":"number","description":"Minimum number of consensus copies","minimum":2,"default":2},"maxCopies":{"type":"number","description":"Maximum number of consensus copies (dynamic consensus)","default":5}}}}}}
```

## The Assignee object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"Assignee":{"type":"object","properties":{"assignee":{"type":"string","description":"User email"},"role":{"type":"string","enum":["Manager","Labeler","Reviewer","Lead"]}}}}}}
```

## The Batch object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"Batch":{"type":"object","required":["name"],"properties":{"_id":{"type":"string","description":"Batch ID (auto-generated if not provided)"},"name":{"type":"string","description":"Batch name"}}}}}}
```

## The AssigneeUpdateRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"AssigneeUpdateRequest":{"type":"object","required":["role","assignees"],"properties":{"role":{"type":"string","enum":["Manager","Labeler","Reviewer","Lead"]},"assignees":{"type":"array","items":{"type":"string"},"description":"Array of user emails"},"roleAssignments":{"type":"object","additionalProperties":{"type":"string","enum":["Manager","Labeler","Reviewer","Lead"]},"description":"Optional map of user email to role override. Example - {\"user1@example.com\":\"Reviewer\"}"}}}}}}
```

## The AssetBuilderTemplateRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"AssetBuilderTemplateRequest":{"type":"object","required":["name","template"],"properties":{"name":{"type":"string"},"template":{"type":"string","description":"Template HTML"}}}}}}
```

## The AssetBuilderTemplateResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"AssetBuilderTemplateResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"template":{"type":"object","description":"Echoes the submitted fields; any extra body fields are stored and returned verbatim.","additionalProperties":true,"properties":{"_id":{"type":"string"},"name":{"type":"string"},"template":{"type":"string","description":"Template HTML"}}}}}}}}}}
```

## The PerformanceRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"PerformanceRequest":{"type":"object","properties":{"interval":{"type":"array","description":"Date-time range as [startISO, endISO].","minItems":2,"maxItems":2,"items":{"type":"string","format":"date-time"}},"batches":{"type":"array","items":{"type":"string"}}}}}}}
```

## The PerformanceResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"PerformanceResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"performance":{"type":"array","description":"Per-user performance metrics.","items":{"type":"object","properties":{"_id":{"type":"string"},"email":{"type":"string"},"completedLabels":{"type":"number"},"totalLabelingDuration":{"type":"number"},"avgLabelingDuration":{"type":"number"},"avgBenchmark":{"type":"number"},"totalLabelingIdleDuration":{"type":"number"},"totalLabelingBlurDuration":{"type":"number"},"completedReviews":{"type":"number"},"totalReviewDuration":{"type":"number"},"avgReviewDuration":{"type":"number"},"totalReviewIdleDuration":{"type":"number"},"totalReviewBlurDuration":{"type":"number"}}}}}}}}}}}
```

## The BatchUpsertResponse object

````json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"BatchUpsertResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"project":{"anyOf":[{"$ref":"#/components/schemas/Batch"},{"$ref":"#/components/schemas/Project"}],"description":"The created batch when `name` was sent, the updated project when `batches` was sent."}}}}},"Batch":{"type":"object","required":["name"],"properties":{"_id":{"type":"string","description":"Batch ID (auto-generated if not provided)"},"name":{"type":"string","description":"Batch name"}}},"Project":{"type":"object","properties":{"_id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"type":{"type":"string","enum":["ango","pct","iframe"],"default":"ango"},"owner":{"type":"string","description":"Owner email"},"organization":{"type":"string","description":"Organization ID"},"categorySchema":{"$ref":"#/components/schemas/CategorySchema"},"stages":{"type":"array","items":{"$ref":"#/components/schemas/Stage"}},"assignedTo":{"type":"array","items":{"$ref":"#/components/schemas/Assignee"}},"batches":{"type":"array","items":{"$ref":"#/components/schemas/Batch"}},"queueVersion":{"type":"number"},"assetCount":{"type":"integer"},"benchmarkEnabled":{"type":"boolean"},"benchmarkRatio":{"type":"number"},"instructions":{"type":"string","description":"Instructions URL"},"ocrLanguage":{"type":"string"},"idleTimeout":{"type":"number","description":"Idle timeout in seconds"},"projectPlugins":{"type":"object"},"pluginPresets":{"type":"array","items":{"type":"object"}},"errorCodesEnabled":{"type":"boolean"},"errorSchema":{"type":"object"},"exportStorageId":{"type":"string"},"exportStorageBucket":{"type":"string"},"exportStoragePath":{"type":"string"},"exportStorageEnabled":{"type":"boolean"},"taskSkipEnabled":{"type":"boolean"},"unassignSkippedTask":{"type":"boolean"},"gcSyncEnabled":{"type":"boolean"},"pctConfig":{"$ref":"#/components/schemas/PctConfig"},"role":{"type":"string","description":"Caller's role on this project (present on GET /project/{projectId} only)","enum":["Manager","Lead","Labeler","Reviewer"]},"deleted":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CategorySchema":{"type":"object","description":"Label configuration schema that defines the annotation tools, classifications, and relations for a project.\nThis schema determines what annotation types are available to users and how they can label assets.\n\n**Note:** Schema structure varies between Ango and PCT projects.\n- Ango projects: Use standard annotation tools (bounding-box, polygon, etc.)\n- PCT projects: Use PCT tool type with markingTools array and nested classifications with PCT-specific fields\n","properties":{"tools":{"type":"array","description":"Array of annotation tools (bounding box, polygon, segmentation, etc.)","items":{"$ref":"#/components/schemas/Tool"}},"classifications":{"type":"array","description":"Array of classification questions (radio, checkbox, dropdown, text, etc.)","items":{"$ref":"#/components/schemas/Classification"}},"relations":{"type":"array","description":"Array of relation types that can connect annotations","items":{"$ref":"#/components/schemas/Relation"}}}},"Tool":{"type":"object","description":"Annotation tool configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this tool"},"title":{"type":"string","description":"Display name for the tool"},"tool":{"type":"string","description":"Type of annotation tool","enum":["bounding-box","polygon","polyline","point","rotated-bounding-box","segmentation","brush","medical-brush","ner","pdf","pct","angle"]},"required":{"type":"boolean","description":"Whether this annotation is required","default":false},"color":{"type":"string","description":"Hex color code for this annotation class","pattern":"^#[0-9A-Fa-f]{6}$"},"shortcutKey":{"type":"string","description":"Keyboard shortcut for quick selection"},"classifications":{"type":"array","description":"Nested classifications specific to this tool","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow creating multiple instances of this annotation","default":false},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data (for organizing exported annotations)"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"markingTools":{"type":"array","description":"(PCT only) List of marking tools enabled for this PCT tool.\nAvailable marking tools: polygon, polygon2d, polyline, polyline2d, cuboid, rectangle\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}},"truncate":{"type":"boolean","description":"(PCT only) Enable truncation for this tool","default":false},"presetCuboidDimensions":{"type":"boolean","description":"(PCT only) Enable preset cuboid dimensions","default":false},"cuboidLength":{"type":"string","description":"(PCT only) Preset cuboid length value (stored as string)"},"cuboidWidth":{"type":"string","description":"(PCT only) Preset cuboid width value (stored as string)"},"cuboidHeight":{"type":"string","description":"(PCT only) Preset cuboid height value (stored as string)"},"keypointWithVerticalLine":{"type":"boolean","description":"(PCT only) Enable keypoint with vertical line feature","default":false},"edgeLengthConstraint":{"type":"string","description":"(PCT only) Minimum edge length constraint (stored as string)"},"hasInstance":{"type":"boolean","description":"(PCT only) Enable instance tracking for this tool","default":false}}},"Classification":{"type":"object","description":"Classification question configuration","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this classification"},"title":{"type":"string","description":"Question or label text"},"tool":{"type":"string","description":"Type of classification input (same as 'type')","enum":["radio","checkbox","single-dropdown","multi-dropdown","tree-dropdown","single-tree-dropdown","text","boolean"]},"color":{"type":"string","description":"Hex color code for this classification","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether answering this classification is required","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut key"},"columnField":{"type":"boolean","description":"Whether to show in column view","default":false},"frameSpecific":{"type":"boolean","description":"Whether classification is frame-specific (for videos)","default":false},"showDropdown":{"type":"boolean","description":"Whether to show as dropdown in UI","default":false},"richText":{"type":"boolean","description":"Whether to enable rich text input (for text type)","default":false},"regex":{"type":"string","nullable":true,"description":"Regular expression for text validation (for text type)"},"parentOptionId":{"type":"string","nullable":true,"description":"Parent option ID for nested classifications"},"options":{"type":"array","description":"Available options (not used for text type)","items":{"$ref":"#/components/schemas/ClassificationOption"}},"treeOptions":{"type":"array","description":"Tree-structured options (for tree-dropdown types)","items":{"type":"object"}},"classifications":{"type":"array","description":"Nested child classifications","items":{"$ref":"#/components/schemas/Classification"}},"multiple":{"type":"boolean","description":"Allow multiple selections","default":false},"parentToolType":{"type":"string","description":"(PCT only) Parent tool type this classification belongs to"},"parentToolId":{"type":"string","description":"(PCT only) Parent tool schemaId this classification is associated with"},"attributeLevel":{"type":"string","description":"(PCT only) Level at which this attribute applies","enum":["Class Level","Instance Level"]},"defaultValue":{"description":"Default value for this classification (type varies based on classification tool type)","oneOf":[{"type":"boolean"},{"type":"string"},{"type":"number"}]},"exportName":{"type":"string","description":"(PCT only) Name used when exporting data"},"displayName":{"type":"string","description":"(PCT only) Display name shown in the annotation interface"},"abbreviatedName":{"type":"string","description":"(PCT only) Abbreviated name for compact UI views"},"attributeMode":{"type":"string","description":"(PCT only) Attribute mode determining how values persist across frames.\n- Time Varying: Value can change per frame\n- Constant: Single value for entire sequence\n- Sensor Specific: Different values per sensor\n","enum":["Time Varying","Constant","Sensor Specific"]},"associatedMarkingTool":{"type":"array","description":"(PCT only) Marking tools this classification is associated with.\nThis classification will only be available when using these marking tools.\n","items":{"type":"string","enum":["polygon","polygon2d","polyline","polyline2d","cuboid","rectangle"]}}}},"ClassificationOption":{"type":"object","description":"Option for a classification question","required":["schemaId","value"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this option"},"value":{"type":"string","description":"Display text and internal value for this option"},"label":{"type":"string","description":"Alternative display text (deprecated, use 'value')"},"children":{"type":"array","description":"Nested classifications that appear when this option is selected","items":{"$ref":"#/components/schemas/Classification"}}}},"Relation":{"type":"object","description":"Relation type that can connect two annotations.\n\n**Relation Types:**\n- `one-to-one`: Direct connection between two annotations (Ango only)\n- `group`: Groups multiple annotations together (Ango only)\n- `one-to-many`: Parent-child hierarchical relationship (PCT only)\n\n**Note:** PCT projects support additional fields like `parentTool` and `childTools` for one-to-many relations.\n","required":["schemaId","title","tool"],"properties":{"schemaId":{"type":"string","description":"Unique identifier for this relation"},"title":{"type":"string","description":"Display name for the relation"},"tool":{"type":"string","description":"Type of relation tool:\n- `one-to-one`: Direct 1:1 connection between annotations (Ango only)\n- `group`: Non-directional grouping of multiple annotations (Ango only)\n- `one-to-many`: Hierarchical parent-child relationship (PCT only)\n","enum":["one-to-one","group","one-to-many"]},"color":{"type":"string","description":"Hex color code for this relation","pattern":"^#[0-9A-Fa-f]{6}$"},"required":{"type":"boolean","description":"Whether this relation must be used in annotations","default":false},"classifications":{"type":"array","description":"Nested classification questions attached to this relation.\nAllows adding metadata or attributes to the relationship itself.\n","default":[]},"multiple":{"type":"boolean","description":"Whether multiple instances of this relation can be created","default":false},"shortcutKey":{"type":"string","description":"Keyboard shortcut to quickly create this relation"},"parentTool":{"type":"string","description":"(PCT only) For one-to-many relations, specifies the schemaId of the parent tool.\nOnly applicable when `tool: \"one-to-many\"`.\n"},"childTools":{"type":"array","description":"(PCT only) For one-to-many relations, specifies the schemaIds of allowed child tools.\nOnly applicable when `tool: \"one-to-many\"`.\n","items":{"type":"string"}}}},"Stage":{"type":"object","description":"Workflow stage configuration. Different stage types have different properties.\n- Label: Manual annotation stage\n- Review: QA/review stage\n- Logic: Conditional routing stage\n- Plugin: ML model integration stage\n- Webhook: External API callback stage\n- Consensus: Multi-annotator agreement stage\n- Hold: Pause stage\n- Start: Entry point (auto-forwards)\n- Complete: Terminal success stage\n- Archive: Terminal archive stage\n","required":["id","type"],"properties":{"id":{"type":"string","description":"Unique identifier for this stage"},"name":{"type":"string","description":"Display name for this stage"},"type":{"type":"string","description":"Type of stage","enum":["Label","Review","Logic","Plugin","Webhook","Consensus","Hold","Start","Complete","Archive"]},"next":{"type":"array","items":{"type":"string"},"description":"Array of next stage IDs (for Logic stages, order matters)"},"position":{"type":"object","description":"Position in workflow diagram UI","properties":{"x":{"type":"number"},"y":{"type":"number"}}},"assignedTo":{"type":"array","description":"Users assigned to work on this stage (Label/Review stages)","items":{"type":"string"}},"rememberAssignee":{"type":"boolean","description":"Auto-assign to previous stage's user (sticky assignment)","default":false},"logic":{"$ref":"#/components/schemas/LogicConfig","description":"Logic routing configuration (Logic stages only)"},"pluginId":{"type":"string","description":"Plugin ID to execute (Plugin stages only)"},"pluginConfig":{"type":"object","description":"Plugin-specific configuration (Plugin stages only)"},"webhook":{"$ref":"#/components/schemas/WebhookConfig","description":"Webhook configuration (Webhook stages only)"},"consensusConfig":{"$ref":"#/components/schemas/ConsensusConfig","description":"Consensus configuration (Consensus stages only)"},"readOnly":{"type":"boolean","description":"Whether this stage is read-only (users can view but not edit)","default":false},"preventRequeue":{"type":"boolean","description":"Prevent tasks from being requeued to this stage","default":false},"autoForward":{"type":"boolean","description":"Automatically advance to next stage without user action","default":true},"consensusId":{"type":"string","description":"Links stage to a consensus stage (used in consensus workflows)"},"consensusDynamic":{"type":"boolean","description":"Dynamic vs static consensus stage","default":false}}},"LogicConfig":{"type":"object","description":"Logic stage routing configuration. Routes tasks based on conditions.\nConditions are evaluated in order, first match wins.\n","properties":{"type":{"type":"string","description":"Type of logic routing","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Default value (usually empty)"},"conditions":{"type":"array","description":"Array of routing conditions","items":{"$ref":"#/components/schemas/LogicCondition"}}}},"LogicCondition":{"type":"object","description":"Individual routing condition for Logic stages","properties":{"type":{"type":"string","description":"Type of condition","enum":["AnnotationType","Annotator","RandomSample","Duration","Batch","IssueErrorCode"]},"value":{"type":"object","description":"Condition-specific value","properties":{"tools":{"type":"array","description":"Tool filters (AnnotationType logic)","items":{"type":"object","properties":{"filter":{"type":"array","items":{"type":"string"},"description":"Annotation filters:\n- exists___<schemaId>: Annotation must exist\n- not-exist___<schemaId>: Annotation must NOT exist\n"}}}},"emails":{"type":"array","description":"User emails (Annotator logic)","items":{"type":"string"}},"sampleRate":{"type":"number","description":"Sample rate 0-1 (RandomSample logic)","minimum":0,"maximum":1},"duration":{"type":"number","description":"Duration in milliseconds (Duration logic)"},"operator":{"type":"string","description":"Comparison operator for Duration","enum":["lt","gt","eq"]},"batches":{"type":"array","description":"Batch names (Batch logic)","items":{"type":"string"}}}},"index":{"type":"number","description":"Index of next stage in parent's next array"}}},"WebhookConfig":{"type":"object","description":"Webhook configuration for external API integration","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"Webhook endpoint URL"},"method":{"type":"string","description":"HTTP method","enum":["POST","PUT"],"default":"POST"},"authType":{"type":"string","description":"Authentication type","enum":["no_auth","secret","bearer_token"],"default":"no_auth"},"secret":{"type":"string","description":"Secret for HMAC signature (authType=secret)"},"token":{"type":"string","description":"Bearer token (authType=bearer_token)"},"timeout":{"type":"number","description":"Timeout in milliseconds","default":30000},"retryCount":{"type":"number","description":"Number of retry attempts","default":3}}},"ConsensusConfig":{"type":"object","description":"Consensus configuration for multi-annotator agreement","properties":{"version":{"type":"number","description":"Consensus algorithm version (1 or 2, v2 recommended)","enum":[1,2],"default":2},"threshold":{"type":"number","description":"Minimum agreement score (0-1) required to pass","minimum":0,"maximum":1},"adjudicationMethod":{"type":"string","description":"Method for resolving disagreements","enum":["majority","union","intersection","first"],"default":"majority"},"dynamicConsensus":{"type":"boolean","description":"Add more copies if threshold not met","default":false},"minCopies":{"type":"number","description":"Minimum number of consensus copies","minimum":2,"default":2},"maxCopies":{"type":"number","description":"Maximum number of consensus copies (dynamic consensus)","default":5}}},"Assignee":{"type":"object","properties":{"assignee":{"type":"string","description":"User email"},"role":{"type":"string","enum":["Manager","Labeler","Reviewer","Lead"]}}},"PctConfig":{"type":"object","description":"Configuration specific to Point Cloud Tool (PCT) projects.\nThese settings control how 3D point cloud annotation behaves.\n\nExample:\n```json\n{\n  \"allowOverlapping\": false,\n  \"trackingMultipleSensors\": true,\n  \"segmentationMode\": false\n}\n```\n","properties":{"allowOverlapping":{"type":"boolean","description":"Allow overlapping 3D bounding boxes","default":false},"trackingMultipleSensors":{"type":"boolean","description":"Enable tracking across multiple LiDAR sensors","default":false},"segmentationMode":{"type":"boolean","description":"Enable segmentation mode for point clouds","default":false},"externalValidation":{"$ref":"#/components/schemas/ExternalValidation"},"segmentationValidation":{"$ref":"#/components/schemas/SegmentationValidation"}}},"ExternalValidation":{"type":"object","description":"Configuration for external API-based validation","properties":{"enabled":{"type":"boolean","description":"Enable external validation API calls","default":false},"apiUrl":{"type":"string","description":"URL of external validation API endpoint","default":""},"singleFrame":{"type":"boolean","description":"For video projects - validate only a single frame","default":false},"specificFrame":{"type":"boolean","description":"For video projects - validate specific frame(s)","default":false},"frameNumbers":{"type":"integer","nullable":true,"description":"Frame number to validate for video projects (string input is coerced to a number)"}}},"SegmentationValidation":{"type":"object","description":"Configuration for segmentation coverage validation","properties":{"enabled":{"type":"boolean","default":false},"completeCoverage":{"type":"number","default":100}}}}}}
````

## The BatchUpdateRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"BatchUpdateRequest":{"type":"object","description":"Send `name` or `batches`, not both. `name` wins when both are present.","anyOf":[{"required":["name"]},{"required":["batches"]}],"properties":{"name":{"type":"string","description":"Name of a single batch to append. Its ID is generated by the server."},"batches":{"type":"array","description":"Replaces the project's batch list. Keep the `_id` of any batch you want to preserve; an entry sent without one gets a new server-generated ID, and dropping a batch here unlinks it from the assets and tasks that still carry its ID.","items":{"$ref":"#/components/schemas/Batch"}}}},"Batch":{"type":"object","required":["name"],"properties":{"_id":{"type":"string","description":"Batch ID (auto-generated if not provided)"},"name":{"type":"string","description":"Batch name"}}}}}}
```

## The LabelTask object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"LabelTask":{"type":"object","properties":{"_id":{"type":"string"},"project":{"type":"string"},"asset":{"oneOf":[{"type":"string"},{"$ref":"#/components/schemas/Asset"}],"description":"Populated Asset object on list/detail responses; id string on annotate/review responses."},"stage":{"type":"string"},"status":{"type":"string"},"assignee":{"type":"string"},"externalId":{"type":"string"},"batches":{"type":"array","items":{"type":"string"}},"priority":{"type":"number"},"answer":{"type":"object"},"createdAt":{"type":"string","format":"date-time"},"duration":{"type":"number","description":"Duration in seconds"}}},"Asset":{"type":"object","description":"When an asset is private, carries a `storage`, or is NRRD, and its `data` is a URL, the read endpoints replace `data`, `dataset` and `overlay` with pre-signed URLs and set `head` to a signed HEAD URL. Otherwise `head` equals `data`.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"data":{"type":"string","description":"Asset URL"},"dataset":{"type":"array","items":{"type":"string"}},"overlay":{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"}}}]}},"head":{"type":"string","description":"Signed HEAD URL for the asset, or the same value as `data`"},"project":{"type":"string","description":"Project ID, or the project object when `populate=project` is used"},"organization":{"type":"string"},"storage":{"type":"string","description":"Storage integration ID, null for assets in the platform bucket","nullable":true},"externalId":{"type":"string"},"editorType":{"type":"string","enum":["ango","pct","llm","nrrd","iframe"]},"uploadStatus":{"type":"string","enum":["READY","PCT_WAITING","METADATA_WAITING","PCT_FAILED","METADATA_FAILED"],"description":"An asset is READY once it can be annotated. PCT and preprocessing assets get their task only after the waiting state clears."},"isPrivate":{"type":"boolean"},"isPreLabeled":{"type":"boolean"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs the asset belongs to"},"metadata":{"$ref":"#/components/schemas/AssetMetadata"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"label":{"type":"object"}}}},"contextData":{"type":"object"},"labelTasks":{"type":"array","description":"Returned only when populated, as objects carrying `_id`, `id`, `stage` and `assignee`. The project assets list always populates it.","items":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"stage":{"type":"string"},"assignee":{"type":"string"}}}},"deleted":{"type":"boolean","description":"Deleted assets are excluded from responses by default, so this is false"},"preprocessEnqueuedAt":{"type":"string","format":"date-time","description":"When the asset was queued for preprocessing"},"createdAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"}}},"AssetMetadata":{"type":"object","description":"Technical asset metadata. Only these fields are stored; any other key is dropped on write. For a video asset, supply width, height and frameTotal to skip server-side preprocessing.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"frameStep":{"type":"integer"},"frameTotal":{"type":"integer"},"frameRate":{"type":"number"},"numOfPages":{"type":"integer"},"numOfWords":{"type":"integer"},"duration":{"type":"integer","description":"Duration in milliseconds"},"hasVariableFrameRate":{"type":"boolean"}}}}}}
```

## The TaskListResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"TaskListResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"tasks":{"type":"array","items":{"$ref":"#/components/schemas/LabelTask"}},"total":{"type":"integer"}}}}},"LabelTask":{"type":"object","properties":{"_id":{"type":"string"},"project":{"type":"string"},"asset":{"oneOf":[{"type":"string"},{"$ref":"#/components/schemas/Asset"}],"description":"Populated Asset object on list/detail responses; id string on annotate/review responses."},"stage":{"type":"string"},"status":{"type":"string"},"assignee":{"type":"string"},"externalId":{"type":"string"},"batches":{"type":"array","items":{"type":"string"}},"priority":{"type":"number"},"answer":{"type":"object"},"createdAt":{"type":"string","format":"date-time"},"duration":{"type":"number","description":"Duration in seconds"}}},"Asset":{"type":"object","description":"When an asset is private, carries a `storage`, or is NRRD, and its `data` is a URL, the read endpoints replace `data`, `dataset` and `overlay` with pre-signed URLs and set `head` to a signed HEAD URL. Otherwise `head` equals `data`.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"data":{"type":"string","description":"Asset URL"},"dataset":{"type":"array","items":{"type":"string"}},"overlay":{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"}}}]}},"head":{"type":"string","description":"Signed HEAD URL for the asset, or the same value as `data`"},"project":{"type":"string","description":"Project ID, or the project object when `populate=project` is used"},"organization":{"type":"string"},"storage":{"type":"string","description":"Storage integration ID, null for assets in the platform bucket","nullable":true},"externalId":{"type":"string"},"editorType":{"type":"string","enum":["ango","pct","llm","nrrd","iframe"]},"uploadStatus":{"type":"string","enum":["READY","PCT_WAITING","METADATA_WAITING","PCT_FAILED","METADATA_FAILED"],"description":"An asset is READY once it can be annotated. PCT and preprocessing assets get their task only after the waiting state clears."},"isPrivate":{"type":"boolean"},"isPreLabeled":{"type":"boolean"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs the asset belongs to"},"metadata":{"$ref":"#/components/schemas/AssetMetadata"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"label":{"type":"object"}}}},"contextData":{"type":"object"},"labelTasks":{"type":"array","description":"Returned only when populated, as objects carrying `_id`, `id`, `stage` and `assignee`. The project assets list always populates it.","items":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"stage":{"type":"string"},"assignee":{"type":"string"}}}},"deleted":{"type":"boolean","description":"Deleted assets are excluded from responses by default, so this is false"},"preprocessEnqueuedAt":{"type":"string","format":"date-time","description":"When the asset was queued for preprocessing"},"createdAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"}}},"AssetMetadata":{"type":"object","description":"Technical asset metadata. Only these fields are stored; any other key is dropped on write. For a video asset, supply width, height and frameTotal to skip server-side preprocessing.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"frameStep":{"type":"integer"},"frameTotal":{"type":"integer"},"frameRate":{"type":"number"},"numOfPages":{"type":"integer"},"numOfWords":{"type":"integer"},"duration":{"type":"integer","description":"Duration in milliseconds"},"hasVariableFrameRate":{"type":"boolean"}}}}}}
```

## The TaskResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"TaskResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"task":{"$ref":"#/components/schemas/LabelTask"}}}}},"LabelTask":{"type":"object","properties":{"_id":{"type":"string"},"project":{"type":"string"},"asset":{"oneOf":[{"type":"string"},{"$ref":"#/components/schemas/Asset"}],"description":"Populated Asset object on list/detail responses; id string on annotate/review responses."},"stage":{"type":"string"},"status":{"type":"string"},"assignee":{"type":"string"},"externalId":{"type":"string"},"batches":{"type":"array","items":{"type":"string"}},"priority":{"type":"number"},"answer":{"type":"object"},"createdAt":{"type":"string","format":"date-time"},"duration":{"type":"number","description":"Duration in seconds"}}},"Asset":{"type":"object","description":"When an asset is private, carries a `storage`, or is NRRD, and its `data` is a URL, the read endpoints replace `data`, `dataset` and `overlay` with pre-signed URLs and set `head` to a signed HEAD URL. Otherwise `head` equals `data`.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"data":{"type":"string","description":"Asset URL"},"dataset":{"type":"array","items":{"type":"string"}},"overlay":{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"}}}]}},"head":{"type":"string","description":"Signed HEAD URL for the asset, or the same value as `data`"},"project":{"type":"string","description":"Project ID, or the project object when `populate=project` is used"},"organization":{"type":"string"},"storage":{"type":"string","description":"Storage integration ID, null for assets in the platform bucket","nullable":true},"externalId":{"type":"string"},"editorType":{"type":"string","enum":["ango","pct","llm","nrrd","iframe"]},"uploadStatus":{"type":"string","enum":["READY","PCT_WAITING","METADATA_WAITING","PCT_FAILED","METADATA_FAILED"],"description":"An asset is READY once it can be annotated. PCT and preprocessing assets get their task only after the waiting state clears."},"isPrivate":{"type":"boolean"},"isPreLabeled":{"type":"boolean"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs the asset belongs to"},"metadata":{"$ref":"#/components/schemas/AssetMetadata"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"label":{"type":"object"}}}},"contextData":{"type":"object"},"labelTasks":{"type":"array","description":"Returned only when populated, as objects carrying `_id`, `id`, `stage` and `assignee`. The project assets list always populates it.","items":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"stage":{"type":"string"},"assignee":{"type":"string"}}}},"deleted":{"type":"boolean","description":"Deleted assets are excluded from responses by default, so this is false"},"preprocessEnqueuedAt":{"type":"string","format":"date-time","description":"When the asset was queued for preprocessing"},"createdAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"}}},"AssetMetadata":{"type":"object","description":"Technical asset metadata. Only these fields are stored; any other key is dropped on write. For a video asset, supply width, height and frameTotal to skip server-side preprocessing.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"frameStep":{"type":"integer"},"frameTotal":{"type":"integer"},"frameRate":{"type":"number"},"numOfPages":{"type":"integer"},"numOfWords":{"type":"integer"},"duration":{"type":"integer","description":"Duration in milliseconds"},"hasVariableFrameRate":{"type":"boolean"}}}}}}
```

## The TaskFilters object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"TaskFilters":{"type":"object","description":"Filter criteria for task operations","properties":{"taskIds":{"type":"array","items":{"type":"string"},"description":"Array of task IDs"},"externalIds":{"type":"array","items":{"type":"string"},"description":"Array of external IDs"},"assetIds":{"type":"array","items":{"type":"string"},"description":"Array of asset IDs"},"fromStageIds":{"type":"array","items":{"type":"string"},"description":"Array of source stage IDs"},"batches":{"type":"array","items":{"type":"string"},"description":"Array of batch IDs"}}}}}}
```

## The RequeueTasksRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"RequeueTasksRequest":{"type":"object","required":["toStageId","filters"],"properties":{"toStageId":{"type":"string","description":"Destination stage ID"},"filters":{"$ref":"#/components/schemas/TaskFilters"},"options":{"type":"object","description":"Options for task requeue operation","properties":{"removeAnnotations":{"type":"boolean","description":"Remove existing annotations when requeuing","default":false},"removeAssignee":{"type":"boolean","description":"Remove task assignee when requeuing","default":false},"removeStageHistory":{"type":"boolean","description":"Remove stage history when requeuing","default":false}}}}},"TaskFilters":{"type":"object","description":"Filter criteria for task operations","properties":{"taskIds":{"type":"array","items":{"type":"string"},"description":"Array of task IDs"},"externalIds":{"type":"array","items":{"type":"string"},"description":"Array of external IDs"},"assetIds":{"type":"array","items":{"type":"string"},"description":"Array of asset IDs"},"fromStageIds":{"type":"array","items":{"type":"string"},"description":"Array of source stage IDs"},"batches":{"type":"array","items":{"type":"string"},"description":"Array of batch IDs"}}}}}}
```

## The RequeueResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"RequeueResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"modifiedCount":{"type":"integer"}}}}}}}}
```

## The UpdatePriorityRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"UpdatePriorityRequest":{"type":"object","required":["priority","filters"],"properties":{"priority":{"type":"number","minimum":-1000,"maximum":1000,"description":"Priority value (-1000 to 1000)"},"filters":{"$ref":"#/components/schemas/TaskFilters"},"options":{"type":"object"}}},"TaskFilters":{"type":"object","description":"Filter criteria for task operations","properties":{"taskIds":{"type":"array","items":{"type":"string"},"description":"Array of task IDs"},"externalIds":{"type":"array","items":{"type":"string"},"description":"Array of external IDs"},"assetIds":{"type":"array","items":{"type":"string"},"description":"Array of asset IDs"},"fromStageIds":{"type":"array","items":{"type":"string"},"description":"Array of source stage IDs"},"batches":{"type":"array","items":{"type":"string"},"description":"Array of batch IDs"}}}}}}
```

## The PriorityUpdateResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"PriorityUpdateResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"result":{"type":"object","properties":{"modifiedCount":{"type":"integer"},"priority":{"type":"number"}}}}}}}}}}
```

## The TaskAssignRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"TaskAssignRequest":{"type":"object","required":["user","project","tasks","stage"],"properties":{"user":{"type":"string","description":"User email to assign to"},"project":{"type":"string","description":"Project ID"},"tasks":{"type":"array","items":{"type":"string"},"description":"Array of task IDs"},"stage":{"type":"string","description":"Stage ID"}}}}}}
```

## The TaskAssignResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"TaskAssignResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"task":{"type":"object","properties":{"updatedCount":{"type":"integer"},"assignedTasks":{"type":"object","properties":{"count":{"type":"integer"}}},"message":{"type":"string"}}}}}}}}}}
```

## The AnnotateRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"AnnotateRequest":{"type":"object","required":["answer"],"properties":{"answer":{"type":"object","description":"Annotation answer object (structure depends on project categorySchema)"}}}}}}
```

## The ReviewRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ReviewRequest":{"type":"object","required":["reviewStatus"],"properties":{"reviewStatus":{"type":"string","enum":["Accepted","Rejected"],"description":"Review decision for the task.\n- `Accepted`: Approve the task and move it to the next stage\n- `Rejected`: Reject the task (typically sends it back for correction)\n"},"answer":{"type":"object","description":"Optional annotation modifications (structure depends on project categorySchema)"},"duration":{"type":"number","description":"Time spent reviewing in seconds"},"idleDuration":{"type":"number","description":"Idle time during review in seconds"},"blurDuration":{"type":"number","description":"Time window was blurred/unfocused in seconds"}}}}}}
```

## The LabelResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"LabelResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"label":{"$ref":"#/components/schemas/LabelTask"}}}}},"LabelTask":{"type":"object","properties":{"_id":{"type":"string"},"project":{"type":"string"},"asset":{"oneOf":[{"type":"string"},{"$ref":"#/components/schemas/Asset"}],"description":"Populated Asset object on list/detail responses; id string on annotate/review responses."},"stage":{"type":"string"},"status":{"type":"string"},"assignee":{"type":"string"},"externalId":{"type":"string"},"batches":{"type":"array","items":{"type":"string"}},"priority":{"type":"number"},"answer":{"type":"object"},"createdAt":{"type":"string","format":"date-time"},"duration":{"type":"number","description":"Duration in seconds"}}},"Asset":{"type":"object","description":"When an asset is private, carries a `storage`, or is NRRD, and its `data` is a URL, the read endpoints replace `data`, `dataset` and `overlay` with pre-signed URLs and set `head` to a signed HEAD URL. Otherwise `head` equals `data`.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"data":{"type":"string","description":"Asset URL"},"dataset":{"type":"array","items":{"type":"string"}},"overlay":{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"}}}]}},"head":{"type":"string","description":"Signed HEAD URL for the asset, or the same value as `data`"},"project":{"type":"string","description":"Project ID, or the project object when `populate=project` is used"},"organization":{"type":"string"},"storage":{"type":"string","description":"Storage integration ID, null for assets in the platform bucket","nullable":true},"externalId":{"type":"string"},"editorType":{"type":"string","enum":["ango","pct","llm","nrrd","iframe"]},"uploadStatus":{"type":"string","enum":["READY","PCT_WAITING","METADATA_WAITING","PCT_FAILED","METADATA_FAILED"],"description":"An asset is READY once it can be annotated. PCT and preprocessing assets get their task only after the waiting state clears."},"isPrivate":{"type":"boolean"},"isPreLabeled":{"type":"boolean"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs the asset belongs to"},"metadata":{"$ref":"#/components/schemas/AssetMetadata"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"label":{"type":"object"}}}},"contextData":{"type":"object"},"labelTasks":{"type":"array","description":"Returned only when populated, as objects carrying `_id`, `id`, `stage` and `assignee`. The project assets list always populates it.","items":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"stage":{"type":"string"},"assignee":{"type":"string"}}}},"deleted":{"type":"boolean","description":"Deleted assets are excluded from responses by default, so this is false"},"preprocessEnqueuedAt":{"type":"string","format":"date-time","description":"When the asset was queued for preprocessing"},"createdAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"}}},"AssetMetadata":{"type":"object","description":"Technical asset metadata. Only these fields are stored; any other key is dropped on write. For a video asset, supply width, height and frameTotal to skip server-side preprocessing.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"frameStep":{"type":"integer"},"frameTotal":{"type":"integer"},"frameRate":{"type":"number"},"numOfPages":{"type":"integer"},"numOfWords":{"type":"integer"},"duration":{"type":"integer","description":"Duration in milliseconds"},"hasVariableFrameRate":{"type":"boolean"}}}}}}
```

## The TaskHistory object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"TaskHistory":{"type":"object","properties":{"_id":{"type":"string"},"labelTaskId":{"type":"string","description":"Reference to the label task"},"project":{"type":"string"},"organization":{"type":"string"},"asset":{"$ref":"#/components/schemas/Asset"},"stage":{"type":"string"},"assignee":{"type":"string"},"prevAssignee":{"type":"string"},"reviewStatus":{"type":"string","enum":["Accepted","Rejected"]},"rework":{"type":"boolean"},"isSkipped":{"type":"boolean"},"type":{"type":"string"},"answer":{"type":"object"},"duration":{"type":"number"},"idleDuration":{"type":"number"},"blurDuration":{"type":"number"},"batches":{"type":"array","items":{"type":"string"}},"consensusAverage":{"type":"number"},"consensusDetailed":{"type":"array","items":{"type":"object"}},"consensusIteration":{"type":"integer"},"benchmarkAverage":{"type":"number"},"benchmarkDetailed":{"type":"array","items":{"type":"object"}},"brushDataUrl":{"type":"string"},"medicalBrushDataUrl":{"type":"string"},"rootStageId":{"type":"string"},"deleted":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"Asset":{"type":"object","description":"When an asset is private, carries a `storage`, or is NRRD, and its `data` is a URL, the read endpoints replace `data`, `dataset` and `overlay` with pre-signed URLs and set `head` to a signed HEAD URL. Otherwise `head` equals `data`.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"data":{"type":"string","description":"Asset URL"},"dataset":{"type":"array","items":{"type":"string"}},"overlay":{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"}}}]}},"head":{"type":"string","description":"Signed HEAD URL for the asset, or the same value as `data`"},"project":{"type":"string","description":"Project ID, or the project object when `populate=project` is used"},"organization":{"type":"string"},"storage":{"type":"string","description":"Storage integration ID, null for assets in the platform bucket","nullable":true},"externalId":{"type":"string"},"editorType":{"type":"string","enum":["ango","pct","llm","nrrd","iframe"]},"uploadStatus":{"type":"string","enum":["READY","PCT_WAITING","METADATA_WAITING","PCT_FAILED","METADATA_FAILED"],"description":"An asset is READY once it can be annotated. PCT and preprocessing assets get their task only after the waiting state clears."},"isPrivate":{"type":"boolean"},"isPreLabeled":{"type":"boolean"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs the asset belongs to"},"metadata":{"$ref":"#/components/schemas/AssetMetadata"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"label":{"type":"object"}}}},"contextData":{"type":"object"},"labelTasks":{"type":"array","description":"Returned only when populated, as objects carrying `_id`, `id`, `stage` and `assignee`. The project assets list always populates it.","items":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"stage":{"type":"string"},"assignee":{"type":"string"}}}},"deleted":{"type":"boolean","description":"Deleted assets are excluded from responses by default, so this is false"},"preprocessEnqueuedAt":{"type":"string","format":"date-time","description":"When the asset was queued for preprocessing"},"createdAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"}}},"AssetMetadata":{"type":"object","description":"Technical asset metadata. Only these fields are stored; any other key is dropped on write. For a video asset, supply width, height and frameTotal to skip server-side preprocessing.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"frameStep":{"type":"integer"},"frameTotal":{"type":"integer"},"frameRate":{"type":"number"},"numOfPages":{"type":"integer"},"numOfWords":{"type":"integer"},"duration":{"type":"integer","description":"Duration in milliseconds"},"hasVariableFrameRate":{"type":"boolean"}}}}}}
```

## The TaskHistoryListResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"TaskHistoryListResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"taskHistory":{"type":"array","items":{"$ref":"#/components/schemas/TaskHistory"}},"total":{"type":"integer"}}}}},"TaskHistory":{"type":"object","properties":{"_id":{"type":"string"},"labelTaskId":{"type":"string","description":"Reference to the label task"},"project":{"type":"string"},"organization":{"type":"string"},"asset":{"$ref":"#/components/schemas/Asset"},"stage":{"type":"string"},"assignee":{"type":"string"},"prevAssignee":{"type":"string"},"reviewStatus":{"type":"string","enum":["Accepted","Rejected"]},"rework":{"type":"boolean"},"isSkipped":{"type":"boolean"},"type":{"type":"string"},"answer":{"type":"object"},"duration":{"type":"number"},"idleDuration":{"type":"number"},"blurDuration":{"type":"number"},"batches":{"type":"array","items":{"type":"string"}},"consensusAverage":{"type":"number"},"consensusDetailed":{"type":"array","items":{"type":"object"}},"consensusIteration":{"type":"integer"},"benchmarkAverage":{"type":"number"},"benchmarkDetailed":{"type":"array","items":{"type":"object"}},"brushDataUrl":{"type":"string"},"medicalBrushDataUrl":{"type":"string"},"rootStageId":{"type":"string"},"deleted":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"Asset":{"type":"object","description":"When an asset is private, carries a `storage`, or is NRRD, and its `data` is a URL, the read endpoints replace `data`, `dataset` and `overlay` with pre-signed URLs and set `head` to a signed HEAD URL. Otherwise `head` equals `data`.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"data":{"type":"string","description":"Asset URL"},"dataset":{"type":"array","items":{"type":"string"}},"overlay":{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"}}}]}},"head":{"type":"string","description":"Signed HEAD URL for the asset, or the same value as `data`"},"project":{"type":"string","description":"Project ID, or the project object when `populate=project` is used"},"organization":{"type":"string"},"storage":{"type":"string","description":"Storage integration ID, null for assets in the platform bucket","nullable":true},"externalId":{"type":"string"},"editorType":{"type":"string","enum":["ango","pct","llm","nrrd","iframe"]},"uploadStatus":{"type":"string","enum":["READY","PCT_WAITING","METADATA_WAITING","PCT_FAILED","METADATA_FAILED"],"description":"An asset is READY once it can be annotated. PCT and preprocessing assets get their task only after the waiting state clears."},"isPrivate":{"type":"boolean"},"isPreLabeled":{"type":"boolean"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs the asset belongs to"},"metadata":{"$ref":"#/components/schemas/AssetMetadata"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"label":{"type":"object"}}}},"contextData":{"type":"object"},"labelTasks":{"type":"array","description":"Returned only when populated, as objects carrying `_id`, `id`, `stage` and `assignee`. The project assets list always populates it.","items":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"stage":{"type":"string"},"assignee":{"type":"string"}}}},"deleted":{"type":"boolean","description":"Deleted assets are excluded from responses by default, so this is false"},"preprocessEnqueuedAt":{"type":"string","format":"date-time","description":"When the asset was queued for preprocessing"},"createdAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"}}},"AssetMetadata":{"type":"object","description":"Technical asset metadata. Only these fields are stored; any other key is dropped on write. For a video asset, supply width, height and frameTotal to skip server-side preprocessing.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"frameStep":{"type":"integer"},"frameTotal":{"type":"integer"},"frameRate":{"type":"number"},"numOfPages":{"type":"integer"},"numOfWords":{"type":"integer"},"duration":{"type":"integer","description":"Duration in milliseconds"},"hasVariableFrameRate":{"type":"boolean"}}}}}}
```

## The TaskHistoryResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"TaskHistoryResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"taskHistory":{"$ref":"#/components/schemas/TaskHistory"}}}}},"TaskHistory":{"type":"object","properties":{"_id":{"type":"string"},"labelTaskId":{"type":"string","description":"Reference to the label task"},"project":{"type":"string"},"organization":{"type":"string"},"asset":{"$ref":"#/components/schemas/Asset"},"stage":{"type":"string"},"assignee":{"type":"string"},"prevAssignee":{"type":"string"},"reviewStatus":{"type":"string","enum":["Accepted","Rejected"]},"rework":{"type":"boolean"},"isSkipped":{"type":"boolean"},"type":{"type":"string"},"answer":{"type":"object"},"duration":{"type":"number"},"idleDuration":{"type":"number"},"blurDuration":{"type":"number"},"batches":{"type":"array","items":{"type":"string"}},"consensusAverage":{"type":"number"},"consensusDetailed":{"type":"array","items":{"type":"object"}},"consensusIteration":{"type":"integer"},"benchmarkAverage":{"type":"number"},"benchmarkDetailed":{"type":"array","items":{"type":"object"}},"brushDataUrl":{"type":"string"},"medicalBrushDataUrl":{"type":"string"},"rootStageId":{"type":"string"},"deleted":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"Asset":{"type":"object","description":"When an asset is private, carries a `storage`, or is NRRD, and its `data` is a URL, the read endpoints replace `data`, `dataset` and `overlay` with pre-signed URLs and set `head` to a signed HEAD URL. Otherwise `head` equals `data`.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"data":{"type":"string","description":"Asset URL"},"dataset":{"type":"array","items":{"type":"string"}},"overlay":{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"}}}]}},"head":{"type":"string","description":"Signed HEAD URL for the asset, or the same value as `data`"},"project":{"type":"string","description":"Project ID, or the project object when `populate=project` is used"},"organization":{"type":"string"},"storage":{"type":"string","description":"Storage integration ID, null for assets in the platform bucket","nullable":true},"externalId":{"type":"string"},"editorType":{"type":"string","enum":["ango","pct","llm","nrrd","iframe"]},"uploadStatus":{"type":"string","enum":["READY","PCT_WAITING","METADATA_WAITING","PCT_FAILED","METADATA_FAILED"],"description":"An asset is READY once it can be annotated. PCT and preprocessing assets get their task only after the waiting state clears."},"isPrivate":{"type":"boolean"},"isPreLabeled":{"type":"boolean"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs the asset belongs to"},"metadata":{"$ref":"#/components/schemas/AssetMetadata"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"label":{"type":"object"}}}},"contextData":{"type":"object"},"labelTasks":{"type":"array","description":"Returned only when populated, as objects carrying `_id`, `id`, `stage` and `assignee`. The project assets list always populates it.","items":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"stage":{"type":"string"},"assignee":{"type":"string"}}}},"deleted":{"type":"boolean","description":"Deleted assets are excluded from responses by default, so this is false"},"preprocessEnqueuedAt":{"type":"string","format":"date-time","description":"When the asset was queued for preprocessing"},"createdAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"}}},"AssetMetadata":{"type":"object","description":"Technical asset metadata. Only these fields are stored; any other key is dropped on write. For a video asset, supply width, height and frameTotal to skip server-side preprocessing.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"frameStep":{"type":"integer"},"frameTotal":{"type":"integer"},"frameRate":{"type":"number"},"numOfPages":{"type":"integer"},"numOfWords":{"type":"integer"},"duration":{"type":"integer","description":"Duration in milliseconds"},"hasVariableFrameRate":{"type":"boolean"}}}}}}
```

## The Asset object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"Asset":{"type":"object","description":"When an asset is private, carries a `storage`, or is NRRD, and its `data` is a URL, the read endpoints replace `data`, `dataset` and `overlay` with pre-signed URLs and set `head` to a signed HEAD URL. Otherwise `head` equals `data`.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"data":{"type":"string","description":"Asset URL"},"dataset":{"type":"array","items":{"type":"string"}},"overlay":{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"}}}]}},"head":{"type":"string","description":"Signed HEAD URL for the asset, or the same value as `data`"},"project":{"type":"string","description":"Project ID, or the project object when `populate=project` is used"},"organization":{"type":"string"},"storage":{"type":"string","description":"Storage integration ID, null for assets in the platform bucket","nullable":true},"externalId":{"type":"string"},"editorType":{"type":"string","enum":["ango","pct","llm","nrrd","iframe"]},"uploadStatus":{"type":"string","enum":["READY","PCT_WAITING","METADATA_WAITING","PCT_FAILED","METADATA_FAILED"],"description":"An asset is READY once it can be annotated. PCT and preprocessing assets get their task only after the waiting state clears."},"isPrivate":{"type":"boolean"},"isPreLabeled":{"type":"boolean"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs the asset belongs to"},"metadata":{"$ref":"#/components/schemas/AssetMetadata"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"label":{"type":"object"}}}},"contextData":{"type":"object"},"labelTasks":{"type":"array","description":"Returned only when populated, as objects carrying `_id`, `id`, `stage` and `assignee`. The project assets list always populates it.","items":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"stage":{"type":"string"},"assignee":{"type":"string"}}}},"deleted":{"type":"boolean","description":"Deleted assets are excluded from responses by default, so this is false"},"preprocessEnqueuedAt":{"type":"string","format":"date-time","description":"When the asset was queued for preprocessing"},"createdAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"}}},"AssetMetadata":{"type":"object","description":"Technical asset metadata. Only these fields are stored; any other key is dropped on write. For a video asset, supply width, height and frameTotal to skip server-side preprocessing.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"frameStep":{"type":"integer"},"frameTotal":{"type":"integer"},"frameRate":{"type":"number"},"numOfPages":{"type":"integer"},"numOfWords":{"type":"integer"},"duration":{"type":"integer","description":"Duration in milliseconds"},"hasVariableFrameRate":{"type":"boolean"}}}}}}
```

## The AssetListResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"AssetListResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"assets":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}},"total":{"type":"integer"}}}}},"Asset":{"type":"object","description":"When an asset is private, carries a `storage`, or is NRRD, and its `data` is a URL, the read endpoints replace `data`, `dataset` and `overlay` with pre-signed URLs and set `head` to a signed HEAD URL. Otherwise `head` equals `data`.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"data":{"type":"string","description":"Asset URL"},"dataset":{"type":"array","items":{"type":"string"}},"overlay":{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"}}}]}},"head":{"type":"string","description":"Signed HEAD URL for the asset, or the same value as `data`"},"project":{"type":"string","description":"Project ID, or the project object when `populate=project` is used"},"organization":{"type":"string"},"storage":{"type":"string","description":"Storage integration ID, null for assets in the platform bucket","nullable":true},"externalId":{"type":"string"},"editorType":{"type":"string","enum":["ango","pct","llm","nrrd","iframe"]},"uploadStatus":{"type":"string","enum":["READY","PCT_WAITING","METADATA_WAITING","PCT_FAILED","METADATA_FAILED"],"description":"An asset is READY once it can be annotated. PCT and preprocessing assets get their task only after the waiting state clears."},"isPrivate":{"type":"boolean"},"isPreLabeled":{"type":"boolean"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs the asset belongs to"},"metadata":{"$ref":"#/components/schemas/AssetMetadata"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"label":{"type":"object"}}}},"contextData":{"type":"object"},"labelTasks":{"type":"array","description":"Returned only when populated, as objects carrying `_id`, `id`, `stage` and `assignee`. The project assets list always populates it.","items":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"stage":{"type":"string"},"assignee":{"type":"string"}}}},"deleted":{"type":"boolean","description":"Deleted assets are excluded from responses by default, so this is false"},"preprocessEnqueuedAt":{"type":"string","format":"date-time","description":"When the asset was queued for preprocessing"},"createdAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"}}},"AssetMetadata":{"type":"object","description":"Technical asset metadata. Only these fields are stored; any other key is dropped on write. For a video asset, supply width, height and frameTotal to skip server-side preprocessing.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"frameStep":{"type":"integer"},"frameTotal":{"type":"integer"},"frameRate":{"type":"number"},"numOfPages":{"type":"integer"},"numOfWords":{"type":"integer"},"duration":{"type":"integer","description":"Duration in milliseconds"},"hasVariableFrameRate":{"type":"boolean"}}}}}}
```

## The AssetResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"AssetResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"asset":{"$ref":"#/components/schemas/Asset"}}}}},"Asset":{"type":"object","description":"When an asset is private, carries a `storage`, or is NRRD, and its `data` is a URL, the read endpoints replace `data`, `dataset` and `overlay` with pre-signed URLs and set `head` to a signed HEAD URL. Otherwise `head` equals `data`.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"data":{"type":"string","description":"Asset URL"},"dataset":{"type":"array","items":{"type":"string"}},"overlay":{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"}}}]}},"head":{"type":"string","description":"Signed HEAD URL for the asset, or the same value as `data`"},"project":{"type":"string","description":"Project ID, or the project object when `populate=project` is used"},"organization":{"type":"string"},"storage":{"type":"string","description":"Storage integration ID, null for assets in the platform bucket","nullable":true},"externalId":{"type":"string"},"editorType":{"type":"string","enum":["ango","pct","llm","nrrd","iframe"]},"uploadStatus":{"type":"string","enum":["READY","PCT_WAITING","METADATA_WAITING","PCT_FAILED","METADATA_FAILED"],"description":"An asset is READY once it can be annotated. PCT and preprocessing assets get their task only after the waiting state clears."},"isPrivate":{"type":"boolean"},"isPreLabeled":{"type":"boolean"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs the asset belongs to"},"metadata":{"$ref":"#/components/schemas/AssetMetadata"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"label":{"type":"object"}}}},"contextData":{"type":"object"},"labelTasks":{"type":"array","description":"Returned only when populated, as objects carrying `_id`, `id`, `stage` and `assignee`. The project assets list always populates it.","items":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"stage":{"type":"string"},"assignee":{"type":"string"}}}},"deleted":{"type":"boolean","description":"Deleted assets are excluded from responses by default, so this is false"},"preprocessEnqueuedAt":{"type":"string","format":"date-time","description":"When the asset was queued for preprocessing"},"createdAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"}}},"AssetMetadata":{"type":"object","description":"Technical asset metadata. Only these fields are stored; any other key is dropped on write. For a video asset, supply width, height and frameTotal to skip server-side preprocessing.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"frameStep":{"type":"integer"},"frameTotal":{"type":"integer"},"frameRate":{"type":"number"},"numOfPages":{"type":"integer"},"numOfWords":{"type":"integer"},"duration":{"type":"integer","description":"Duration in milliseconds"},"hasVariableFrameRate":{"type":"boolean"}}}}}}
```

## The UploadUrlResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"UploadUrlResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"uploadUrl":{"type":"string"}}}}}}}}
```

## The SignedUrlResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"SignedUrlResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"signedUrl":{"type":"string"}}}}}}}}
```

## The AssetMetadata object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"AssetMetadata":{"type":"object","description":"Technical asset metadata. Only these fields are stored; any other key is dropped on write. For a video asset, supply width, height and frameTotal to skip server-side preprocessing.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"frameStep":{"type":"integer"},"frameTotal":{"type":"integer"},"frameRate":{"type":"number"},"numOfPages":{"type":"integer"},"numOfWords":{"type":"integer"},"duration":{"type":"integer","description":"Duration in milliseconds"},"hasVariableFrameRate":{"type":"boolean"}}}}}}
```

## The CloudImportAsset object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"CloudImportAsset":{"type":"object","required":["data"],"properties":{"data":{"type":"string","description":"Cloud storage URL or path to the asset.\n- For images/videos: Full URL (e.g., `https://bucket.s3.amazonaws.com/image.jpg`)\n- For PCT (Point Cloud): Folder path in format `bucket-name/folder-path/` (must end with `/`)\n"},"dataset":{"type":"array","items":{"type":"string"},"description":"Alternative to data - array of URLs for multi-file assets (e.g., videos with frames)"},"_id":{"type":"string","description":"Asset ID to create the asset with, as a 24 character hex string. Must be unused and unique within the request; a duplicate inside one request returns 400. `id` is accepted as an alias."},"externalId":{"type":"string","maxLength":255,"description":"External identifier for the asset. At most 255 characters, longer or non-string values return 400. When omitted the server derives it as a hash of `data`, so it is not the data URL itself."},"storage":{"type":"string","description":"Storage integration ID (required for accessing private cloud buckets and PCT imports)"},"editorType":{"type":"string","enum":["ango","pct","llm","nrrd","iframe"],"description":"Asset editor type. Determines how the asset is processed and displayed.\n- `ango`: Standard AngoHub editor (default) - supports images, videos, PDFs, and more\n- `pct`: Point Cloud Tool - 3D point cloud annotation (requires folder path in `data`)\n- `llm`: LLM/Chat annotation editor\n- `nrrd`: Medical imaging (DICOM to NRRD conversion)\n- `iframe`: Custom iframe-based editor\n"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs to assign this asset to. Use the `_id` values from the project's `batches` array, not batch names."},"metadata":{"$ref":"#/components/schemas/AssetMetadata"},"overlay":{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"}}}]},"description":"Overlay images, either URLs or `{id, url}` objects"},"contextData":{"type":"object","description":"Additional context data for the asset"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"label":{"type":"object"}}},"description":"Extra material shown next to the asset"},"priority":{"type":"number","minimum":-1000,"maximum":1000,"description":"Task priority for this asset, used only when the request-level `priority` is absent. It is keyed by `externalId`, so assets sharing one `externalId` share one priority, and it reaches only the tasks created at import time."},"tools":{"type":"array","items":{"type":"object"},"description":"Pre-label annotation objects (bounding boxes, polygons, and so on). Each object carries the `schemaId` of a tool in the project category schema. Pre-labels are applied only to assets that are ready at import time; an asset queued for preprocessing (a video without `metadata.width`, `metadata.height` and `metadata.frameTotal`, a DICOM series, or a PCT asset) gets its task later and keeps no pre-label."},"objects":{"type":"array","items":{"type":"object"},"description":"Alias for `tools`, read only when `tools` is absent."},"classifications":{"type":"array","items":{"type":"object"},"description":"Pre-label classification answers"},"relations":{"type":"array","items":{"type":"object"},"description":"Pre-label relation objects"},"brushDataUrl":{"type":"string","description":"URL of an uploaded brush mask to pre-label a brush annotation"},"medicalBrushDataUrl":{"type":"string","description":"URL of an uploaded medical brush mask"}}},"AssetMetadata":{"type":"object","description":"Technical asset metadata. Only these fields are stored; any other key is dropped on write. For a video asset, supply width, height and frameTotal to skip server-side preprocessing.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"frameStep":{"type":"integer"},"frameTotal":{"type":"integer"},"frameRate":{"type":"number"},"numOfPages":{"type":"integer"},"numOfWords":{"type":"integer"},"duration":{"type":"integer","description":"Duration in milliseconds"},"hasVariableFrameRate":{"type":"boolean"}}}}}}
```

## The CloudImportRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"CloudImportRequest":{"type":"object","required":["assets"],"properties":{"assets":{"type":"array","items":{"$ref":"#/components/schemas/CloudImportAsset"}},"uploadLocal":{"type":"string","enum":["true","false"],"description":"\"true\" marks the created assets private. Nothing is downloaded, the files stay where they are. Read endpoints return pre-signed URLs when an asset is private, carries a `storage`, or is NRRD, so an asset with a `storage` gets signed URLs either way.","default":"false"},"priority":{"type":"number","minimum":-1000,"maximum":1000,"description":"Task priority for queue ordering (higher values first). Applies to every asset in the request and wins over a per-asset `priority`, including when it is 0.","default":0}}},"CloudImportAsset":{"type":"object","required":["data"],"properties":{"data":{"type":"string","description":"Cloud storage URL or path to the asset.\n- For images/videos: Full URL (e.g., `https://bucket.s3.amazonaws.com/image.jpg`)\n- For PCT (Point Cloud): Folder path in format `bucket-name/folder-path/` (must end with `/`)\n"},"dataset":{"type":"array","items":{"type":"string"},"description":"Alternative to data - array of URLs for multi-file assets (e.g., videos with frames)"},"_id":{"type":"string","description":"Asset ID to create the asset with, as a 24 character hex string. Must be unused and unique within the request; a duplicate inside one request returns 400. `id` is accepted as an alias."},"externalId":{"type":"string","maxLength":255,"description":"External identifier for the asset. At most 255 characters, longer or non-string values return 400. When omitted the server derives it as a hash of `data`, so it is not the data URL itself."},"storage":{"type":"string","description":"Storage integration ID (required for accessing private cloud buckets and PCT imports)"},"editorType":{"type":"string","enum":["ango","pct","llm","nrrd","iframe"],"description":"Asset editor type. Determines how the asset is processed and displayed.\n- `ango`: Standard AngoHub editor (default) - supports images, videos, PDFs, and more\n- `pct`: Point Cloud Tool - 3D point cloud annotation (requires folder path in `data`)\n- `llm`: LLM/Chat annotation editor\n- `nrrd`: Medical imaging (DICOM to NRRD conversion)\n- `iframe`: Custom iframe-based editor\n"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs to assign this asset to. Use the `_id` values from the project's `batches` array, not batch names."},"metadata":{"$ref":"#/components/schemas/AssetMetadata"},"overlay":{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"}}}]},"description":"Overlay images, either URLs or `{id, url}` objects"},"contextData":{"type":"object","description":"Additional context data for the asset"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"label":{"type":"object"}}},"description":"Extra material shown next to the asset"},"priority":{"type":"number","minimum":-1000,"maximum":1000,"description":"Task priority for this asset, used only when the request-level `priority` is absent. It is keyed by `externalId`, so assets sharing one `externalId` share one priority, and it reaches only the tasks created at import time."},"tools":{"type":"array","items":{"type":"object"},"description":"Pre-label annotation objects (bounding boxes, polygons, and so on). Each object carries the `schemaId` of a tool in the project category schema. Pre-labels are applied only to assets that are ready at import time; an asset queued for preprocessing (a video without `metadata.width`, `metadata.height` and `metadata.frameTotal`, a DICOM series, or a PCT asset) gets its task later and keeps no pre-label."},"objects":{"type":"array","items":{"type":"object"},"description":"Alias for `tools`, read only when `tools` is absent."},"classifications":{"type":"array","items":{"type":"object"},"description":"Pre-label classification answers"},"relations":{"type":"array","items":{"type":"object"},"description":"Pre-label relation objects"},"brushDataUrl":{"type":"string","description":"URL of an uploaded brush mask to pre-label a brush annotation"},"medicalBrushDataUrl":{"type":"string","description":"URL of an uploaded medical brush mask"}}},"AssetMetadata":{"type":"object","description":"Technical asset metadata. Only these fields are stored; any other key is dropped on write. For a video asset, supply width, height and frameTotal to skip server-side preprocessing.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"frameStep":{"type":"integer"},"frameTotal":{"type":"integer"},"frameRate":{"type":"number"},"numOfPages":{"type":"integer"},"numOfWords":{"type":"integer"},"duration":{"type":"integer","description":"Duration in milliseconds"},"hasVariableFrameRate":{"type":"boolean"}}}}}}
```

## The AssetImportResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"AssetImportResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"assets":{"type":"object","properties":{"assetsCreated":{"type":"integer"},"tasksCreated":{"type":"integer"},"preLabelsCount":{"type":"integer"},"validPreLabelsCount":{"type":"integer"}}}}}}}}}}
```

## The ChatAssetUploadRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ChatAssetUploadRequest":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"JSON file holding the conversations to import"},"storageId":{"type":"string","description":"Storage integration ID. Send an empty value to use the platform bucket."},"bucket":{"type":"string"},"batches":{"type":"string","description":"JSON stringified array of batch IDs, applied to every conversation asset created by this request."},"llmConfig":{"type":"string","description":"LLM configuration JSON"},"numberOfConversations":{"type":"number","description":"How many conversations to create when no file is uploaded"},"namingStrategy":{"type":"string","description":"Accepted for backward compatibility, has no effect"},"priority":{"type":"number"}}}}}}
```

## The AssetUpdateRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"AssetUpdateRequest":{"type":"object","description":"The fields below are the supported ones. The endpoint applies the request body to the asset without validation, so any other Asset field is writable as well. The response carries the updated asset with its URLs re-signed.","properties":{"metadata":{"$ref":"#/components/schemas/AssetMetadata"},"contextData":{"type":"object"},"data":{"type":"string"},"overlay":{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"}}}]}},"externalId":{"type":"string"}}},"AssetMetadata":{"type":"object","description":"Technical asset metadata. Only these fields are stored; any other key is dropped on write. For a video asset, supply width, height and frameTotal to skip server-side preprocessing.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"frameStep":{"type":"integer"},"frameTotal":{"type":"integer"},"frameRate":{"type":"number"},"numOfPages":{"type":"integer"},"numOfWords":{"type":"integer"},"duration":{"type":"integer","description":"Duration in milliseconds"},"hasVariableFrameRate":{"type":"boolean"}}}}}}
```

## The BulkDeleteAssetsRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"BulkDeleteAssetsRequest":{"type":"object","required":["assets"],"properties":{"assets":{"type":"array","items":{"type":"string"}}}}}}}
```

## The AssetBulkDeleteResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"AssetBulkDeleteResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"assets":{"type":"array","description":"The asset IDs from the request, echoed back, including IDs that did not exist","items":{"type":"string"}}}}}}}}}
```

## The AssignBatchesRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"AssignBatchesRequest":{"type":"object","required":["assets","batches","projectId"],"properties":{"assets":{"type":"array","items":{"type":"string"},"description":"Asset IDs to update"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs to set on the assets. Use the `_id` values from the project's `batches` array, not batch names; an unknown ID fails the whole request with 400."},"projectId":{"type":"string","description":"Project the batches belong to"}}}}}}
```

## The AssignBatchesResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"AssignBatchesResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"assets":{"type":"object","properties":{"count":{"type":"integer","description":"Number of assets updated"},"message":{"type":"string"}}}}}}}}}}
```

## The LLM object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"LLM":{"type":"object","description":"`configuration` is stored hidden but these endpoints put `apiKey` back into the response, so the payload contains the provider credential.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"name":{"type":"string"},"provider":{"type":"string","enum":["openrouter","openai","pluto","llama-meta","openai_api_compatible"]},"model":{"type":"string","nullable":true},"isStreamable":{"type":"boolean"},"configuration":{"type":"object","properties":{"apiKey":{"type":"string","description":"Provider API key"},"baseUrl":{"type":"string","description":"Present only when the integration was created with one"}}},"organization":{"type":"string"},"createdBy":{"type":"string","description":"Email of the member who created the integration"},"createdAt":{"type":"string","format":"date-time"},"deleted":{"type":"boolean"}}}}}}
```

## The LLMListResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"LLMListResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"llms":{"type":"array","items":{"$ref":"#/components/schemas/LLM"}},"total":{"type":"integer","description":"Number of integrations matching the filter, ignoring pagination"}}}}},"LLM":{"type":"object","description":"`configuration` is stored hidden but these endpoints put `apiKey` back into the response, so the payload contains the provider credential.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"name":{"type":"string"},"provider":{"type":"string","enum":["openrouter","openai","pluto","llama-meta","openai_api_compatible"]},"model":{"type":"string","nullable":true},"isStreamable":{"type":"boolean"},"configuration":{"type":"object","properties":{"apiKey":{"type":"string","description":"Provider API key"},"baseUrl":{"type":"string","description":"Present only when the integration was created with one"}}},"organization":{"type":"string"},"createdBy":{"type":"string","description":"Email of the member who created the integration"},"createdAt":{"type":"string","format":"date-time"},"deleted":{"type":"boolean"}}}}}}
```

## The LLMResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"LLMResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"llm":{"$ref":"#/components/schemas/LLM"}}}}},"LLM":{"type":"object","description":"`configuration` is stored hidden but these endpoints put `apiKey` back into the response, so the payload contains the provider credential.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"name":{"type":"string"},"provider":{"type":"string","enum":["openrouter","openai","pluto","llama-meta","openai_api_compatible"]},"model":{"type":"string","nullable":true},"isStreamable":{"type":"boolean"},"configuration":{"type":"object","properties":{"apiKey":{"type":"string","description":"Provider API key"},"baseUrl":{"type":"string","description":"Present only when the integration was created with one"}}},"organization":{"type":"string"},"createdBy":{"type":"string","description":"Email of the member who created the integration"},"createdAt":{"type":"string","format":"date-time"},"deleted":{"type":"boolean"}}}}}}
```

## The LLMCreateRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"LLMCreateRequest":{"type":"object","required":["name","provider"],"properties":{"name":{"type":"string"},"provider":{"type":"string","enum":["openai","openrouter"],"description":"Other values are stored without complaint but have no runtime implementation, so the integration fails the first time a project uses it."},"model":{"type":"string","description":"Provider model id, for example `gpt-4o`"},"isStreamable":{"type":"boolean","default":false},"configuration":{"type":"object","description":"Provider credentials. Only `apiKey` and `baseUrl` come back in responses.","properties":{"apiKey":{"type":"string"},"baseUrl":{"type":"string","description":"Base URL of the provider endpoint, when it is not the provider default"}}}}}}}}
```

## The SendNotificationRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"SendNotificationRequest":{"type":"object","required":["receivers","message"],"properties":{"receivers":{"type":"array","minItems":1,"maxItems":100,"description":"Emails of organization members. Lowercased and de-duplicated before sending.","items":{"type":"string","format":"email"}},"message":{"type":"string","maxLength":1000},"title":{"type":"string","maxLength":200,"description":"Rendered in bold above the message"}}}}}}
```

## The NotificationResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"NotificationResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"notification":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"sender":{"type":"string"},"receivers":{"type":"array","items":{"type":"string"}},"title":{"type":"string"},"actionType":{"type":"string"},"description":{"type":"string","description":"Title and message, HTML escaped and joined by a line break"},"parentType":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}}}}}}}}}
```

## The BenchmarkTasksRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"BenchmarkTasksRequest":{"type":"object","required":["tasks","isBenchmark"],"properties":{"tasks":{"type":"array","minItems":1,"description":"Task IDs, each a 24 character hex string","items":{"type":"string"}},"isBenchmark":{"type":"boolean","description":"True marks the tasks as benchmark, false reverts them"}}}}}}
```

## The BenchmarkTasksResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"BenchmarkTasksResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"benchmarkTasks":{"type":"object","properties":{"isBenchmark":{"type":"boolean"},"modifiedCount":{"type":"integer","description":"Tasks actually changed, skipped ones are not counted"},"tasks":{"type":"array","description":"IDs of the tasks that were changed","items":{"type":"string"}}}}}}}}}}}
```

## The CloneProjectRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"CloneProjectRequest":{"type":"object","description":"Every flag defaults to true when omitted.","properties":{"categorySchema":{"type":"boolean","default":true},"assignedTo":{"type":"boolean","default":true,"description":"Copy the project members and their roles"},"batches":{"type":"boolean","default":true},"workflow":{"type":"boolean","default":true,"description":"Copy the workflow stages"},"pluginPresets":{"type":"boolean","default":true},"projectGeneralSettings":{"type":"boolean","default":true,"description":"Copy idle timeout, benchmark ratio and cutoff, export storage settings, error codes, OCR language and the queue and skip options. The benchmark on and off switch is copied either way."}}}}}}
```

## The Issue object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"Issue":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"content":{"type":"string"},"project":{"type":"string"},"organization":{"type":"string"},"asset":{"type":"string"},"labelTask":{"type":"string"},"status":{"type":"string","enum":["Open","Resolved","Closed"]},"createdBy":{"type":"string"},"assignee":{"type":"string"},"labeler":{"type":"string","description":"Assignee of the task at the time the issue was created"},"stage":{"type":"string","description":"Stage the task was in when the issue was created"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs inherited from the task"},"createdFromApi":{"type":"boolean","description":"True when the issue was created through this API"},"hasBeenSeen":{"type":"boolean"},"errorType":{"type":"string"},"errorCode":{"type":"string"},"severity":{"type":"string","enum":["High","Medium","Low"]},"severityLevel":{"type":"integer","description":"1 to 5. Values written through this API are validated, issues created elsewhere may carry other values."},"page":{"type":"integer","description":"Page index, sent for PDF and multi-document assets"},"pageEnd":{"type":"integer","description":"Last page or frame the issue spans"},"dataIndex":{"type":"integer","description":"Index into the asset `dataset` the issue points at"},"plane":{"type":"string"},"objectId":{"type":"string","description":"Annotation object the issue is attached to"},"schemaId":{"type":"string","description":"Category schema entry the issue is attached to"},"linkedObjectId":{"type":"string"},"position":{"type":"string"},"points":{"type":"array","items":{"type":"object"}},"contentMentions":{"type":"array","items":{"type":"object","properties":{"userEmail":{"type":"string"},"userName":{"type":"string"}}}},"comments":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"content":{"type":"string"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contentMentions":{"type":"array","items":{"type":"object","properties":{"userEmail":{"type":"string"},"userName":{"type":"string"}}}}}}},"errorCodes":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"},"name":{"type":"string"},"key":{"type":"string"}}}},"pctDetails":{"type":"object","description":"PCT-specific issue details containing positional metadata, severity,\nreference source, and optional marker information for navigating to\na point inside a 3D/2D scene.\n","properties":{"attribute":{"type":"string","nullable":true,"description":"Attribute ID from configuration."},"class":{"type":"string","nullable":true,"description":"Class ID from configuration."},"frame":{"type":"integer","description":"Starting frame number of the issue."},"endInputFrame":{"type":"integer","description":"Last frame where the issue persists."},"instance":{"type":"integer","description":"Class instance index."},"severity":{"type":"integer","enum":[2,3,4],"description":"Issue severity: 2=High, 3=Medium, 4=Low"},"referenceIndex":{"type":"integer","description":"-1 for 3D sensor, 0+ for 2D camera sensor index"},"marker":{"type":"object","description":"Marker for navigating to the issue location.","properties":{"type":{"type":"string","enum":["Marker 3D","Marker 2D"]},"value":{"type":"object","properties":{"geometry":{"type":"object","properties":{"position":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}},"rotation":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}},"boxSize":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}}}}}}}}}},"resolvedBy":{"type":"string"},"resolvedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"},"updatedAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}}}}
```

## The IssueListResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"IssueListResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"issue":{"type":"array","items":{"$ref":"#/components/schemas/Issue"}},"total":{"type":"integer","description":"Number of issues matching the filter, ignoring pagination"}}}}},"Issue":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"content":{"type":"string"},"project":{"type":"string"},"organization":{"type":"string"},"asset":{"type":"string"},"labelTask":{"type":"string"},"status":{"type":"string","enum":["Open","Resolved","Closed"]},"createdBy":{"type":"string"},"assignee":{"type":"string"},"labeler":{"type":"string","description":"Assignee of the task at the time the issue was created"},"stage":{"type":"string","description":"Stage the task was in when the issue was created"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs inherited from the task"},"createdFromApi":{"type":"boolean","description":"True when the issue was created through this API"},"hasBeenSeen":{"type":"boolean"},"errorType":{"type":"string"},"errorCode":{"type":"string"},"severity":{"type":"string","enum":["High","Medium","Low"]},"severityLevel":{"type":"integer","description":"1 to 5. Values written through this API are validated, issues created elsewhere may carry other values."},"page":{"type":"integer","description":"Page index, sent for PDF and multi-document assets"},"pageEnd":{"type":"integer","description":"Last page or frame the issue spans"},"dataIndex":{"type":"integer","description":"Index into the asset `dataset` the issue points at"},"plane":{"type":"string"},"objectId":{"type":"string","description":"Annotation object the issue is attached to"},"schemaId":{"type":"string","description":"Category schema entry the issue is attached to"},"linkedObjectId":{"type":"string"},"position":{"type":"string"},"points":{"type":"array","items":{"type":"object"}},"contentMentions":{"type":"array","items":{"type":"object","properties":{"userEmail":{"type":"string"},"userName":{"type":"string"}}}},"comments":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"content":{"type":"string"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contentMentions":{"type":"array","items":{"type":"object","properties":{"userEmail":{"type":"string"},"userName":{"type":"string"}}}}}}},"errorCodes":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"},"name":{"type":"string"},"key":{"type":"string"}}}},"pctDetails":{"type":"object","description":"PCT-specific issue details containing positional metadata, severity,\nreference source, and optional marker information for navigating to\na point inside a 3D/2D scene.\n","properties":{"attribute":{"type":"string","nullable":true,"description":"Attribute ID from configuration."},"class":{"type":"string","nullable":true,"description":"Class ID from configuration."},"frame":{"type":"integer","description":"Starting frame number of the issue."},"endInputFrame":{"type":"integer","description":"Last frame where the issue persists."},"instance":{"type":"integer","description":"Class instance index."},"severity":{"type":"integer","enum":[2,3,4],"description":"Issue severity: 2=High, 3=Medium, 4=Low"},"referenceIndex":{"type":"integer","description":"-1 for 3D sensor, 0+ for 2D camera sensor index"},"marker":{"type":"object","description":"Marker for navigating to the issue location.","properties":{"type":{"type":"string","enum":["Marker 3D","Marker 2D"]},"value":{"type":"object","properties":{"geometry":{"type":"object","properties":{"position":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}},"rotation":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}},"boxSize":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}}}}}}}}}},"resolvedBy":{"type":"string"},"resolvedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"},"updatedAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}}}}
```

## The IssueResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"IssueResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"issue":{"$ref":"#/components/schemas/Issue"}}}}},"Issue":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"content":{"type":"string"},"project":{"type":"string"},"organization":{"type":"string"},"asset":{"type":"string"},"labelTask":{"type":"string"},"status":{"type":"string","enum":["Open","Resolved","Closed"]},"createdBy":{"type":"string"},"assignee":{"type":"string"},"labeler":{"type":"string","description":"Assignee of the task at the time the issue was created"},"stage":{"type":"string","description":"Stage the task was in when the issue was created"},"batches":{"type":"array","items":{"type":"string"},"description":"Batch IDs inherited from the task"},"createdFromApi":{"type":"boolean","description":"True when the issue was created through this API"},"hasBeenSeen":{"type":"boolean"},"errorType":{"type":"string"},"errorCode":{"type":"string"},"severity":{"type":"string","enum":["High","Medium","Low"]},"severityLevel":{"type":"integer","description":"1 to 5. Values written through this API are validated, issues created elsewhere may carry other values."},"page":{"type":"integer","description":"Page index, sent for PDF and multi-document assets"},"pageEnd":{"type":"integer","description":"Last page or frame the issue spans"},"dataIndex":{"type":"integer","description":"Index into the asset `dataset` the issue points at"},"plane":{"type":"string"},"objectId":{"type":"string","description":"Annotation object the issue is attached to"},"schemaId":{"type":"string","description":"Category schema entry the issue is attached to"},"linkedObjectId":{"type":"string"},"position":{"type":"string"},"points":{"type":"array","items":{"type":"object"}},"contentMentions":{"type":"array","items":{"type":"object","properties":{"userEmail":{"type":"string"},"userName":{"type":"string"}}}},"comments":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"content":{"type":"string"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contentMentions":{"type":"array","items":{"type":"object","properties":{"userEmail":{"type":"string"},"userName":{"type":"string"}}}}}}},"errorCodes":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"},"name":{"type":"string"},"key":{"type":"string"}}}},"pctDetails":{"type":"object","description":"PCT-specific issue details containing positional metadata, severity,\nreference source, and optional marker information for navigating to\na point inside a 3D/2D scene.\n","properties":{"attribute":{"type":"string","nullable":true,"description":"Attribute ID from configuration."},"class":{"type":"string","nullable":true,"description":"Class ID from configuration."},"frame":{"type":"integer","description":"Starting frame number of the issue."},"endInputFrame":{"type":"integer","description":"Last frame where the issue persists."},"instance":{"type":"integer","description":"Class instance index."},"severity":{"type":"integer","enum":[2,3,4],"description":"Issue severity: 2=High, 3=Medium, 4=Low"},"referenceIndex":{"type":"integer","description":"-1 for 3D sensor, 0+ for 2D camera sensor index"},"marker":{"type":"object","description":"Marker for navigating to the issue location.","properties":{"type":{"type":"string","enum":["Marker 3D","Marker 2D"]},"value":{"type":"object","properties":{"geometry":{"type":"object","properties":{"position":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}},"rotation":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}},"boxSize":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}}}}}}}}}},"resolvedBy":{"type":"string"},"resolvedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"},"updatedAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}}}}
```

## The IssueDeleteResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"IssueDeleteResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"issue":{"type":"string","description":"Fixed confirmation string, not an issue object"}}}}}}}}
```

## The IssueCreateRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"IssueCreateRequest":{"type":"object","description":"The issue inherits `project`, `asset`, `labeler`, `stage` and `batches` from the task, and the server sets `createdBy`, `organization` and `createdFromApi`; sending those in the body has no effect. Creating an issue notifies the task assignee and, when set, the issue assignee, skipping whichever of them is the caller.","required":["labelTask"],"properties":{"labelTask":{"type":"string","description":"Task ID to create issue for"},"content":{"type":"string"},"contentMentions":{"type":"array","items":{"type":"object","properties":{"userEmail":{"type":"string"},"userName":{"type":"string"}}}},"assignee":{"type":"string","description":"Email of the user the issue is assigned to. An unknown email returns 404."},"position":{"type":"string"},"points":{"type":"array","items":{"type":"object"}},"errorType":{"type":"string"},"errorCode":{"type":"string"},"errorCodes":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"},"name":{"type":"string"},"key":{"type":"string"}}}},"severity":{"type":"string","enum":["High","Medium","Low"]},"severityLevel":{"type":"integer","minimum":1,"maximum":5,"description":"A value outside 1 to 5, or a non-numeric one, returns 400."},"page":{"type":"integer","description":"Page index, sent for PDF and multi-document assets"},"pageEnd":{"type":"integer","description":"Last page or frame the issue spans"},"dataIndex":{"type":"integer","description":"Index into the asset `dataset` the issue points at"},"plane":{"type":"string"},"objectId":{"type":"string","description":"Annotation object the issue is attached to"},"schemaId":{"type":"string","description":"Category schema entry the issue is attached to"},"linkedObjectId":{"type":"string"},"pctDetails":{"type":"object","description":"PCT-specific issue details containing positional metadata, severity,\nreference source, and optional marker information for navigating to\na point inside a 3D/2D scene.\n","properties":{"attribute":{"type":"string","nullable":true,"description":"Attribute ID from configuration. Can be null or omitted if not assigned."},"class":{"type":"string","nullable":true,"description":"Class ID from configuration. Can be null or omitted if not available."},"frame":{"type":"integer","default":1,"description":"Starting frame number of the issue. Defaults to 1."},"endInputFrame":{"type":"integer","default":1,"description":"Last frame where the issue persists. Defaults to 1."},"instance":{"type":"integer","default":-1,"description":"Class instance index. Defaults to -1."},"severity":{"type":"integer","enum":[2,3,4],"description":"Issue severity: 2=High, 3=Medium, 4=Low"},"referenceIndex":{"type":"integer","default":-1,"description":"-1 for 3D sensor, 0+ for 2D camera sensor index"},"marker":{"type":"object","description":"Optional marker for navigating to the issue location.","properties":{"type":{"type":"string","enum":["Marker 3D","Marker 2D"]},"value":{"type":"object","properties":{"geometry":{"type":"object","properties":{"position":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}},"rotation":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}},"boxSize":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}}}}}}}}}}}}}}}
```

## The IssueUpdateRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"IssueUpdateRequest":{"type":"object","description":"All fields are optional. Only `content`, `contentMentions`, `status`, `pctDetails` and `errorCodes` are applied, any other field in the body is ignored, and `updatedBy` and `updatedAt` are stamped by the server.","properties":{"content":{"type":"string","description":"Updated issue content/description"},"contentMentions":{"type":"array","description":"Users mentioned in the content. Applied only when `content` is sent in the same request, and it replaces the existing list, so send the full list with every content update.","items":{"type":"object","properties":{"userEmail":{"type":"string"},"userName":{"type":"string"}}}},"status":{"type":"string","enum":["Open","Resolved"],"description":"Only these two values are accepted, anything else returns 400. `Closed` can be filtered on but not set here. Moving to `Resolved` stamps `resolvedBy` and `resolvedAt`."},"pctDetails":{"type":"object","description":"PCT-specific issue details containing positional metadata, severity,\nreference source, and optional marker information for navigating to\na point inside a 3D/2D scene.\n","properties":{"attribute":{"type":"string","nullable":true,"description":"Attribute ID from configuration. Can be null or omitted if not assigned."},"class":{"type":"string","nullable":true,"description":"Class ID from configuration. Can be null or omitted if not available."},"frame":{"type":"integer","default":1,"description":"Starting frame number of the issue. Defaults to 1."},"endInputFrame":{"type":"integer","default":1,"description":"Last frame where the issue persists. Defaults to 1."},"instance":{"type":"integer","default":-1,"description":"Class instance index. Defaults to -1."},"severity":{"type":"integer","enum":[2,3,4],"description":"Issue severity: 2=High, 3=Medium, 4=Low"},"referenceIndex":{"type":"integer","default":-1,"description":"-1 for 3D sensor, 0+ for 2D camera sensor index"},"marker":{"type":"object","description":"Optional marker for navigating to the issue location.","properties":{"type":{"type":"string","enum":["Marker 3D","Marker 2D"]},"value":{"type":"object","properties":{"geometry":{"type":"object","properties":{"position":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}},"rotation":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}},"boxSize":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}}}}}}}}}},"errorCodes":{"type":"array","description":"Error codes associated with the issue","items":{"type":"object","properties":{"category":{"type":"string"},"name":{"type":"string"},"key":{"type":"string"}}}}}}}}}
```

## The IssueCommentRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"IssueCommentRequest":{"type":"object","required":["content"],"properties":{"content":{"type":"string","description":"The comment text content. This route runs no server-side validation, so the requirement is a client contract only."},"contentMentions":{"type":"array","description":"Users mentioned in the comment","items":{"type":"object","properties":{"userEmail":{"type":"string"},"userName":{"type":"string"}}}}}}}}}
```

## The Storage object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"Storage":{"type":"object","properties":{"_id":{"type":"string"},"name":{"type":"string"},"provider":{"type":"string","enum":["AWS","GCP","AZURE","CUSTOM_S3"]},"region":{"type":"string"},"s3AccessType":{"type":"string","enum":["IAM_USER","IAM_DELEGATED"]},"s3RoleArn":{"type":"string"},"s3ExternalId":{"type":"string"},"s3UserArn":{"type":"string"},"host":{"type":"string"},"azureAuthType":{"type":"string","enum":["ACCOUNT_KEY","SERVICE_PRINCIPAL","MULTI_TENANT"]},"azureTenantId":{"type":"string"},"azureClientId":{"type":"string"},"publicKey":{"type":"string"},"useAccelerateEndpoint":{"type":"boolean"},"organization":{"type":"string"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}}}}}
```

## The StorageListResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"StorageListResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"storages":{"type":"array","items":{"$ref":"#/components/schemas/Storage"}}}}}},"Storage":{"type":"object","properties":{"_id":{"type":"string"},"name":{"type":"string"},"provider":{"type":"string","enum":["AWS","GCP","AZURE","CUSTOM_S3"]},"region":{"type":"string"},"s3AccessType":{"type":"string","enum":["IAM_USER","IAM_DELEGATED"]},"s3RoleArn":{"type":"string"},"s3ExternalId":{"type":"string"},"s3UserArn":{"type":"string"},"host":{"type":"string"},"azureAuthType":{"type":"string","enum":["ACCOUNT_KEY","SERVICE_PRINCIPAL","MULTI_TENANT"]},"azureTenantId":{"type":"string"},"azureClientId":{"type":"string"},"publicKey":{"type":"string"},"useAccelerateEndpoint":{"type":"boolean"},"organization":{"type":"string"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}}}}}
```

## The StorageResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"StorageResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"storage":{"$ref":"#/components/schemas/Storage"}}}}},"Storage":{"type":"object","properties":{"_id":{"type":"string"},"name":{"type":"string"},"provider":{"type":"string","enum":["AWS","GCP","AZURE","CUSTOM_S3"]},"region":{"type":"string"},"s3AccessType":{"type":"string","enum":["IAM_USER","IAM_DELEGATED"]},"s3RoleArn":{"type":"string"},"s3ExternalId":{"type":"string"},"s3UserArn":{"type":"string"},"host":{"type":"string"},"azureAuthType":{"type":"string","enum":["ACCOUNT_KEY","SERVICE_PRINCIPAL","MULTI_TENANT"]},"azureTenantId":{"type":"string"},"azureClientId":{"type":"string"},"publicKey":{"type":"string"},"useAccelerateEndpoint":{"type":"boolean"},"organization":{"type":"string"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}}}}}
```

## The StorageCreateRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"StorageCreateRequest":{"type":"object","required":["name","provider"],"properties":{"name":{"type":"string","description":"Storage configuration display name"},"provider":{"type":"string","enum":["AWS","GCP","AZURE","CUSTOM_S3"]},"region":{"type":"string","default":"eu-central-1","description":"AWS only. Bucket region."},"publicKey":{"type":"string","description":"Access key ID for AWS (IAM user) and CUSTOM_S3. For AZURE this is the storage account name (required for all Azure auth types)."},"privateKey":{"type":"string","description":"Secret access key for AWS (IAM user) and CUSTOM_S3. For AZURE (ACCOUNT_KEY) this is the account key. Write-only, never returned."},"s3AccessType":{"type":"string","enum":["IAM_USER","IAM_DELEGATED"],"default":"IAM_USER","description":"AWS only. IAM_DELEGATED uses STS AssumeRole instead of static keys."},"s3RoleArn":{"type":"string","description":"AWS IAM_DELEGATED only. Role ARN to assume."},"s3ExternalId":{"type":"string","description":"AWS IAM_DELEGATED only. External ID for the AssumeRole trust policy."},"host":{"type":"string","description":"CUSTOM_S3 only (required). S3-compatible endpoint URL."},"credentials":{"type":"object","description":"GCP only. Service-account JSON object. Write-only, never returned."},"azureAuthType":{"type":"string","enum":["ACCOUNT_KEY","SERVICE_PRINCIPAL","MULTI_TENANT"],"default":"ACCOUNT_KEY","description":"AZURE only."},"azureTenantId":{"type":"string","description":"AZURE SERVICE_PRINCIPAL and MULTI_TENANT."},"azureClientId":{"type":"string","description":"AZURE SERVICE_PRINCIPAL only."},"azureClientSecret":{"type":"string","description":"AZURE SERVICE_PRINCIPAL only. Write-only, never returned."},"useAccelerateEndpoint":{"type":"boolean","default":false,"description":"AWS only. Use S3 Transfer Acceleration."}}}}}}
```

## The StorageFilesResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"StorageFilesResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"files":{"type":"array","description":"Flat list of object keys, or pre-signed URLs when getSignedUrls=true (default).","items":{"type":"string"}}}}}}}}}
```

## The User object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"User":{"type":"object","description":"Credentials and tokens (password, API key, reset tokens, two factor secret) are never returned.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"email":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["human","agent"]},"organizations":{"type":"array","description":"Organization memberships. On the members endpoint this is trimmed to your organization, and `organizationRole` here is the role that endpoint manages.","items":{"type":"object","properties":{"organization":{"type":"string"},"organizationRole":{"type":"string","enum":["admin","member"]}}}},"organization":{"type":"string","description":"ID of the user's active organization"},"organizationRole":{"type":"string","enum":["admin","member"],"description":"Role on the user's active organization"},"organizationLimit":{"type":"integer"},"pinnedProjects":{"type":"array","items":{"type":"string"}},"lastActiveAt":{"type":"string","format":"date-time"},"emailVerified":{"type":"boolean"},"globalExpert":{"type":"boolean"},"twoFactorEnabled":{"type":"boolean"},"notificationsSnoozed":{"type":"boolean"},"personalInformation":{"type":"object"},"professionalInformation":{"type":"object"},"deleted":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}}}}}
```

## The UserListResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"UserListResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"users":{"type":"array","items":{"$ref":"#/components/schemas/User"}},"total":{"type":"integer","description":"Number of members matching the filter, ignoring pagination"}}}}},"User":{"type":"object","description":"Credentials and tokens (password, API key, reset tokens, two factor secret) are never returned.","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"email":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["human","agent"]},"organizations":{"type":"array","description":"Organization memberships. On the members endpoint this is trimmed to your organization, and `organizationRole` here is the role that endpoint manages.","items":{"type":"object","properties":{"organization":{"type":"string"},"organizationRole":{"type":"string","enum":["admin","member"]}}}},"organization":{"type":"string","description":"ID of the user's active organization"},"organizationRole":{"type":"string","enum":["admin","member"],"description":"Role on the user's active organization"},"organizationLimit":{"type":"integer"},"pinnedProjects":{"type":"array","items":{"type":"string"}},"lastActiveAt":{"type":"string","format":"date-time"},"emailVerified":{"type":"boolean"},"globalExpert":{"type":"boolean"},"twoFactorEnabled":{"type":"boolean"},"notificationsSnoozed":{"type":"boolean"},"personalInformation":{"type":"object"},"professionalInformation":{"type":"object"},"deleted":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}}}}}
```

## The BulkUpdateMembersRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"BulkUpdateMembersRequest":{"type":"object","required":["users"],"properties":{"users":{"type":"array","items":{"type":"object","required":["email","organizationRole"],"properties":{"email":{"type":"string","format":"email"},"organizationRole":{"type":"string","enum":["admin","member"]}}}}}}}}}
```

## The MemberBulkWriteResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"MemberBulkWriteResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"users":{"type":"object","properties":{"matchedCount":{"type":"integer","description":"Number of entries sent in the request, including ones that matched no member"},"modifiedCount":{"type":"integer","description":"Same value as `matchedCount`"}}}}}}}}}}
```

## The InviteBulkDeleteResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"InviteBulkDeleteResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"invites":{"type":"object","properties":{"matchedCount":{"type":"integer","description":"Number of invitations actually revoked"},"modifiedCount":{"type":"integer","description":"Same value as `matchedCount`"}}}}}}}}}}
```

## The BulkDeleteMembersRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"BulkDeleteMembersRequest":{"type":"object","required":["users"],"properties":{"users":{"type":"array","items":{"type":"string","format":"email"}}}}}}}
```

## The Invitation object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"Invitation":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"to":{"type":"string","format":"email","description":"Recipient email"},"sender":{"type":"string","description":"Email of the member who sent the invitation"},"organization":{"type":"string"},"organizationRole":{"type":"string","enum":["admin","member"]},"status":{"type":"string","enum":["pending","registered","revoked"]},"projectAssignments":{"type":"array","items":{"type":"object","properties":{"projectId":{"type":"string"},"projectRole":{"type":"string","enum":["Manager","Lead","Reviewer","Labeler"]}}}},"toExist":{"type":"boolean","description":"True when the recipient already had a user account"},"link":{"type":"string","description":"Invitation link sent to the recipient. Absent when generating or sending the email failed, which does not fail the request."},"sso":{"type":"boolean","description":"True when the recipient's email domain is bound to an SSO provider"},"deleted":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}}}}}
```

## The InviteListResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"InviteListResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"invites":{"type":"array","items":{"$ref":"#/components/schemas/Invitation"}},"total":{"type":"integer","description":"Number of invitations matching the filter, ignoring pagination. Only the list endpoint sets it."}}}}},"Invitation":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string","description":"Same value as `_id`"},"to":{"type":"string","format":"email","description":"Recipient email"},"sender":{"type":"string","description":"Email of the member who sent the invitation"},"organization":{"type":"string"},"organizationRole":{"type":"string","enum":["admin","member"]},"status":{"type":"string","enum":["pending","registered","revoked"]},"projectAssignments":{"type":"array","items":{"type":"object","properties":{"projectId":{"type":"string"},"projectRole":{"type":"string","enum":["Manager","Lead","Reviewer","Labeler"]}}}},"toExist":{"type":"boolean","description":"True when the recipient already had a user account"},"link":{"type":"string","description":"Invitation link sent to the recipient. Absent when generating or sending the email failed, which does not fail the request."},"sso":{"type":"boolean","description":"True when the recipient's email domain is bound to an SSO provider"},"deleted":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}}}}}
```

## The InvitationRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"InvitationRequest":{"type":"object","required":["to","organizationRole"],"properties":{"to":{"type":"array","items":{"type":"string","format":"email"},"minItems":1},"organizationRole":{"type":"string","enum":["admin","member"],"description":"Rejected with 400 when it is not one of these two values"},"projectAssignments":{"type":"array","items":{"type":"object","required":["projectId","projectRole"],"properties":{"projectId":{"type":"string"},"projectRole":{"type":"string","enum":["Manager","Lead","Reviewer","Labeler"],"description":"Not validated by the server, but only these values are recognised by the authorization layer. The value is copied onto the project when the invite is accepted."}}}},"organizationRoleAssignments":{"type":"object","additionalProperties":{"type":"string"},"description":"Per-email override of `organizationRole`, keyed by recipient email. Unlike `organizationRole` it is not validated, so an unrecognised role is stored as is."},"projectRoleAssignments":{"type":"object","additionalProperties":{"type":"string"},"description":"Per-email override of the project role, keyed by recipient email. Not validated either."}}}}}}
```

## The BulkDeleteInvitesRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"BulkDeleteInvitesRequest":{"type":"object","required":["invites"],"properties":{"invites":{"type":"array","description":"Recipient emails of the invitations to revoke, not invitation IDs","items":{"type":"string","format":"email"}}}}}}}
```

## The ImportAttachmentsRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ImportAttachmentsRequest":{"type":"object","required":["project","attachments"],"properties":{"project":{"type":"string","description":"Project whose assets are updated"},"attachments":{"type":"array","minItems":1,"maxItems":10000,"description":"One entry per asset matcher","items":{"type":"object","required":["attachments"],"anyOf":[{"required":["externalId"]},{"required":["data"]}],"properties":{"externalId":{"type":"string","description":"Asset external ID to match. Must be a non-empty string. Takes precedence over `data`."},"data":{"type":"string","description":"Asset URL to match, used when `externalId` is absent"},"attachments":{"type":"array","maxItems":1000,"description":"Replaces the matched assets' attachments","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"label":{"type":"object"}}}}}}}}}}}}
```

## The ImportAttachmentsResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ImportAttachmentsResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"project":{"type":"object","properties":{"importCount":{"type":"integer","description":"Number of assets actually modified across all items. An asset whose attachments already equal the submitted array is not counted."}}}}}}}}}}
```

## The ImportLabelsRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ImportLabelsRequest":{"type":"object","required":["project","jsonContent"],"properties":{"project":{"type":"string"},"jsonContent":{"type":"array","description":"Pre-label items. The annotation payload sits at the top level of each item, in the same shape the export produces.","items":{"type":"object","properties":{"externalId":{"type":"string","description":"Task external ID to match. Takes precedence over `data`."},"data":{"type":"string","description":"Asset URL, used when `externalId` is absent. It only matches assets that were uploaded without an explicit `externalId`."},"tools":{"type":"array","items":{"type":"object"},"description":"Annotation objects. Read only when `objects` is absent."},"objects":{"type":"array","items":{"type":"object"},"description":"Annotation objects. Takes precedence over `tools` on this endpoint."},"classifications":{"type":"array","items":{"type":"object"}},"relations":{"type":"array","items":{"type":"object"}},"brushDataUrl":{"type":"string","description":"URL of an uploaded brush mask"},"medicalBrushDataUrl":{"type":"string","description":"URL of an uploaded NRRD medical brush mask, ignored when it is not an NRRD URL"}}}}}}}}}
```

## The ImportLabelsResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ImportLabelsResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"project":{"type":"string","description":"Message of the form \"<labeled> / <sent> tasks are labeled.\". When the two numbers differ it is followed by a hint that the tasks must be in the Start stage and the external IDs must match."}}}}}}}}
```

## The ExportResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"ExportResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"exportPath":{"type":"string","description":"Pre-signed link to the export archive, a zip containing the export files. The link is valid for 24 hours. The same object is kept in the project's export history, which re-signs it on read, so an expired link can be refreshed there."}}}}}}}}
```

## The OverviewResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"OverviewResponse":{"type":"object","properties":{"status":{"type":"string"},"data":{"type":"object","properties":{"overview":{"oneOf":[{"type":"object"},{"type":"array"}]}}}}}}}}
```

## The PluginResponseRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"PluginResponseRequest":{"type":"object","required":["session"],"properties":{"session":{"type":"string","description":"Plugin session ID. Must be a valid ObjectId, otherwise 400; an unknown one returns 404."},"response":{"description":"Result of the plugin run, stored and shown to the user who started it"},"error":{"type":"string","description":"Set it to mark the session failed. The user is notified that the plugin failed."},"export":{"type":"boolean","description":"Marks the result as a ready export, which changes the notification the user gets"}}}}}}
```

## The PluginLogRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"PluginLogRequest":{"type":"object","description":"Mapped onto the plugin log collection: only the fields below are stored, anything else is dropped.","required":["pluginId","organization","session"],"properties":{"pluginId":{"type":"string"},"organization":{"type":"string"},"session":{"type":"string","description":"Plugin session ID"},"projectId":{"type":"string"},"runBy":{"type":"string","description":"Email of the user who started the run"},"timestamp":{"type":"string","format":"date-time"},"level":{"type":"string"},"msg":{"type":"string","description":"The log line itself"}}}}}}
```

## The PluginAck object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"PluginAck":{"type":"object","description":"The body carries no payload, the status code is the acknowledgement.","properties":{"status":{"type":"string"},"data":{"type":"object"}}}}}}
```

## The RerunWebhookRequest object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"RerunWebhookRequest":{"type":"object","required":["stageId"],"properties":{"stageId":{"type":"string","description":"ID of the webhook stage whose tasks are re-sent"}}}}}}
```

## The WebhookResponse object

```json
{"openapi":"3.0.3","info":{"title":"Ango Hub API v2","version":"2.0.0"},"components":{"schemas":{"WebhookResponse":{"type":"object","description":"The body carries no payload, the status code is the acknowledgement.","properties":{"status":{"type":"string"},"data":{"type":"object"}}}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.imerit.net/api/docs/models.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
