Sign in Get started

Annotation comments

Comment on a single annotation for QA review: list, create, reply, react, edit, resolve and delete. An @username mention notifies that member.

View as Markdown
On this page

A comment is pinned to one annotation, addressed by its id from annotations.get; the annotator sees it on that shape in the editor. Writing a comment needs a key that belongs to a person.

list

Every comment on an image’s annotations, oldest first, open and resolved.

Source: AnnotationComments.list

Arg Type Default Notes
dataset str required Dataset name or id
image str required Image path (addressing)
comments = client.annotation_comments.list(
    dataset="road-signs",
    image="img-001.jpg",
)
for c in comments:
    print(c.id, c.annotation_id, c.resolved, c.body)
pictograph annotation-comments list road-signs 001.jpg --unresolved
curl -s "https://api.pictograph.io/api/v1/developer/annotation-comments?dataset=road-signs&image=img-001.jpg" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

Returns Sequence[AnnotationComment]

AnnotationComment · 18 fields
class AnnotationComment(BaseModel):
    """One comment on an annotation."""
    id: str
    annotation_id: str
    body: str
    resolved: bool = False
    parent_comment_id: str | None = None
    is_edited: bool = False
    is_deleted: bool = False
    resolved_by: str | None = None
    resolved_by_name: str | None = None
    resolved_at: datetime | None = None
    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
    reactions: list[AnnotationCommentReaction] = []

create

Comment on one annotation. Needs a member key.

Source: AnnotationComments.create

Arg Type Default Notes
dataset str required Dataset name or id
image str required Image path (addressing)
annotation str required Annotation id to comment on
body str required Comment text, 1 to 5,000 characters; an @username notifies that member
parent str | None None Comment id to reply to; a reply to a reply attaches to the top comment
annotations = client.annotations.get(
    dataset="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="road-signs",
        image="img-001.jpg",
        annotation=ann.id,
        body=f"@dana low confidence on this {ann.name} - please re-check.",
    )
pictograph annotation-comments add road-signs 001.jpg <annotation-id> "Is this a yield sign? @noah"
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 '{
    "dataset": "road-signs",
    "image": "img-001.jpg",
    "annotation": "b41e7d90-5c62-4a38-8e15-9d3f2a7c6b81",
    "body": "@dana low confidence on this stop-sign - please re-check."
  }'

Returns AnnotationComment

AnnotationComment · 18 fields
class AnnotationComment(BaseModel):
    """One comment on an annotation."""
    id: str
    annotation_id: str
    body: str
    resolved: bool = False
    parent_comment_id: str | None = None
    is_edited: bool = False
    is_deleted: bool = False
    resolved_by: str | None = None
    resolved_by_name: str | None = None
    resolved_at: datetime | None = None
    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
    reactions: list[AnnotationCommentReaction] = []

reply

Reply to a comment; a reply to a reply attaches to the top comment. A parent on another annotation is a 404.

Source: AnnotationComments.reply

Arg Type Default Notes
dataset str required Dataset name or id
image str required Image path (addressing)
annotation str required Annotation id of the thread
parent str required Comment id to reply to
body str required Reply text, 1 to 5,000 characters; an @username notifies that member
reply = client.annotation_comments.reply(
    dataset="road-signs",
    image="img-001.jpg",
    annotation="b41e7d90-5c62-4a38-8e15-9d3f2a7c6b81",
    parent="7c2a91d5-4e83-4b16-9f27-8a0d6e3b512c",
    body="Re-drawn tighter. Good to resolve?",
)
print(reply.parent_comment_id)
pictograph annotation-comments reply road-signs 001.jpg <comment-id> "Yes - relabelled."
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 '{
    "dataset": "road-signs",
    "image": "img-001.jpg",
    "annotation": "b41e7d90-5c62-4a38-8e15-9d3f2a7c6b81",
    "parent": "7c2a91d5-4e83-4b16-9f27-8a0d6e3b512c",
    "body": "Re-drawn tighter. Good to resolve?"
  }'

Returns AnnotationComment

AnnotationComment · 18 fields
class AnnotationComment(BaseModel):
    """One comment on an annotation."""
    id: str
    annotation_id: str
    body: str
    resolved: bool = False
    parent_comment_id: str | None = None
    is_edited: bool = False
    is_deleted: bool = False
    resolved_by: str | None = None
    resolved_by_name: str | None = None
    resolved_at: datetime | None = None
    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
    reactions: list[AnnotationCommentReaction] = []

update

Edit the body, resolve or reopen, or both. Only the author edits the body; anyone in the organization can resolve.

