Sign in Get started

Annotation comments

Review comments pinned to a single annotation - list, create, edit, resolve, delete. The programmatic side of annotation QA.

View as Markdown

A comment is attached to one annotation inside one image, not to the image and not to the dataset. That is what makes it useful for review: a QA pass can flag the specific box it disagrees with, and the annotator sees the note on that box in the editor.

An @username mention in the body notifies that member of your organization. See Notifications for reading the resulting feed.

Images are addressed the way you read them off the grid, by dataset name and filename. The annotation itself stays an id: an annotation carries a class name, not a unique one, so a hundred boxes on an image can all be called car and no name identifies one of them. Take the id from client.annotations.get.

There is no pictograph CLI group for annotation comments; use the Python SDK or raw REST.

list

Every comment on the given image’s annotations, oldest first, resolved and open alike.

Source: AnnotationComments.list

Arg Type Default Notes
dataset_name str required Dataset name.
image str required Image filename, or an image id
comments = client.annotation_comments.list(
    dataset_name="road-signs",
    image="img-001.jpg",
)
for c in comments:
    print(c.id, c.annotation_id, c.resolved, c.body)
curl -s "https://api.pictograph.io/api/v1/developer/annotation-comments?image_id=3f1c8e42-6b90-4a71-9d0e-2b5c7a11e004" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

Returns Sequence[AnnotationComment]

AnnotationComment · 11 fields
class AnnotationComment(BaseModel):
    """One comment on an annotation."""
    id: str
    annotation_id: str
    body: str
    resolved: bool = False
    created_at: datetime | None = None
    updated_at: datetime | None = None
    user_id: str | None = None
    author_name: str | None = None
    author_username: str | None = None
    author_avatar_url: str | None = None
    is_mine: bool = False

REST takes the image id rather than the filename; resolve one with GET /api/v1/developer/images/?dataset=road-signs&filename=img-001.jpg.

create

Post a comment on one annotation. Requires member+ role, and the API key must belong to a user, since a comment has an author.

Source: AnnotationComments.create

Arg Type Default Notes
dataset_name str required Dataset name.
image str required Image filename, or an image id
annotation_id str required The annotation to pin the comment to
body str required Comment text, 1 to 5000 characters. @username mentions notify org members
annotations = client.annotations.get(
    dataset_name="road-signs",
    image="img-001.jpg",
)
low = [a for a in annotations if (a.confidence or 1.0) < 0.4]

for ann in low:
    client.annotation_comments.create(
        dataset_name="road-signs",
        image="img-001.jpg",
        annotation_id=ann.id,
        body=f"@dana low confidence on this {ann.name} - please re-check.",
    )
curl -s -X POST "https://api.pictograph.io/api/v1/developer/annotation-comments" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "image_id": "3f1c8e42-6b90-4a71-9d0e-2b5c7a11e004",
    "annotation_id": "b41e7d90-5c62-4a38-8e15-9d3f2a7c6b81",
    "body": "@dana low confidence on this stop-sign - please re-check."
  }'

Returns AnnotationComment

AnnotationComment · 11 fields
class AnnotationComment(BaseModel):
    """One comment on an annotation."""
    id: str
    annotation_id: str
    body: str
    resolved: bool = False
    created_at: datetime | None = None
    updated_at: datetime | None = None
    user_id: str | None = None
    author_name: str | None = None
    author_username: str | None = None
    author_avatar_url: str | None = None
    is_mine: bool = False

update

Edit the body, change the resolved flag, or both in one call. Editing the body is restricted to the comment’s author; changing the resolved flag is open to anyone in the organization. Sending neither field is a 400.

Source: AnnotationComments.update

Arg Type Default Notes
comment_id str required Comment id, from list or create
body str | None None New text. Author only
resolved bool | None None Mark resolved or reopen
comment = client.annotation_comments.update(
    comment_id="7c2a91d5-4e83-4b16-9f27-8a0d6e3b512c",
    body="Re-checked against the raw frame - the box is correct after all.",
    resolved=True,
)
print(comment.resolved, comment.updated_at)
curl -s -X PATCH "https://api.pictograph.io/api/v1/developer/annotation-comments/7c2a91d5-4e83-4b16-9f27-8a0d6e3b512c" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY" -H "Content-Type: application/json" \
  -d '{"body": "Re-checked against the raw frame - the box is correct after all.", "resolved": true}'

Returns AnnotationComment

AnnotationComment · 11 fields
class AnnotationComment(BaseModel):
    """One comment on an annotation."""
    id: str
    annotation_id: str
    body: str
    resolved: bool = False
    created_at: datetime | None = None
    updated_at: datetime | None = None
    user_id: str | None = None
    author_name: str | None = None
    author_username: str | None = None
    author_avatar_url: str | None = None
    is_mine: bool = False

resolve

The one-argument form of update for the common case: close a comment, or reopen it with resolved=False. It issues the same request.

Source: AnnotationComments.resolve

Arg Type Default Notes
comment_id str required Comment id
resolved bool True False reopens the comment
client.annotation_comments.resolve(
    comment_id="7c2a91d5-4e83-4b16-9f27-8a0d6e3b512c",
    resolved=True,
)
curl -s -X PATCH "https://api.pictograph.io/api/v1/developer/annotation-comments/7c2a91d5-4e83-4b16-9f27-8a0d6e3b512c" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY" -H "Content-Type: application/json" \
  -d '{"resolved": true}'

Returns AnnotationComment

AnnotationComment · 11 fields
class AnnotationComment(BaseModel):
    """One comment on an annotation."""
    id: str
    annotation_id: str
    body: str
    resolved: bool = False
    created_at: datetime | None = None
    updated_at: datetime | None = None
    user_id: str | None = None
    author_name: str | None = None
    author_username: str | None = None
    author_avatar_url: str | None = None
    is_mine: bool = False

delete

Remove a comment permanently. Allowed to the author, or to an admin / owner key moderating the thread.

Source: AnnotationComments.delete

Arg Type Default Notes
comment_id str required Comment id
client.annotation_comments.delete(
    comment_id="7c2a91d5-4e83-4b16-9f27-8a0d6e3b512c",
)
curl -s -X DELETE "https://api.pictograph.io/api/v1/developer/annotation-comments/7c2a91d5-4e83-4b16-9f27-8a0d6e3b512c" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

Returns None

Common errors

Status Exception Cause
400 ValidationError update sent neither a new body nor a resolved flag, or the API key has no associated user to author a comment
403 ForbiddenError A viewer key tried to comment, a non-author tried to edit a body, or a non-author without admin+ tried to delete
404 NotFoundError The image or the comment does not exist in your organization
Copied to clipboard