Circle V2 API Docs
    Preparing search index...
    getAnswers: QueryProcedure<
        {
            input: { runId: string };
            meta: object;
            output: {
                answers: {
                    annotationId: string;
                    annotationPrompt: string | null;
                    annotationType: "ai_prompt" | "checkbox" | "text" | "contact";
                    answerId: string;
                    canRevertToAiAnswer: boolean;
                    citations: unknown;
                    contactLink: { contactTypeName: string; fieldKey: string } | null;
                    contextSources: NormalizedContextSource[];
                    coordinates: FormAnswerCoordinate[];
                    coordinatesSource: CoordinateSource;
                    createdAt: string | null;
                    fieldStatus: FormRunFieldStatus;
                    fontSize: string | null;
                    generatedByAi: boolean | null;
                    isExclusive: boolean | null;
                    noAnswerReason: NoAnswerReasonCode | null;
                    status: string | null;
                    subfieldProgress: { filled: number; total: number } | null;
                    updatedAt: string | null;
                    value: DecodedAnswerValue;
                }[];
                formId: string & { __brand: "FormId" };
                isTerminal: boolean;
                runId: FormRunId;
                status: string;
            };
        },
    > = ...

    Everything the fill drawer needs to render a run's answers over the PDF: the decoded value, its provenance (citations + normalized context sources), a per-field status, and the boxes to position each answer in.

    Values arrive already decoded by the repo's codec, so all three at-rest v1 shapes (["text"], batched {coordId: "text"}, and manually-edited {coordId: {value, fontSize}}) come back as one discriminated union rather than raw jsonb the client has to sniff. Unrecognized shapes pass through as kind: "unknown" — decoding never throws, so one weird legacy row cannot blank the whole drawer.

    Read-only. update-answer.mutation returns EXACTLY one element of answers (same toAnswerOutput) so the client can patch this cache in place.