Source: AnnotationComments.update

Arg Type Default Notes
comment str required Comment id
body str | None None New text (author only)
resolved bool | None None True resolves, False reopens
comment = client.annotation_comments.update(
    comment="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)
pictograph annotation-comments edit <comment-id> "Yield sign, partly hidden."
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 · 18 fields
class AnnotationComment(BaseModel):
    """One comment on an annotation."""
    id: str
    annotation_id: str
    body: str
    resolved: bool = False
    parent_comment_id: str | None = None
    is_edited: bool = False
    is_deleted: bool = False
    resolved_by: str | None = None
    resolved_by_name: str | None = None
    resolved_at: datetime | None = None
    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
    reactions: list[AnnotationCommentReaction] = []

resolve

Resolve a comment, or reopen it.

Source: AnnotationComments.resolve

Arg Type Default Notes
comment str required Comment id
resolved bool True False reopens the comment
client.annotation_comments.resolve(
    comment="7c2a91d5-4e83-4b16-9f27-8a0d6e3b512c",
    resolved=True,
)
pictograph annotation-comments resolve <comment-id>            # --reopen to open it again
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 · 18 fields
class AnnotationComment(BaseModel):
    """One comment on an annotation."""
    id: str
    annotation_id: str
    body: str
    resolved: bool = False
    parent_comment_id: str | None = None
    is_edited: bool = False
    is_deleted: bool = False
    resolved_by: str | None = None
    resolved_by_name: str | None = None
    resolved_at: datetime | None = None
    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
    reactions: list[AnnotationCommentReaction] = []

react

React to a comment. Any role can; repeating a reaction changes nothing, and an unknown one is a 400.

Source: AnnotationComments.react

Arg Type Default Notes
comment str required Comment id
reaction str required Reaction to add: like, dislike, love, celebrate, exclamation, question or haha
comment = client.annotation_comments.react(
    comment="7c2a91d5-4e83-4b16-9f27-8a0d6e3b512c",
    reaction="like",
)
print(comment.reactions)
# Reactions are Python and REST only; list shows them with --json.
pictograph annotation-comments list road-signs 001.jpg --json
curl -s -X PUT "https://api.pictograph.io/api/v1/developer/annotation-comments/7c2a91d5-4e83-4b16-9f27-8a0d6e3b512c/reactions/like" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

Returns AnnotationComment

AnnotationComment · 18 fields
class AnnotationComment(BaseModel):
    """One comment on an annotation."""
    id: str
    annotation_id: str
    body: str
    resolved: bool = False
    parent_comment_id: str | None = None
    is_edited: bool = False
    is_deleted: bool = False
    resolved_by: str | None = None
    resolved_by_name: str | None = None
    resolved_at: datetime | None = None
    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
    reactions: list[AnnotationCommentReaction] = []

unreact

Remove your reaction; removing one you never left changes nothing.

Source: AnnotationComments.unreact

Arg Type Default Notes
comment str required Comment id
reaction str required Reaction to remove
comment = client.annotation_comments.unreact(
    comment="7c2a91d5-4e83-4b16-9f27-8a0d6e3b512c",
    reaction="like",
)
print(comment.reactions)
# Reactions are Python and REST only; list shows them with --json.
pictograph annotation-comments list road-signs 001.jpg --json
curl -s -X DELETE "https://api.pictograph.io/api/v1/developer/annotation-comments/7c2a91d5-4e83-4b16-9f27-8a0d6e3b512c/reactions/like" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

Returns AnnotationComment

AnnotationComment · 18 fields
class AnnotationComment(BaseModel):
    """One comment on an annotation."""
    id: str
    annotation_id: str
    body: str
    resolved: bool = False
    parent_comment_id: str | None = None
    is_edited: bool = False
    is_deleted: bool = False
    resolved_by: str | None = None
    resolved_by_name: str | None = None
    resolved_at: datetime | None = None
    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
    reactions: list[AnnotationCommentReaction] = []

delete

Delete a comment: its author can, and so can an admin key. It keeps its place in the thread with an empty body, so its replies stay attached.

Source: AnnotationComments.delete

Arg Type Default Notes
comment str required Comment id
client.annotation_comments.delete(comment="7c2a91d5-4e83-4b16-9f27-8a0d6e3b512c")
pictograph annotation-comments delete <comment-id> --yes
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 with neither body nor resolved, or a key with no person to author the comment
403 ForbiddenError A viewer key commenting, a non-author editing a body, or a non-author below admin deleting
404 NotFoundError The image or comment doesn’t exist in your organization
Copied to clipboard