---
title: Annotations
description: Read, save, and delete annotations on individual images. Save is a full overwrite — pass the complete list every time.
section: API Reference
order: 3
---
Annotations follow the canonical Pictograph JSON schema — see
[Annotation format](/docs/annotation-format.md) for the full spec.

The class label field is **`name`** (not `class`). Polygons use
multi-ring `paths`, not flat coordinate arrays.

```python
from pictograph import Client
client = Client()
```

## get

Fetch the typed annotation list attached to an image.

```python
annotations = client.annotations.get("img-uuid-1")
for ann in annotations:
    print(ann.name, ann.type)
```

Returns `list[Annotation]` — a discriminated union over
`BBoxAnnotation` / `PolygonAnnotation` / `PolylineAnnotation` /
`KeypointAnnotation`. An image with no annotations returns `[]`
(never raises for the "no annotations" case — only for "no such image").

## save

Replace the image's annotations with the supplied list. **Full overwrite** —
existing annotations are dropped.

```python
from pictograph import BBoxAnnotation, BoundingBox, PolygonAnnotation, PolygonGeometry, Point

result = client.annotations.save("img-uuid-1", [
    BBoxAnnotation(
        id="ann-1",
        name="person",
        bounding_box=BoundingBox(x=100, y=200, w=50, h=80),
    ),
    PolygonAnnotation(
        id="ann-2",
        name="car",
        polygon=PolygonGeometry(paths=[
            [Point(x=0, y=0), Point(x=10, y=0), Point(x=10, y=10)],
        ]),
    ),
])
print(result.previous_count, "→", result.new_count, result.status)
```

| Arg | Type | Notes |
|---|---|---|
| `image_id` | `str` | Image UUID |
| `annotations` | `Sequence[Annotation]` | Pydantic-validated client-side; backend re-validates |

Returns `SaveResult` — `image_id`, `previous_count`, `new_count`, `status`
(`"new"` / `"in_progress"` / `"complete"`, set automatically by count).

Polygons may omit `bounding_box` on save — the backend computes the
enclosing rectangle server-side.

## delete

Remove every annotation from the image. Equivalent to `save(image_id, [])`
but uses `DELETE` and requires `admin`+ role.

```python
result = client.annotations.delete("img-uuid-1")
print(result.deleted_count)
```

## Validation

The SDK Pydantic models reject malformed payloads at construction:

```python
from pictograph import PolygonAnnotation, PolygonGeometry, Point

PolygonGeometry(paths=[[Point(x=0, y=0)]])
# ValidationError: paths[0] has 1 point(s); polygon ring requires >= 3
```

The backend re-validates on save as defense-in-depth — agents that
construct dicts directly will hit `422 ValidationError` for the same
class of mistakes.

## Common errors

| Status | Exception | Cause |
|---|---|---|
| 404 | `NotFoundError` | `image_id` doesn't exist |
| 422 | `ValidationError` | `class` instead of `name`, flat polygon array, unknown class label |
| 403 | `ForbiddenError` | `delete` requires `admin`+ role |

## Auto-annotate workflow

If you want SAM3 to generate annotations rather than write them by hand,
see the [`auto-annotate`](/docs/api-reference/auto-annotate.md) resource.
The `auto_annotate_dataset` workflow saves annotations automatically;
the single-prompt methods return a `PromptResult` and you call `save`
yourself.

## REST equivalent

```bash
curl -X POST -H "X-API-Key: pk_live_…" \
     -H "Content-Type: application/json" \
     -d '{"image_id":"…","annotations":[…]}' \
     https://api.pictograph.io/api/v1/developer/annotations/<image_id>
```