client.workflow¶
Bases: SyncResource
Sync labeling-workflow namespace (client.workflow).
list_asset_states ¶
list_asset_states(project_id: UUID | str, *, status: WorkflowStatus | str | None = None, status_not: list[WorkflowStatus | str] | None = None, assigned_to: UUID | str | None = None, assigned: bool | None = None, reviewer_id: UUID | str | None = None, rework_count_min: int | None = None, tags: list[str] | None = None, tags_none: list[str] | None = None, tagged: bool | None = None, classes: list[UUID | str] | None = None, classes_none: list[UUID | str] | None = None, annotated: bool | None = None, q: str | None = None, uploaded_by: UUID | str | None = None, created_at_from: datetime | str | None = None, created_at_to: datetime | str | None = None, include: str | None = None, include_total: bool = False, limit: int | None = None, cursor: str | None = None) -> Page[AssetState]
Browse asset states across the project (manager dashboard), newest-updated first (cursor-paginated; iterate for all pages).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
status
|
WorkflowStatus | str | None
|
single |
None
|
status_not
|
list[WorkflowStatus | str] | None
|
exclude ALL listed statuses (negative twin of |
None
|
assigned_to
|
UUID | str | None
|
keep only tasks assigned to this specific labeler (user id). |
None
|
assigned
|
bool | None
|
|
None
|
reviewer_id
|
UUID | str | None
|
keep only tasks last reviewed by this specific reviewer (user id). |
None
|
rework_count_min
|
int | None
|
keep tasks with |
None
|
tags
|
list[str] | None
|
contains-ALL over the per-(project, asset) tag set (ADR-0050). |
None
|
tags_none
|
list[str] | None
|
carries NONE of the listed tags (untagged matches) —
combine with |
None
|
tagged
|
bool | None
|
|
None
|
classes
|
list[UUID | str] | None
|
ANY-of over the asset's live annotation classes (ADR-0100). |
None
|
classes_none
|
list[UUID | str] | None
|
has NO annotation of ANY listed class. |
None
|
annotated
|
bool | None
|
|
None
|
q
|
str | None
|
case-insensitive substring on the asset filename. |
None
|
uploaded_by
|
UUID | str | None
|
keep only assets uploaded by this specific user (user id). |
None
|
created_at_from
|
datetime | str | None
|
inclusive lower bound ( |
None
|
created_at_to
|
datetime | str | None
|
exclusive upper bound ( |
None
|
include
|
str | None
|
CSV of embeds; only |
None
|
include_total
|
bool
|
also populate |
False
|
Raises:
| Type | Description |
|---|---|
UnprocessableError
|
invalid |
BadRequestError
|
a contradictory combination — |
NotFoundError
|
unknown project (PROJECT_NOT_FOUND). |
ForbiddenError
|
caller is not a project member (AUTH_FORBIDDEN). |
queue ¶
queue(project_id: UUID | str, *, status: Literal['to_label', 'in_progress'] | str | None = None, rework: bool | None = None, include: str | None = None, include_total: bool = False, limit: int | None = None, cursor: str | None = None) -> Page[AssetState]
The caller's own queue: tasks they should work on now — their assigned
in_progress / to_label tasks, plus (in self_claim mode) the
unassigned claimable pool. Cursor-paginated; iterate for all pages.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
status
|
Literal['to_label', 'in_progress'] | str | None
|
restrict to |
None
|
rework
|
bool | None
|
|
None
|
include
|
str | None
|
CSV of embeds; only |
None
|
include_total
|
bool
|
also populate |
False
|
Raises:
| Type | Description |
|---|---|
UnprocessableError
|
|
BadRequestError
|
unknown |
NotFoundError
|
unknown project (PROJECT_NOT_FOUND). |
ForbiddenError
|
caller is not a project member (AUTH_FORBIDDEN). |
queue_stats ¶
Bucket counts for the caller's 4-card hero (ADR-0029) — a complete
snapshot, scoped exactly like :meth:queue.
Raises:
| Type | Description |
|---|---|
NotFoundError
|
unknown project (PROJECT_NOT_FOUND). |
ForbiddenError
|
caller is not a project member (AUTH_FORBIDDEN). |
get_labeling_context ¶
One-shot labeler-page bundle for an asset: workflow state + asset metadata + all its annotations in this project + a presigned download URL + the project's class vocabulary.
Raises:
| Type | Description |
|---|---|
NotFoundError
|
the asset is not in this project's scope (ASSET_NOT_FOUND), or its bytes were hard-deleted (ASSET_DELETED). |
ForbiddenError
|
caller is not a project member (AUTH_FORBIDDEN). |
claim ¶
Self-claim a task (to_label/in_progress → in_progress assigned to
you). Requires assignment_mode=self_claim unless the task is already yours.
Raises:
| Type | Description |
|---|---|
ConflictError
|
task is not claimable — wrong status or already assigned to another labeler (PROJECT_STATE_FORBIDDEN). |
ForbiddenError
|
project requires manual assignment, or you're not a member (AUTH_FORBIDDEN). |
NotFoundError
|
asset not in this project's scope (ASSET_NOT_FOUND). |
assign ¶
Assign a task to a specific member (Manager+). The target must be a project member (labeler/reviewer/manager) or a workspace admin.
Sets the assignee but leaves status to_label — the assignee still
:meth:claim\ s to start (that flips it to in_progress). Contrast
workflow_batch.batch_assign, which assigns AND starts in one step.
Raises:
| Type | Description |
|---|---|
NotFoundError
|
asset not in scope (ASSET_NOT_FOUND) or target user not in this tenant (USER_NOT_FOUND). |
BadRequestError
|
target user is not a member of this project (PROJECT_MEMBER_NOT_FOUND). |
ForbiddenError
|
caller is not a project manager (AUTH_FORBIDDEN). |
unassign ¶
Clear the current assignee (Manager+). An in_progress row reverts to
to_label; already-unassigned is a no-op that returns the row unchanged.
Raises:
| Type | Description |
|---|---|
NotFoundError
|
asset not in this project's scope (ASSET_NOT_FOUND). |
ForbiddenError
|
caller is not a project manager (AUTH_FORBIDDEN). |
submit ¶
Submit your work for review (in_progress → in_review). Submission is
final — the only way back is reviewer :meth:reject.
Raises:
| Type | Description |
|---|---|
ConflictError
|
not in |
ForbiddenError
|
you are not the assigned labeler (AUTH_FORBIDDEN). |
BadRequestError
|
a required classification is unanswered — |
NotFoundError
|
asset not in this project's scope (ASSET_NOT_FOUND). |
approve ¶
Approve reviewed work (in_review → done; stamps you as reviewer,
Reviewer+).
Raises:
| Type | Description |
|---|---|
ConflictError
|
not in |
ForbiddenError
|
caller is not a project reviewer (AUTH_FORBIDDEN). |
NotFoundError
|
asset not in this project's scope (ASSET_NOT_FOUND). |
reject ¶
Reject reviewed work — loops back to the original labeler (in_review →
in_progress, bumps rework_count, Reviewer+). reason (optional) is
stored on the state and dropped into the asset's comment thread the labeler sees.
Raises:
| Type | Description |
|---|---|
ConflictError
|
not in |
ForbiddenError
|
caller is not a project reviewer (AUTH_FORBIDDEN). |
NotFoundError
|
asset not in this project's scope (ASSET_NOT_FOUND). |
skip ¶
Set the asset aside — 'nothing to label' or unusable data (in_progress
→ skipped, ADR-0110). Terminal, distinct from a done label; no reason
captured. Skipped assets are excluded from version freezes; a reviewer can
:meth:reopen one back into the queue.
Raises:
| Type | Description |
|---|---|
ConflictError
|
not in |
ForbiddenError
|
you are not the assigned labeler (AUTH_FORBIDDEN). |
NotFoundError
|
asset not in this project's scope (ASSET_NOT_FOUND). |
reopen ¶
Revive a skipped asset back into the queue (skipped → to_label,
clears the assignee; Reviewer+). For when set-aside data must be labeled
after all (e.g. data is scarce).
Raises:
| Type | Description |
|---|---|
ConflictError
|
not in |
ForbiddenError
|
caller is not a project reviewer (AUTH_FORBIDDEN). |
NotFoundError
|
asset not in this project's scope (ASSET_NOT_FOUND). |
create_annotation ¶
create_annotation(project_id: UUID | str, asset_id: UUID | str, *, class_id: UUID | str, type: AnnotationType | str, geometry: Geometry | GeometryParam | None = None, data: dict[str, Any] | None = None, instance_id: UUID | str | None = None, parent_id: UUID | str | None = None, locked: bool | None = None, id: UUID | str | None = None) -> Annotation
Create one annotation on an asset (201). Creating more than a handful?
Do NOT loop this — use annotations.asset_batch_create (one asset,
≤1000), annotations.project_batch_create (across assets, ≤1000), or
annotations.import_ndjson (any size, async) instead.
geometry is required for drawable
types (bbox/polygon/keypoint) and must be omitted for classification (whose
answer goes in data). id is the client-minted UUID primary key — omit to
let the server mint one; supplying an existing id upserts (last-write-wins,
ADR-0092).
instance_id groups shapes that are the same real-world instance (e.g. one
object across keyframes / parts) under a shared id. parent_id links this shape
to a parent annotation for nesting (child rows cascade-delete with the parent).
locked (default False) marks the shape edit-locked so later content edits
are refused until it is unlocked (ADR-0084).
Raises:
| Type | Description |
|---|---|
ConflictError
|
the asset is edit-locked for you — |
NotFoundError
|
asset not in scope (ASSET_NOT_FOUND) or |
BadRequestError
|
archived class, |
UnprocessableError
|
malformed body — e.g. geometry.type ≠ |
ForbiddenError
|
caller is not a project member (AUTH_FORBIDDEN). |
update_annotation ¶
update_annotation(project_id: UUID | str, annotation_id: UUID | str, *, class_id: UUID | str | None = None, geometry: Geometry | GeometryParam | None = None, data: dict[str, Any] | None = None, instance_id: UUID | str | None = None, locked: bool | None = None) -> Annotation
Partially update an annotation (only the fields you pass change). type is
immutable — changing geometry kind is a delete + create. Relabeling validates the
new class_id against the annotation's stored type. instance_id re-groups
the shape under a different instance id (the grouping shared by same-instance
shapes; set to a new id to move it, ADR-0092).
Note: a locked=True shape rejects content edits unless this same call also
sets locked=False; a lock-only toggle always passes (ADR-0084).
Raises:
| Type | Description |
|---|---|
NotFoundError
|
no such annotation in this project (ANNOTATION_NOT_FOUND), or
the new |
ConflictError
|
the asset is edit-locked for you (PROJECT_STATE_FORBIDDEN), or the shape is locked and you're editing its content (CONCURRENT_MODIFICATION). |
BadRequestError
|
the new class is archived or forbids the stored type (VALIDATION_ERROR). |
ForbiddenError
|
caller is not a project member (AUTH_FORBIDDEN). |
delete_annotation ¶
Hard-delete an annotation (204). Idempotent — an unknown id is a silent no-op.
Children (rows whose parent_id is this shape) cascade-delete; comment anchors
are set null (ADR-0092).
Raises:
| Type | Description |
|---|---|
ConflictError
|
the asset is edit-locked for you (PROJECT_STATE_FORBIDDEN). |
ForbiddenError
|
caller is not a project member (AUTH_FORBIDDEN). |
Response models¶
Models returned by client.workflow methods (fields, types, and what each means).
Project workflow envelopes: sync batch assign/unassign/approve/reject (ADR-0041).
The four manager/reviewer workflow verbs each come in two flavors:
- async
bulk-*kickoffs return aJob(seetypes.jobs) — no model here;JobHandle.wait()polls it to terminal. Best-effort: the worker skips rows that fail the per-verb state precondition (e.g. an approve target that isn'tin_review) rather than erroring. - sync
batch-*ops return the canonical batch envelope modeled below:total == <verb_past> + skippedover the requested asset ids, oneresultsentry per requested id. Each verb self-describes its count field (assigned/unassigned/approved/rejected) — no genericapplied— so a caller reads the number without knowing which verb it called.
skipped counts requested ids the UPDATE's WHERE clause left untouched: the asset
wasn't in the project, was already in the desired state (already assigned to that
user / already unassigned), or wasn't in the required source state (only in_review
rows approve/reject).
Response models only (extra="allow"); request bodies are built as dicts in the
resource.
This module is shared by the workflow domain group (single-asset verbs + queue +
asset-state list + per-asset annotation CRUD): its read shapes — :class:Annotation,
:class:AssetState, :class:QueueStats, :class:LabelingContext — follow the batch
envelopes below.
OWNERSHIP NOTE (STYLE §3): :class:Annotation is a shape shared with the
annotations / annotation-list groups. It is declared here because this is the
first workflow module to need it and the annotations type module had not landed;
the highest-common-module home is types.annotations — if that module exists at
assembly, promote :class:Annotation there and import it here (do NOT keep two copies).
BatchAssignResultItem ¶
Bases: BaseModel
Per-asset outcome of a sync batch_assign. skipped = the id wasn't in
the project or was already assigned to user_id (and not a to_label row).
BatchAssignResult ¶
Bases: BaseModel
Sync batch-op envelope (ADR-0041): total == assigned + skipped and
len(results) == total over the requested asset ids. To-label rows flip to
in_progress on assign (claim-like semantics).
BatchUnassignResultItem ¶
Bases: BaseModel
Per-asset outcome of a sync batch_unassign. skipped = the id wasn't in
the project or already had no assignee.
BatchUnassignResult ¶
Bases: BaseModel
Sync batch-op envelope (ADR-0041): total == unassigned + skipped and
len(results) == total. in_progress rows revert to to_label on unassign.
BatchApproveResultItem ¶
Bases: BaseModel
Per-asset outcome of a sync batch_approve. skipped = the id wasn't in
the project or wasn't in in_review (only in_review rows approve to done).
BatchApproveResult ¶
Bases: BaseModel
Sync batch-op envelope (ADR-0041): total == approved + skipped and
len(results) == total. Approving flips in_review -> done and stamps the
caller as reviewer.
BatchRejectResultItem ¶
Bases: BaseModel
Per-asset outcome of a sync batch_reject. skipped = the id wasn't in
the project or wasn't in in_review (only in_review rows reject).
BatchRejectResult ¶
Bases: BaseModel
Sync batch-op envelope (ADR-0041): total == rejected + skipped and
len(results) == total. Rejecting flips in_review -> in_progress, bumps
rework_count, and (when reason is set) records it on the state row and a
per-asset Comment (origin=workflow_reject) the labeler sees inline.
AssetState ¶
Bases: BaseModel
One project_asset_state row — an asset's workflow state within a project.
status walks to_label -> in_progress -> in_review -> done (reject loops
in_review back to in_progress and bumps rework_count). asset,
assigned_to_user and reviewer_user are populated only when the route
JOINed them (the list / queue / single-state reads do); tags are the
per-(project, asset) tags (ADR-0050), independent of other projects on the same
asset.
QueueStats ¶
Bases: BaseModel
Bucket counts for the labeler's 4-card hero (ADR-0029), scoped to the caller's
queue (their assigned tasks + the unassigned pool in self_claim mode).
all == to_label + rework + continue_.
LabelingContext ¶
Bases: BaseModel
One-shot labeler-page bundle (saves ~4 round trips): the asset's workflow
asset_state + asset metadata + all its shapes (annotations) in this
project + a presigned download_url for the full-res bytes + the project's class
vocabulary (object classes with inline attributes + image-level
image_classifications). annotations is a legacy alias of shapes.