---
title: "Create project annotation"
method: POST
path: "/v1/projects/{projectSlug}/annotations"
tags: ["Annotations"]
---

# Create project annotation

`POST /v1/projects/{projectSlug}/annotations`

Creates a published annotation score against a target trace. The trace is resolved by explicit id (`trace.by = "id"`) or by a filter set (`trace.by = "filters"`, exactly one match required). When called with an OAuth token, the annotation is attributed to the authenticated user.

## Path parameters

- `projectSlug` string, required — Project slug (human-readable identifier)

## Request body

- CreateAnnotationBody
  - `simulationId` string, nullable — Simulation this annotation is tied to, if any. `null` (default) when not part of a simulation.
  - `signalId` string, nullable — Pre-selected signal this annotation belongs to. Leave `null` (default) to let the automatic signal-discovery pipeline route the annotation.
  - `value` number, required — Normalized score value in [0, 1]. Higher = better.
  - `passed` boolean, required — Whether the annotated output passes the reviewer's bar.
  - `feedback` string, required — Free-text feedback explaining the score. Surfaced alongside the trace.
  - `anchor` AnnotationAnchor — Optional anchor pinning the annotation to a specific message / part / offset range inside the trace.
    - `messageIndex` integer — 0-based message index inside the conversation. Omit for conversation-level annotations.
    - `partIndex` integer — 0-based index into the target message's `parts[]`. Requires `messageIndex`.
    - `startOffset` integer — Inclusive start offset for substring annotations. Must be paired with `endOffset` and `partIndex`.
    - `endOffset` integer — Exclusive end offset for substring annotations. Must be paired with `startOffset` and `partIndex`, and `>= startOffset`.
    - `textFormat` 'pretty-json' — UI-side text transform applied before the offsets were captured (e.g. `"pretty-json"`). Resolvers must apply the same transform before slicing.
  - `trace` union, required — Target trace. Either an explicit id or a filter set matching exactly one trace.
    - object
      - `by` 'id', required — Match a single trace by its identifier. Pair with `id`.
      - `id` string, required — 32-character trace identifier.
    - object
      - `by` 'filters', required — Match a single trace by a filter set. Pair with `filters`; exactly one trace must match.
      - `filters` FilterSet, required — Filter set keyed by field name. Each entry holds an array of conditions ANDed together for that field; field-level groups are also ANDed across the set.

## Response `201`

Annotation created

- Annotation
  - `id` string, required — Stable annotation identifier.
  - `organizationId` string, required — Organization that owns this annotation.
  - `projectId` string, required — Project this annotation lives in.
  - `sessionId` string, nullable, required — Session id lifted from the annotated trace. `null` when the trace has no session.
  - `traceId` string, nullable, required — Identifier of the annotated trace.
  - `spanId` string, nullable, required — Span the annotation pins to. Defaults to the trace's last LLM-completion span.
  - `simulationId` string, nullable, required — Simulation reference, if any.
  - `signalId` string, nullable, required — Signal this annotation contributes to, if any.
  - `value` number, required — Normalized score value in [0, 1]. Higher = better.
  - `passed` boolean, required — Whether the annotation marks the output as passing.
  - `feedback` string, required — Free-text feedback explaining the score.
  - `error` string, nullable, required — Generation error text, when the annotation itself errored. `null` for successful annotations.
  - `errored` boolean, required — `true` when the annotation could not be generated successfully.
  - `duration` integer, required — Generation duration in nanoseconds. `0` for human annotations.
  - `tokens` integer, required — Total LLM tokens consumed generating the score. `0` for human annotations.
  - `cost` integer, required — Total LLM cost in microcents (100,000,000 per USD). `0` for human annotations.
  - `draftedAt` string, date-time, nullable, required — ISO-8601 timestamp at which the annotation was drafted. `null` for published annotations.
  - `annotatorId` string, nullable, required — User who authored the annotation. `null` when not attributed to a user.
  - `createdAt` string, date-time, required — ISO-8601 timestamp at which the annotation was created.
  - `updatedAt` string, date-time, required — ISO-8601 timestamp of the last metadata update.
  - `source` 'annotation', required — Always `"annotation"` for this shape.
  - `sourceId` union, required — Origin marker. Sentinel `"UI"` / `"API"` / `"SYSTEM"` for drafts and automation, or a CUID for authored rows.
    - 'UI' | 'API' | 'SYSTEM'
    - string
  - `metadata` AnnotationMetadata, required — Annotation-specific metadata: `rawFeedback` plus a snapshot of the anchor at write time.
    - `rawFeedback` string, required — Original feedback text as written by the annotator, before any server-side enrichment.
    - `messageIndex` integer — 0-based message index inside the conversation. Omit for conversation-level annotations.
    - `partIndex` integer — 0-based index into the target message's `parts[]`. Requires `messageIndex`.
    - `startOffset` integer — Inclusive start offset for substring annotations. Must be paired with `endOffset` and `partIndex`.
    - `endOffset` integer — Exclusive end offset for substring annotations. Must be paired with `startOffset` and `partIndex`, and `>= startOffset`.
    - `textFormat` 'pretty-json' — UI-side text transform applied before the offsets were captured (e.g. `"pretty-json"`). Resolvers must apply the same transform before slicing.

## Other responses

- `400` — Validation error
- `401` — Unauthorized
- `404` — Not found

---

[API](https://skmtc.net/latitude-dev/apis/latitude.md) · [All operations](https://skmtc.net/latitude-dev/apis/latitude/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/latitude-dev/latitude/revisions/2ce07f01d6c2/schema)
