Skip to main content
Session annotations attach feedback to multi-turn conversations or threads. Use them for conversation-level quality signals: whether the session achieved its goal, whether a human handoff was needed, overall satisfaction scores. All functions are imported from @arizeai/phoenix-client/sessions. See Annotations for the shared annotation model and concepts.
Requires Phoenix Server ≥ 12.0.0. The client will throw an error with the minimum required version if the server is too old.

Relevant Source Files

  • src/sessions/addSessionAnnotation.ts for the single-annotation API
  • src/sessions/logSessionAnnotations.ts for batch logging
  • src/sessions/types.ts for the SessionAnnotation interface

Add A Single Session Annotation

Mark a support session as resolved after human review:
import { addSessionAnnotation } from "@arizeai/phoenix-client/sessions";

await addSessionAnnotation({
  sessionAnnotation: {
    sessionId: "cst_abc123",
    name: "resolution",
    annotatorKind: "HUMAN",
    label: "resolved",
    score: 1,
    explanation: "User confirmed their issue was resolved.",
  },
});

Batch Log Session Annotations

Use logSessionAnnotations to annotate multiple sessions in a single request. This example scores a batch of support sessions for handoff detection:
import { logSessionAnnotations } from "@arizeai/phoenix-client/sessions";

await logSessionAnnotations({
  sessionAnnotations: [
    {
      sessionId: "cst_abc123",
      name: "handoff-required",
      annotatorKind: "CODE",
      score: 0,
      label: "no",
    },
    {
      sessionId: "cst_def456",
      name: "handoff-required",
      annotatorKind: "CODE",
      score: 1,
      label: "yes",
      explanation: "Sentiment dropped below threshold at turn 4.",
    },
  ],
});

Conversation Quality Scoring

After a multi-turn conversation ends, use an LLM to evaluate overall coherence and goal completion:
import { addSessionAnnotation } from "@arizeai/phoenix-client/sessions";

// evaluationResult comes from your LLM judge pipeline
await addSessionAnnotation({
  sessionAnnotation: {
    sessionId: sessionId,
    name: "conversation-quality",
    annotatorKind: "LLM",
    score: evaluationResult.score,
    label: evaluationResult.score > 0.7 ? "good" : "needs-improvement",
    explanation: evaluationResult.reasoning,
    metadata: { model: "gpt-4o", evaluatorVersion: "v2" },
  },
});

End-User Satisfaction (CSAT)

Log customer satisfaction at the end of a chat session. Normalize the raw rating to a 0–1 scale for consistent scoring:
import { addSessionAnnotation } from "@arizeai/phoenix-client/sessions";

// User submitted a 1-5 star rating at end of chat
await addSessionAnnotation({
  sessionAnnotation: {
    sessionId: sessionId,
    name: "csat",
    annotatorKind: "HUMAN",
    score: userRating / 5,
    label: userRating >= 4 ? "satisfied" : "unsatisfied",
    metadata: { rawRating: userRating, channel: "mobile-app" },
  },
});

Idempotent Upserts With identifier

Session annotations are unique by (name, sessionId, identifier). The identifier field controls whether a write creates a new annotation or updates an existing one. Without identifier, a session can only have one annotation per name. Adding an identifier lets you store multiple annotations with the same name on the same session, each keyed by a different identifier. Re-sending the same tuple updates that specific annotation in place.
import { addSessionAnnotation } from "@arizeai/phoenix-client/sessions";

await addSessionAnnotation({
  sessionAnnotation: {
    sessionId: sessionId,
    name: "goal-completion",
    annotatorKind: "LLM",
    score: 0.85,
    identifier: "goal-eval-v3",
  },
});
// Running this again updates the existing annotation.
// Using identifier: "goal-eval-v4" would create a second annotation.

Parameter Reference

SessionAnnotation

FieldTypeRequiredDescription
sessionIdstringYesSession / conversation / thread identifier
namestringYesAnnotation name (e.g. "csat")
annotatorKind"HUMAN" | "LLM" | "CODE"NoDefaults to "HUMAN"
labelstringNo*Categorical label
scorenumberNo*Numeric score
explanationstringNo*Free-text explanation
identifierstringNoFor idempotent upserts
metadataRecord<string, unknown>NoArbitrary metadata
*At least one of label, score, or explanation is required.

Source Map

  • src/sessions/addSessionAnnotation.ts
  • src/sessions/logSessionAnnotations.ts
  • src/sessions/types.ts
  • src/types/annotations.ts