{
  "openapi": "3.1.0",
  "info": {
    "title": "OutFigure API",
    "version": "1.0.0-m1",
    "description": "Integration API for learner events, observed learner-concept state and supporting evidence. observed-v0 summarizes observed attempts; it does not forecast or estimate mastery. All examples are synthetic."
  },
  "servers": [
    {
      "url": "http://127.0.0.1:8787",
      "description": "Local development server"
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Project-scoped partner API key: `Authorization: Bearer lsk_test_…`. Server-side use only."
      }
    },
    "schemas": {
      "WebhookNotice": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "example": "11111111-1111-4111-8111-111111111111"
              },
              "type": {
                "type": "string",
                "enum": [
                  "outcome.receipt.created"
                ]
              },
              "created_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "data": {
                "type": "object",
                "properties": {
                  "receipt_id": {
                    "type": "string",
                    "format": "uuid",
                    "example": "11111111-1111-4111-8111-111111111111"
                  },
                  "contract_id": {
                    "type": "string",
                    "format": "uuid",
                    "example": "11111111-1111-4111-8111-111111111111"
                  },
                  "version": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "status": {
                    "$ref": "#/components/schemas/ReceiptStatus"
                  },
                  "measurement_depth": {
                    "$ref": "#/components/schemas/MeasurementDepth"
                  },
                  "causal_strength": {
                    "$ref": "#/components/schemas/CausalStrength"
                  }
                },
                "required": [
                  "receipt_id",
                  "contract_id",
                  "version",
                  "status",
                  "measurement_depth",
                  "causal_strength"
                ]
              }
            },
            "required": [
              "id",
              "type",
              "created_at",
              "data"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "example": "11111111-1111-4111-8111-111111111111"
              },
              "type": {
                "type": "string",
                "enum": [
                  "outcome.receipt.updated"
                ]
              },
              "created_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "data": {
                "type": "object",
                "properties": {
                  "receipt_id": {
                    "type": "string",
                    "format": "uuid",
                    "example": "11111111-1111-4111-8111-111111111111"
                  },
                  "contract_id": {
                    "type": "string",
                    "format": "uuid",
                    "example": "11111111-1111-4111-8111-111111111111"
                  },
                  "version": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "status": {
                    "$ref": "#/components/schemas/ReceiptStatus"
                  },
                  "measurement_depth": {
                    "$ref": "#/components/schemas/MeasurementDepth"
                  },
                  "causal_strength": {
                    "$ref": "#/components/schemas/CausalStrength"
                  },
                  "supersedes_receipt_id": {
                    "type": "string",
                    "format": "uuid",
                    "example": "11111111-1111-4111-8111-111111111111"
                  }
                },
                "required": [
                  "receipt_id",
                  "contract_id",
                  "version",
                  "status",
                  "measurement_depth",
                  "causal_strength",
                  "supersedes_receipt_id"
                ]
              }
            },
            "required": [
              "id",
              "type",
              "created_at",
              "data"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "example": "11111111-1111-4111-8111-111111111111"
              },
              "type": {
                "type": "string",
                "enum": [
                  "outcome.contract.expired"
                ]
              },
              "created_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "data": {
                "type": "object",
                "properties": {
                  "contract_id": {
                    "type": "string",
                    "format": "uuid",
                    "example": "11111111-1111-4111-8111-111111111111"
                  },
                  "latest_receipt_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid",
                    "example": "11111111-1111-4111-8111-111111111111"
                  }
                },
                "required": [
                  "contract_id",
                  "latest_receipt_id"
                ]
              }
            },
            "required": [
              "id",
              "type",
              "created_at",
              "data"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "example": "11111111-1111-4111-8111-111111111111"
              },
              "type": {
                "type": "string",
                "enum": [
                  "experiment.data_ready"
                ]
              },
              "created_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "data": {
                "type": "object",
                "properties": {
                  "experiment_id": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "not_started",
                      "immature",
                      "insufficient_sample",
                      "ready"
                    ]
                  },
                  "causal_strength": {
                    "$ref": "#/components/schemas/CausalStrength"
                  },
                  "computed_at": {
                    "type": "string",
                    "description": "UTC instant in ISO 8601 format with millisecond precision.",
                    "example": "2026-09-13T09:00:00.000Z"
                  }
                },
                "required": [
                  "experiment_id",
                  "status",
                  "causal_strength",
                  "computed_at"
                ]
              }
            },
            "required": [
              "id",
              "type",
              "created_at",
              "data"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "example": "11111111-1111-4111-8111-111111111111"
              },
              "type": {
                "type": "string",
                "enum": [
                  "experiment.sample_ratio_mismatch"
                ]
              },
              "created_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "data": {
                "type": "object",
                "properties": {
                  "experiment_id": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "not_started",
                      "immature",
                      "insufficient_sample",
                      "ready"
                    ]
                  },
                  "causal_strength": {
                    "$ref": "#/components/schemas/CausalStrength"
                  },
                  "computed_at": {
                    "type": "string",
                    "description": "UTC instant in ISO 8601 format with millisecond precision.",
                    "example": "2026-09-13T09:00:00.000Z"
                  },
                  "p_value": {
                    "type": "number"
                  },
                  "threshold": {
                    "type": "number"
                  }
                },
                "required": [
                  "experiment_id",
                  "status",
                  "causal_strength",
                  "computed_at",
                  "p_value",
                  "threshold"
                ]
              }
            },
            "required": [
              "id",
              "type",
              "created_at",
              "data"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "example": "11111111-1111-4111-8111-111111111111"
              },
              "type": {
                "type": "string",
                "enum": [
                  "experiment.guardrail_triggered"
                ]
              },
              "created_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "data": {
                "type": "object",
                "properties": {
                  "experiment_id": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "not_started",
                      "immature",
                      "insufficient_sample",
                      "ready"
                    ]
                  },
                  "causal_strength": {
                    "$ref": "#/components/schemas/CausalStrength"
                  },
                  "computed_at": {
                    "type": "string",
                    "description": "UTC instant in ISO 8601 format with millisecond precision.",
                    "example": "2026-09-13T09:00:00.000Z"
                  },
                  "guardrail_id": {
                    "type": "string"
                  },
                  "look": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "level": {
                    "type": "number"
                  },
                  "arms": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "The arms the look found below control by more than the tolerance."
                  }
                },
                "required": [
                  "experiment_id",
                  "status",
                  "causal_strength",
                  "computed_at",
                  "guardrail_id",
                  "look",
                  "level",
                  "arms"
                ]
              }
            },
            "required": [
              "id",
              "type",
              "created_at",
              "data"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "example": "11111111-1111-4111-8111-111111111111"
              },
              "type": {
                "type": "string",
                "enum": [
                  "experiment.release_advice_changed"
                ]
              },
              "created_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "data": {
                "type": "object",
                "properties": {
                  "experiment_id": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "not_started",
                      "immature",
                      "insufficient_sample",
                      "ready"
                    ]
                  },
                  "causal_strength": {
                    "$ref": "#/components/schemas/CausalStrength"
                  },
                  "computed_at": {
                    "type": "string",
                    "description": "UTC instant in ISO 8601 format with millisecond precision.",
                    "example": "2026-09-13T09:00:00.000Z"
                  },
                  "rule_version": {
                    "type": "string",
                    "enum": [
                      "release-gate-v1"
                    ]
                  },
                  "versions": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "arm": {
                          "type": "string"
                        },
                        "from": {
                          "type": "string",
                          "enum": [
                            "ALLOW",
                            "ALLOW_WITH_CONSTRAINTS",
                            "DEFER",
                            "REVIEW",
                            "DENY"
                          ]
                        },
                        "to": {
                          "type": "string",
                          "enum": [
                            "ALLOW",
                            "ALLOW_WITH_CONSTRAINTS",
                            "DEFER",
                            "REVIEW",
                            "DENY"
                          ]
                        }
                      },
                      "required": [
                        "arm",
                        "from",
                        "to"
                      ]
                    },
                    "minItems": 1,
                    "description": "Every version whose advice changed. Read the release check for the reasons."
                  }
                },
                "required": [
                  "experiment_id",
                  "status",
                  "causal_strength",
                  "computed_at",
                  "rule_version",
                  "versions"
                ]
              }
            },
            "required": [
              "id",
              "type",
              "created_at",
              "data"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "example": "11111111-1111-4111-8111-111111111111"
              },
              "type": {
                "type": "string",
                "enum": [
                  "data_quality.degraded"
                ]
              },
              "created_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "data": {
                "type": "object",
                "properties": {
                  "version": {
                    "type": "string",
                    "enum": [
                      "data-quality-v1"
                    ]
                  },
                  "as_of": {
                    "type": "string",
                    "description": "When the check that found it ran.",
                    "example": "2026-09-13T09:00:00.000Z"
                  },
                  "checks": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "duplicate_submissions",
                            "late_arrival",
                            "unregistered_capability",
                            "assistance_contamination",
                            "missing_baseline",
                            "no_followup",
                            "followup_not_mature",
                            "item_reuse_blocks_transfer",
                            "exposure_without_assignment",
                            "sample_ratio_mismatch",
                            "differential_missingness"
                          ]
                        },
                        "from": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "enum": [
                            "ok",
                            "warning",
                            "critical",
                            "not_applicable",
                            null
                          ],
                          "description": "The state the check before found; null the first time."
                        },
                        "to": {
                          "type": "string",
                          "enum": [
                            "ok",
                            "warning",
                            "critical",
                            "not_applicable"
                          ]
                        }
                      },
                      "required": [
                        "code",
                        "from",
                        "to"
                      ]
                    },
                    "minItems": 1,
                    "description": "Every check that became worse. Read the report for the counts."
                  }
                },
                "required": [
                  "version",
                  "as_of",
                  "checks"
                ]
              }
            },
            "required": [
              "id",
              "type",
              "created_at",
              "data"
            ]
          }
        ],
        "description": "The JSON body of a notice. `id` is the event; the `OutFigure-Webhook-Id` header is the delivery, the same on every retry and replay."
      },
      "ReceiptStatus": {
        "type": "string",
        "enum": [
          "provisional",
          "evaluated",
          "insufficient_evidence"
        ],
        "description": "`provisional`: at least one role has its evidence while a later window is still open. `evaluated`: every role has its evidence or its window has closed. `insufficient_evidence`: every window closed and no role was satisfied. A missing follow-up is never a failure."
      },
      "MeasurementDepth": {
        "type": "string",
        "enum": [
          "MD0",
          "MD1",
          "MD2",
          "MD3",
          "MD4"
        ],
        "description": "`MD0`: no usable evidence after the intervention. `MD1`: proxy evidence only. `MD2`: direct performance soon after. `MD3`: direct performance at least a day later. `MD4`: that, on an unseen or transfer task, or an external assessment."
      },
      "CausalStrength": {
        "type": "string",
        "enum": [
          "CS0",
          "CS1",
          "CS2",
          "CS3",
          "CS4"
        ],
        "description": "`CS0`: no attribution. `CS1`: the outcome followed the intervention, with no control. `CS2`: an adjusted observational design, which this build does not run. `CS3`: randomized assignment with a declared analysis. `CS4`: replicated randomized evidence, which this build never assigns automatically."
      },
      "IngestionReceipt": {
        "type": "object",
        "properties": {
          "receipt_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "accepted_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Events stored for the first time by this request."
          },
          "duplicate_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Events whose event_id and content were already stored. They are not counted again."
          },
          "job_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid",
              "example": "11111111-1111-4111-8111-111111111111"
            },
            "description": "Processing jobs for the events in this request, including earlier jobs of duplicates."
          },
          "processing_status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "completed",
              "failed"
            ],
            "description": "Acknowledgement means stored, not yet reflected in state. Poll GET /v1/jobs/{id}."
          },
          "request_id": {
            "type": "string",
            "description": "Server-generated request identifier. Quote it when reporting a problem.",
            "example": "req_01j9demo0000000000000000"
          }
        },
        "required": [
          "receipt_id",
          "accepted_count",
          "duplicate_count",
          "job_ids",
          "processing_status",
          "request_id"
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "example": "event_id_conflict"
              },
              "message": {
                "type": "string",
                "example": "This event ID already exists with different content."
              },
              "request_id": {
                "type": "string",
                "description": "Server-generated request identifier. Quote it when reporting a problem.",
                "example": "req_01j9demo0000000000000000"
              },
              "details": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ErrorDetail"
                }
              },
              "retryable": {
                "type": "boolean",
                "description": "True when retrying the identical request (same Idempotency-Key) may succeed."
              }
            },
            "required": [
              "code",
              "message",
              "request_id"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "ErrorDetail": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "minimum": 0,
            "description": "Zero-based event index within a batch."
          },
          "line": {
            "type": "integer",
            "minimum": 1,
            "description": "One-based line of an uploaded file where the problem is."
          },
          "field": {
            "type": "string",
            "description": "Field path, without the submitted value."
          },
          "code": {
            "type": "string",
            "description": "Stable machine-readable reason."
          }
        },
        "required": [
          "code"
        ]
      },
      "LearningEvent": {
        "type": "object",
        "properties": {
          "schema_version": {
            "type": "string",
            "enum": [
              "1.0"
            ]
          },
          "event_id": {
            "type": "string",
            "description": "Unique per project. Resending the same event with identical content is acknowledged as a duplicate; different content returns 409.",
            "example": "evt_demo_0001"
          },
          "event_type": {
            "$ref": "#/components/schemas/EventType"
          },
          "learner_id": {
            "type": "string",
            "description": "Pseudonymous learner identifier, unique within the project.",
            "example": "learner_demo_01"
          },
          "occurred_at": {
            "type": "string",
            "description": "ISO 8601 instant with an explicit offset (`Z` or `±hh:mm`), at most millisecond precision. Stored and returned in UTC.",
            "example": "2026-09-13T09:00:00Z"
          },
          "source": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "System that observed the event.",
            "example": "partner_backend"
          },
          "course_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "session_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Learning session identifier. Without it, eligibility falls back to attempt grouping and sessions cannot be counted.",
            "example": "learner_demo_01"
          },
          "item_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "item_version": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "Version of the item content the learner saw. A content edit requires a new version.",
            "example": "1"
          },
          "attempt_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "attempt_number": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 10000
          },
          "response_kind": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ResponseKind"
              },
              {
                "type": "null"
              }
            ]
          },
          "correct": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "score": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1
          },
          "rubric_version": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$"
          },
          "latency_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 3600000,
            "description": "Time measured by the source. Not a proxy for ability."
          },
          "hint_count": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 100,
            "description": "Missing means unknown; zero means explicitly none."
          },
          "assistance": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Assistance"
              },
              {
                "type": "null"
              }
            ]
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Optional learner self-report in [0,1]. Never inferred from timing."
          },
          "selected_option_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "feedback_seen": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether feedback was visible for the measured attempt."
          },
          "answer_revealed": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether the correct answer was shown to the learner before or during this attempt. Missing means unknown and is never read as false."
          },
          "collaborated": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether the learner worked on this attempt with another person. Missing means unknown and is never read as false."
          },
          "assistance_source": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/AssistanceSource"
              },
              {
                "type": "null"
              }
            ]
          },
          "construct_type": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ConstructType"
              },
              {
                "type": "null"
              }
            ]
          },
          "assessment_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The assessment this response belongs to, when it is part of one.",
            "example": "learner_demo_01"
          },
          "assessment_version": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "Version of that assessment. Required with assessment_id, and only with it."
          },
          "intended_delay_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 7776000,
            "description": "The delay the product planned between teaching and this measurement. Missing means unknown, never zero."
          },
          "independent_of_teaching": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether this measurement was produced independently of the teaching being evaluated: different content, or an assessment the teaching side did not choose. Missing means unknown."
          },
          "intervention_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "provenance": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "intervention_kind": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "human",
                  "static_content",
                  "generic_tutor",
                  "ai_tutor",
                  "retrieval_practice",
                  "explanation",
                  "hint",
                  "feedback",
                  "customer_defined",
                  "unknown",
                  null
                ],
                "description": "What kind of teaching action was delivered."
              },
              "provider": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                "description": "Who supplied the system that delivered it, as your system names them."
              },
              "model_family": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                "description": "Model family, as reported."
              },
              "model_id": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                "description": "Exact model identifier or version, as reported."
              },
              "policy_version": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                "description": "Release of the product or tutoring policy that chose the action."
              },
              "prompt_version": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                "description": "Opaque, immutable ID of the prompt or system policy in use. Never the prompt text."
              },
              "experiment_id": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                "description": "Experiment this delivery belonged to. Required with arm."
              },
              "arm": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                "description": "Experiment arm. Required with experiment_id, and only with it."
              },
              "human_review": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "none",
                  "reviewed",
                  "unknown",
                  null
                ],
                "description": "Whether a person reviewed the delivered content before the learner saw it."
              }
            },
            "additionalProperties": false,
            "description": "Only on intervention events: what was delivered and by which system and release (PROV-01)."
          },
          "metadata": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/EventMetadata"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "schema_version",
          "event_id",
          "event_type",
          "learner_id",
          "occurred_at",
          "source"
        ],
        "additionalProperties": false,
        "description": "One observed learning event. Required for all types: schema_version, event_id, event_type, learner_id, occurred_at, source. Responses additionally require item_id, item_version, attempt_id, attempt_number, response_kind and the outcome that matches the kind."
      },
      "EventType": {
        "type": "string",
        "enum": [
          "response",
          "exposure",
          "hint",
          "feedback",
          "intervention"
        ],
        "description": "`response`: the learner answered an item. `exposure`: content was shown (not evidence of mastery). `hint`/`feedback`: assistance delivered. `intervention`: an action actually delivered, not merely recommended."
      },
      "ResponseKind": {
        "type": [
          "string",
          "null"
        ],
        "enum": [
          "binary",
          "partial_credit",
          "unscored",
          null
        ],
        "description": "`binary` requires `correct`. `partial_credit` requires `score` in [0,1] and `rubric_version`. `unscored` carries neither."
      },
      "Assistance": {
        "type": [
          "string",
          "null"
        ],
        "enum": [
          "none",
          "hints",
          "external",
          "unknown",
          null
        ],
        "description": "Help available or used for this attempt. Omit or send `unknown` when not known; unknown assistance is never assumed to be unassisted."
      },
      "AssistanceSource": {
        "type": [
          "string",
          "null"
        ],
        "enum": [
          "platform_telemetry",
          "learner_report",
          "proctored",
          "unknown",
          null
        ],
        "description": "How the assistance fields for this attempt were established. `platform_telemetry`: the product recorded it. `learner_report`: the learner said so. `proctored`: a person or service supervised the attempt. Omit or send `unknown` when not known. No source establishes that the learner had no help outside the platform."
      },
      "ConstructType": {
        "type": [
          "string",
          "null"
        ],
        "enum": [
          "practice",
          "formative_check",
          "immediate_post_test",
          "delayed_check",
          "near_transfer",
          "far_transfer",
          "external_assessment",
          null
        ],
        "description": "What this response was meant to measure, as the product intended it. `practice`: work while learning. `formative_check`: a check during learning. `immediate_post_test`: measured right after teaching. `delayed_check`: measured after a planned delay. `near_transfer` / `far_transfer`: a related or a distant task. `external_assessment`: measured outside this product. Omit when the product does not distinguish them; an immediate and a delayed measurement are never the same target."
      },
      "EventMetadata": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "locale": {
            "type": "string",
            "maxLength": 35,
            "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$",
            "description": "BCP 47 language tag of the content shown.",
            "example": "en"
          },
          "client_platform": {
            "type": "string",
            "enum": [
              "web",
              "ios",
              "android",
              "desktop",
              "other"
            ],
            "description": "Surface on which the learner interacted."
          },
          "app_version": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "Version of the integrating application."
          },
          "delivery_mode": {
            "type": "string",
            "enum": [
              "practice",
              "quiz",
              "exam_simulation",
              "review",
              "diagnostic",
              "other"
            ],
            "description": "Context in which the item was delivered."
          }
        },
        "additionalProperties": false,
        "description": "Allowlisted keys only, at most 2 KiB. Free text, answers, names and hidden identifiers are not accepted."
      },
      "CorrectionRequest": {
        "type": "object",
        "properties": {
          "supersedes_event_id": {
            "type": "string",
            "description": "The stored event this corrects. Only the latest version of an event can be corrected: if it already has a correction, correct that one instead.",
            "example": "evt_demo_0001"
          },
          "replacement": {
            "type": "object",
            "properties": {
              "schema_version": {
                "type": "string",
                "enum": [
                  "1.0"
                ]
              },
              "event_id": {
                "type": "string",
                "description": "Unique per project. Resending the same event with identical content is acknowledged as a duplicate; different content returns 409.",
                "example": "evt_demo_0001"
              },
              "event_type": {
                "$ref": "#/components/schemas/EventType"
              },
              "learner_id": {
                "type": "string",
                "description": "Pseudonymous learner identifier, unique within the project.",
                "example": "learner_demo_01"
              },
              "occurred_at": {
                "type": "string",
                "description": "ISO 8601 instant with an explicit offset (`Z` or `±hh:mm`), at most millisecond precision. Stored and returned in UTC.",
                "example": "2026-09-13T09:00:00Z"
              },
              "source": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                "description": "System that observed the event.",
                "example": "partner_backend"
              },
              "course_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
                "example": "learner_demo_01"
              },
              "session_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Learning session identifier. Without it, eligibility falls back to attempt grouping and sessions cannot be counted.",
                "example": "learner_demo_01"
              },
              "item_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
                "example": "learner_demo_01"
              },
              "item_version": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                "description": "Version of the item content the learner saw. A content edit requires a new version.",
                "example": "1"
              },
              "attempt_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
                "example": "learner_demo_01"
              },
              "attempt_number": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 1,
                "maximum": 10000
              },
              "response_kind": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/ResponseKind"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "correct": {
                "type": [
                  "boolean",
                  "null"
                ]
              },
              "score": {
                "type": [
                  "number",
                  "null"
                ],
                "minimum": 0,
                "maximum": 1
              },
              "rubric_version": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$"
              },
              "latency_ms": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "maximum": 3600000,
                "description": "Time measured by the source. Not a proxy for ability."
              },
              "hint_count": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "maximum": 100,
                "description": "Missing means unknown; zero means explicitly none."
              },
              "assistance": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/Assistance"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "confidence": {
                "type": [
                  "number",
                  "null"
                ],
                "minimum": 0,
                "maximum": 1,
                "description": "Optional learner self-report in [0,1]. Never inferred from timing."
              },
              "selected_option_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
                "example": "learner_demo_01"
              },
              "feedback_seen": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "Whether feedback was visible for the measured attempt."
              },
              "answer_revealed": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "Whether the correct answer was shown to the learner before or during this attempt. Missing means unknown and is never read as false."
              },
              "collaborated": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "Whether the learner worked on this attempt with another person. Missing means unknown and is never read as false."
              },
              "assistance_source": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/AssistanceSource"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "construct_type": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/ConstructType"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "assessment_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The assessment this response belongs to, when it is part of one.",
                "example": "learner_demo_01"
              },
              "assessment_version": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                "description": "Version of that assessment. Required with assessment_id, and only with it."
              },
              "intended_delay_seconds": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "maximum": 7776000,
                "description": "The delay the product planned between teaching and this measurement. Missing means unknown, never zero."
              },
              "independent_of_teaching": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "Whether this measurement was produced independently of the teaching being evaluated: different content, or an assessment the teaching side did not choose. Missing means unknown."
              },
              "intervention_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
                "example": "learner_demo_01"
              },
              "provenance": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "intervention_kind": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "enum": [
                      "human",
                      "static_content",
                      "generic_tutor",
                      "ai_tutor",
                      "retrieval_practice",
                      "explanation",
                      "hint",
                      "feedback",
                      "customer_defined",
                      "unknown",
                      null
                    ],
                    "description": "What kind of teaching action was delivered."
                  },
                  "provider": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 64,
                    "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                    "description": "Who supplied the system that delivered it, as your system names them."
                  },
                  "model_family": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 64,
                    "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                    "description": "Model family, as reported."
                  },
                  "model_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 128,
                    "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                    "description": "Exact model identifier or version, as reported."
                  },
                  "policy_version": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 64,
                    "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                    "description": "Release of the product or tutoring policy that chose the action."
                  },
                  "prompt_version": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 128,
                    "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                    "description": "Opaque, immutable ID of the prompt or system policy in use. Never the prompt text."
                  },
                  "experiment_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 64,
                    "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                    "description": "Experiment this delivery belonged to. Required with arm."
                  },
                  "arm": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 64,
                    "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                    "description": "Experiment arm. Required with experiment_id, and only with it."
                  },
                  "human_review": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "enum": [
                      "none",
                      "reviewed",
                      "unknown",
                      null
                    ],
                    "description": "Whether a person reviewed the delivered content before the learner saw it."
                  }
                },
                "additionalProperties": false,
                "description": "Only on intervention events: what was delivered and by which system and release (PROV-01)."
              },
              "metadata": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/EventMetadata"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "schema_version",
              "event_id",
              "event_type",
              "learner_id",
              "occurred_at",
              "source"
            ],
            "additionalProperties": false,
            "description": "The complete corrected event, with a new event_id. It keeps the learner, event type, item and item version of the event it replaces; nothing else is inherited, so send every field."
          }
        },
        "required": [
          "supersedes_event_id",
          "replacement"
        ],
        "additionalProperties": false
      },
      "AssignmentReceipt": {
        "type": "object",
        "properties": {
          "recorded_count": {
            "type": "integer",
            "minimum": 0
          },
          "duplicate_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Assignments already recorded with identical content. Not recorded twice."
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer",
                  "minimum": 0
                },
                "assignment_id": {
                  "type": "string",
                  "format": "uuid",
                  "example": "11111111-1111-4111-8111-111111111111"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "recorded",
                    "duplicate"
                  ]
                }
              },
              "required": [
                "index",
                "assignment_id",
                "status"
              ]
            },
            "description": "One result per submitted assignment, in request order. Learner IDs are not repeated, so a stored response never outlives an erasure with them."
          }
        },
        "required": [
          "recorded_count",
          "duplicate_count",
          "results"
        ]
      },
      "AssignmentBatch": {
        "type": "object",
        "properties": {
          "assignments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AssignmentInput"
            },
            "minItems": 1,
            "maxItems": 100
          }
        },
        "required": [
          "assignments"
        ],
        "additionalProperties": false
      },
      "AssignmentInput": {
        "type": "object",
        "properties": {
          "learner_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "experiment_id": {
            "$ref": "#/components/schemas/ExperimentId"
          },
          "arm": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "example": "treatment"
          },
          "assigned_at": {
            "type": "string",
            "description": "When the assignment was made, ISO 8601 with an offset. Not more than 5 minutes in the future or 90 days in the past.",
            "example": "2026-09-13T08:55:00Z"
          },
          "assignment_method": {
            "$ref": "#/components/schemas/AssignmentMethod"
          },
          "intended_intervention_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The intervention this arm was meant to deliver, as your system identifies it.",
            "example": "learner_demo_01"
          },
          "analysis_version": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "The analysis plan this experiment is registered under, if one exists."
          },
          "source": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "The system that made the assignment. `outfigure` is reserved for the assignments OutFigure makes in the experiments it runs.",
            "example": "experiment_service"
          }
        },
        "required": [
          "learner_id",
          "experiment_id",
          "arm",
          "assigned_at",
          "assignment_method",
          "source"
        ],
        "additionalProperties": false
      },
      "ExperimentId": {
        "type": "string",
        "maxLength": 64,
        "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
        "description": "The experiment as your system identifies it.",
        "example": "exp_worked_examples"
      },
      "AssignmentMethod": {
        "type": "string",
        "enum": [
          "randomized",
          "stratified_randomized",
          "rule_based",
          "self_selected",
          "manual",
          "unknown"
        ],
        "description": "`randomized` and `stratified_randomized`: chance decided the arm. `rule_based`: a deterministic rule did. `self_selected`: the learner chose. `manual`: a person chose. `unknown`: not recorded. Only randomized methods can support a causal comparison, and even then only with a declared analysis."
      },
      "AssignmentList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Assignment"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null when there are no more results."
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "Assignment": {
        "type": "object",
        "properties": {
          "assignment_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "learner_id": {
            "type": "string"
          },
          "experiment_id": {
            "type": "string"
          },
          "arm": {
            "type": "string"
          },
          "assigned_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "assignment_method": {
            "$ref": "#/components/schemas/AssignmentMethod"
          },
          "intended_intervention_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "analysis_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": "string"
          },
          "recorded_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "assignment_id",
          "learner_id",
          "experiment_id",
          "arm",
          "assigned_at",
          "assignment_method",
          "intended_intervention_id",
          "analysis_version",
          "source",
          "recorded_at"
        ]
      },
      "SessionCreateRequest": {
        "type": "object",
        "properties": {
          "candidate_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "role": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 120
          },
          "domain": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "external_session_ref": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "source_tool": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 120
          }
        },
        "required": [
          "candidate_id",
          "domain"
        ],
        "additionalProperties": false,
        "description": "Opens a scoring session beside an interview the customer already runs. Nothing about the candidate beyond an opaque identifier is accepted."
      },
      "TranscriptIngestRequest": {
        "type": "object",
        "properties": {
          "responses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SessionResponse"
            },
            "minItems": 1,
            "maxItems": 50
          }
        },
        "required": [
          "responses"
        ],
        "additionalProperties": false,
        "description": "Adds responses to an open session. Ingestion is idempotent on the session and item pair: the same answer sent twice is one response."
      },
      "SessionResponse": {
        "type": "object",
        "properties": {
          "item_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "answer": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20000
          },
          "assistance": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "none",
              "ai_tool",
              "documentation",
              "unreported",
              null
            ]
          },
          "responded_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "duration_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 86400
          }
        },
        "required": [
          "item_id",
          "answer",
          "responded_at"
        ],
        "additionalProperties": false,
        "description": "One answer inside a session. Duration is stored for reporting and is never an input to a reading: no timing signal is interpreted as evidence about a person."
      },
      "JudgmentProfile": {
        "type": "object",
        "properties": {
          "profile_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "session_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "candidate_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "issued_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "evidence_cutoff": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "rubric_version": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "scorer_version": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "judge_model": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 120
          },
          "judge_prompt_version": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 60
          },
          "readings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DimensionReading"
            }
          },
          "responses_scored": {
            "type": "integer",
            "minimum": 0
          },
          "design_status": {
            "type": "string",
            "enum": [
              "descriptive"
            ],
            "description": "Counts of what was recorded. It explains nothing and predicts nothing."
          }
        },
        "required": [
          "profile_id",
          "session_id",
          "candidate_id",
          "issued_at",
          "evidence_cutoff",
          "rubric_version",
          "scorer_version",
          "judge_model",
          "judge_prompt_version",
          "readings",
          "responses_scored",
          "design_status"
        ],
        "additionalProperties": false,
        "description": "A frozen reading of one session. It describes what the responses showed. It is not a prediction of job performance, and no evidence that it predicts one exists yet."
      },
      "DimensionReading": {
        "type": "object",
        "properties": {
          "dimension": {
            "$ref": "#/components/schemas/JudgmentDimension"
          },
          "reading": {
            "$ref": "#/components/schemas/ReadingLevel"
          },
          "evidence_count": {
            "type": "integer",
            "minimum": 0
          },
          "evidence_item_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
              "example": "learner_demo_01"
            }
          },
          "score": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1
          },
          "interval": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "not_estimated"
                ]
              },
              "low": {
                "type": "null"
              },
              "high": {
                "type": "null"
              }
            },
            "required": [
              "kind",
              "low",
              "high"
            ],
            "additionalProperties": false,
            "description": "No interval method has been validated, so none is reported."
          }
        },
        "required": [
          "dimension",
          "reading",
          "evidence_count",
          "evidence_item_ids",
          "score",
          "interval"
        ],
        "additionalProperties": false
      },
      "JudgmentDimension": {
        "type": "string",
        "enum": [
          "recall_only",
          "partial_understanding",
          "confabulation_adjacent",
          "transfer"
        ],
        "description": "`recall_only`: right on the standard case, wrong once it is not standard. `partial_understanding`: catches a shallow error, misses one needing causal reasoning. `confabulation_adjacent`: fluent and confident, and the explanation does not survive follow-up. `transfer`: adapts correctly when the ground shifts."
      },
      "ReadingLevel": {
        "type": "string",
        "enum": [
          "not_enough_evidence",
          "weak",
          "mixed",
          "strong"
        ],
        "description": "`not_enough_evidence` is reported whenever the responses behind a dimension are too few to read, and is never rounded into a score."
      },
      "ResponseVerdict": {
        "type": "object",
        "properties": {
          "item_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "task_type": {
            "$ref": "#/components/schemas/TaskType"
          },
          "verdict": {
            "$ref": "#/components/schemas/Verdict"
          },
          "rubric_line": {
            "type": "string",
            "minLength": 1,
            "maxLength": 400
          },
          "signals_found": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 120
            }
          },
          "signals_missed": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 120
            }
          },
          "proposed_by": {
            "type": "string",
            "enum": [
              "rubric",
              "model"
            ]
          }
        },
        "required": [
          "item_id",
          "task_type",
          "verdict",
          "rubric_line",
          "signals_found",
          "signals_missed",
          "proposed_by"
        ],
        "additionalProperties": false,
        "description": "One judged response. Every reading in a profile points at these, so no figure exists that cannot be traced to an answer the candidate gave."
      },
      "TaskType": {
        "type": "string",
        "enum": [
          "seeded_error",
          "constraint_shift",
          "defend_decision",
          "delayed_recheck"
        ],
        "description": "`seeded_error`: review an artifact holding a planted, context-dependent flaw. `constraint_shift`: a requirement changes mid-task in a way the first approach cannot absorb. `defend_decision`: adversarial follow-up on a decision the candidate made. `delayed_recheck`: a structurally related question later in the same pipeline."
      },
      "Verdict": {
        "type": "string",
        "enum": [
          "found_flaw",
          "missed_flaw",
          "endorsed_flawed",
          "rederived",
          "repatched",
          "held",
          "collapsed",
          "retained",
          "not_retained",
          "unscored"
        ],
        "description": "Seeded error: `found_flaw`, `missed_flaw`, or `endorsed_flawed` when the answer states the artifact is sound. Constraint shift: `rederived` or `repatched`. Defend: `held` or `collapsed`. Delayed re-check: `retained` or `not_retained`. `unscored` when the rubric cannot decide, which is not a failure."
      },
      "JudgmentOutcomeRequest": {
        "type": "object",
        "properties": {
          "kind": {
            "$ref": "#/components/schemas/JudgmentOutcomeKind"
          },
          "value": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "scale": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "observed_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "source": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "blind": {
            "type": "boolean",
            "description": "True when the source had not seen the profile before recording this."
          }
        },
        "required": [
          "kind",
          "value",
          "scale",
          "observed_at",
          "source",
          "blind"
        ],
        "additionalProperties": false,
        "description": "Records what happened after a profile was issued. The service refuses an outcome observed before the profile was frozen, because that is not an outcome."
      },
      "JudgmentOutcomeKind": {
        "type": "string",
        "enum": [
          "panel_judgment",
          "hire_decision",
          "performance_rating"
        ],
        "description": "`panel_judgment`: a blind human reading of the same session. `hire_decision`: what the customer decided. `performance_rating`: a later on-the-job signal the customer recorded."
      },
      "CalibrationReport": {
        "type": "object",
        "properties": {
          "project_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "computed_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "outcome_kind": {
            "$ref": "#/components/schemas/JudgmentOutcomeKind"
          },
          "profiles_issued": {
            "type": "integer",
            "minimum": 0
          },
          "outcomes_matched": {
            "type": "integer",
            "minimum": 0
          },
          "coverage": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1
          },
          "blind_outcomes": {
            "type": "integer",
            "minimum": 0
          },
          "agreement": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "mean_absolute_error",
                  "insufficient_sample"
                ]
              },
              "value": {
                "type": [
                  "number",
                  "null"
                ],
                "minimum": 0,
                "maximum": 1
              },
              "sample": {
                "type": "integer",
                "minimum": 0
              }
            },
            "required": [
              "kind",
              "value",
              "sample"
            ],
            "additionalProperties": false,
            "description": "Reported only from blind, matched outcomes, and only above the minimum sample. Below it the report says insufficient_sample rather than printing a number a handful of cases would swing."
          },
          "design_status": {
            "type": "string",
            "enum": [
              "descriptive"
            ],
            "description": "Counts of what was recorded. It explains nothing and predicts nothing."
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 400
            }
          }
        },
        "required": [
          "project_id",
          "computed_at",
          "outcome_kind",
          "profiles_issued",
          "outcomes_matched",
          "coverage",
          "blind_outcomes",
          "agreement",
          "design_status",
          "limitations"
        ],
        "additionalProperties": false,
        "description": "Predicted against observed for one customer, one outcome kind. Descriptive: it says how closely the readings matched what was later recorded, and never that the reading caused the outcome."
      },
      "MarkingSession": {
        "type": "object",
        "properties": {
          "session_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "name": {
            "type": "string"
          },
          "second_reading_source": {
            "type": "string",
            "enum": [
              "second_marker",
              "second_reader_model",
              "moderator"
            ]
          },
          "created_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "questions": {
            "type": "integer"
          },
          "scripts": {
            "type": "integer"
          },
          "markers": {
            "type": "integer"
          },
          "signed_off_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "When an owner or admin signed the session off; null until then. A signed-off session takes no more scripts or decisions.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "results_due": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "The date results are due, as YYYY-MM-DD, set in the console; null if not set."
          },
          "kind": {
            "$ref": "#/components/schemas/MarkingSessionKind"
          }
        },
        "required": [
          "session_id",
          "name",
          "second_reading_source",
          "created_at",
          "questions",
          "scripts",
          "markers",
          "signed_off_at",
          "results_due",
          "kind"
        ]
      },
      "MarkingSessionKind": {
        "type": "string",
        "enum": [
          "live",
          "past_sitting"
        ],
        "description": "`live`: a sitting being marked, with flags for senior markers to decide. `past_sitting`: a sitting whose results were issued, uploaded to see what a second reader would have caught; it takes no decisions and is never signed off."
      },
      "CreateMarkingSessionRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "second_reading_source": {
            "type": "string",
            "enum": [
              "second_marker",
              "second_reader_model",
              "moderator"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "live",
              "past_sitting"
            ],
            "description": "`live` when left out."
          },
          "questions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarkingQuestionInput"
            },
            "minItems": 1,
            "maxItems": 50
          }
        },
        "required": [
          "name",
          "second_reading_source",
          "questions"
        ],
        "additionalProperties": false
      },
      "MarkingQuestionInput": {
        "type": "object",
        "properties": {
          "question_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "max_mark": {
            "type": "integer",
            "minimum": 1,
            "maximum": 1000
          },
          "tolerance": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "question_id",
          "title",
          "max_mark",
          "tolerance"
        ],
        "additionalProperties": false
      },
      "MarkingSessionList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarkingSession"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null when there are no more results."
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "MarkingSessionReport": {
        "type": "object",
        "properties": {
          "session": {
            "$ref": "#/components/schemas/MarkingSession"
          },
          "questions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarkingQuestion"
            }
          },
          "version": {
            "type": "string",
            "description": "The rules that drew the flags and patterns."
          },
          "minimum_marker_scripts": {
            "type": "integer",
            "description": "Scripts a marker needs before their pattern is described."
          },
          "summary": {
            "type": "object",
            "properties": {
              "scripts": {
                "type": "integer"
              },
              "second_read": {
                "type": "integer",
                "description": "Scripts read a second time; the rest stand as marked, unchecked."
              },
              "markers": {
                "type": "integer"
              },
              "flagged": {
                "type": "integer"
              },
              "decided": {
                "type": "integer"
              },
              "changed": {
                "type": "integer"
              },
              "kept": {
                "type": "integer"
              },
              "open": {
                "type": "integer",
                "description": "Flagged scripts still waiting for a senior marker."
              }
            },
            "required": [
              "scripts",
              "second_read",
              "markers",
              "flagged",
              "decided",
              "changed",
              "kept",
              "open"
            ]
          },
          "markers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarkerSummary"
            }
          },
          "agreement": {
            "$ref": "#/components/schemas/MarkingAgreement"
          },
          "flags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarkingFlag"
            },
            "description": "The largest gaps first."
          },
          "reasons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReasonCount"
            },
            "description": "Why decided flags were changed or kept, from the latest decision on each, every reason listed in a fixed order."
          },
          "assignments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarkingAssignment"
            },
            "description": "Who was given which questions or markers, oldest first."
          },
          "queue": {
            "$ref": "#/components/schemas/MarkingQueue"
          },
          "sign_off": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "signed_off_by": {
                "type": "string",
                "description": "Who signed it off: the operator's sign-in subject."
              },
              "signed_off_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "scripts": {
                "type": "integer",
                "description": "Scripts in the session when it was signed off."
              },
              "second_read": {
                "type": "integer",
                "description": "Of those, scripts read a second time."
              },
              "flagged": {
                "type": "integer",
                "description": "Scripts flagged, every one decided."
              },
              "changed": {
                "type": "integer",
                "description": "Flagged scripts whose mark a senior marker changed."
              },
              "final_marks_sha256": {
                "type": "string",
                "pattern": "^[0-9a-f]{64}$",
                "description": "SHA-256, in hex, of the final-marks file exactly as it stood at sign-off. A file downloaded since matches it unless scripts were removed."
              },
              "scripts_removed_since": {
                "type": "integer",
                "description": "Scripts removed since sign-off by a candidate's erasure. None can be added."
              },
              "final_marks_unchanged": {
                "type": "boolean",
                "description": "Whether the final-marks file today is byte for byte the one signed off."
              }
            },
            "required": [
              "signed_off_by",
              "signed_off_at",
              "scripts",
              "second_read",
              "flagged",
              "changed",
              "final_marks_sha256",
              "scripts_removed_since",
              "final_marks_unchanged"
            ],
            "description": "Null until an owner or admin signs the session off in the console, which needs every flag decided."
          },
          "past_sitting": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "version": {
                "type": "string"
              },
              "scripts": {
                "type": "integer"
              },
              "second_read": {
                "type": "integer"
              },
              "flagged": {
                "type": "integer"
              },
              "later_known": {
                "type": "integer",
                "description": "Scripts with a later outcome known."
              },
              "changed": {
                "type": "integer",
                "description": "Of those, scripts whose mark changed from the first mark."
              },
              "caught": {
                "type": "integer",
                "description": "Changed later, and flagged: caught before results."
              },
              "within_gap": {
                "type": "integer"
              },
              "not_read_twice": {
                "type": "integer"
              },
              "flagged_changed": {
                "type": "integer"
              },
              "flagged_kept": {
                "type": "integer"
              },
              "flagged_unknown": {
                "type": "integer"
              },
              "by_stage": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/LaterStageCount"
                }
              },
              "changes": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/LaterChange"
                },
                "description": "Every later change, the largest first."
              }
            },
            "required": [
              "version",
              "scripts",
              "second_read",
              "flagged",
              "later_known",
              "changed",
              "caught",
              "within_gap",
              "not_read_twice",
              "flagged_changed",
              "flagged_kept",
              "flagged_unknown",
              "by_stage",
              "changes"
            ],
            "description": "For a past sitting, what a second reader would have caught; null for a live session."
          }
        },
        "required": [
          "session",
          "questions",
          "version",
          "minimum_marker_scripts",
          "summary",
          "markers",
          "agreement",
          "flags",
          "reasons",
          "assignments",
          "queue",
          "sign_off",
          "past_sitting"
        ]
      },
      "MarkingQuestion": {
        "type": "object",
        "properties": {
          "question_id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "max_mark": {
            "type": "integer"
          },
          "tolerance": {
            "type": "integer",
            "description": "The allowed gap: how far the marker's mark may sit from the second reading before the script is flagged."
          },
          "mark_scheme": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "scheme_id": {
                "type": "string"
              },
              "version": {
                "type": "integer",
                "minimum": 1
              },
              "linked_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              }
            },
            "required": [
              "scheme_id",
              "version",
              "linked_at"
            ],
            "description": "The mark scheme version the question is marked against now, or null."
          }
        },
        "required": [
          "question_id",
          "title",
          "max_mark",
          "tolerance",
          "mark_scheme"
        ]
      },
      "MarkerSummary": {
        "type": "object",
        "properties": {
          "marker_id": {
            "type": "string"
          },
          "scripts": {
            "type": "integer"
          },
          "second_read": {
            "type": "integer",
            "description": "Of the marker's scripts, those read a second time: a pattern is measured on these."
          },
          "status": {
            "type": "string",
            "enum": [
              "measured",
              "insufficient_sample"
            ]
          },
          "severity": {
            "type": [
              "number",
              "null"
            ],
            "description": "Mean of (mark - second reading) / maximum mark: below 0, harsher than the second reading."
          },
          "consistency": {
            "type": [
              "number",
              "null"
            ],
            "description": "Spread of the same differences."
          },
          "drift": {
            "type": [
              "number",
              "null"
            ],
            "description": "Mean difference in the second half of the pile minus the first half."
          },
          "flagged": {
            "type": "integer"
          },
          "patterns": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "`in line`, or any of `harsh`, `lenient`, `inconsistent`, `drifting`; empty when not measured."
          },
          "reasons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReasonCount"
            },
            "description": "Why the marker's decided flags were changed or kept, every reason listed."
          }
        },
        "required": [
          "marker_id",
          "scripts",
          "second_read",
          "status",
          "severity",
          "consistency",
          "drift",
          "flagged",
          "patterns",
          "reasons"
        ]
      },
      "ReasonCount": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "enum": [
              "missed_follow_through",
              "mark_scheme_misread",
              "arithmetic",
              "second_reading_wrong",
              "other",
              "not_given"
            ]
          },
          "changed": {
            "type": "integer",
            "description": "Flags whose mark the senior marker changed."
          },
          "kept": {
            "type": "integer",
            "description": "Flags where the senior marker kept the marker's mark."
          }
        },
        "required": [
          "reason",
          "changed",
          "kept"
        ]
      },
      "MarkingAgreement": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string"
          },
          "minimum_scripts": {
            "type": "integer"
          },
          "minimum_candidates": {
            "type": "integer"
          },
          "questions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarkingQuestionAgreement"
            }
          }
        },
        "required": [
          "version",
          "minimum_scripts",
          "minimum_candidates",
          "questions"
        ]
      },
      "MarkingQuestionAgreement": {
        "type": "object",
        "properties": {
          "question_id": {
            "type": "string"
          },
          "max_mark": {
            "type": "integer"
          },
          "scripts": {
            "type": "integer"
          },
          "candidates": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "measured",
              "insufficient_sample"
            ]
          },
          "exact": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Share of scripts where the marker and the second reading gave the same mark."
          },
          "within_one": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Share within one mark of each other."
          },
          "weighted_kappa": {
            "type": [
              "number",
              "null"
            ],
            "description": "Quadratic weighted kappa: 1 is perfect agreement, 0 is what chance would give."
          },
          "kappa_interval": {
            "type": [
              "array",
              "null"
            ],
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ],
            "description": "95% interval for the kappa, resampling candidates; null when not estimated."
          },
          "marker_minus_second_reading": {
            "type": [
              "number",
              "null"
            ],
            "description": "Mean of the marker's mark minus the second reading, in marks."
          }
        },
        "required": [
          "question_id",
          "max_mark",
          "scripts",
          "candidates",
          "status",
          "exact",
          "within_one",
          "weighted_kappa",
          "kappa_interval",
          "marker_minus_second_reading"
        ]
      },
      "MarkingFlag": {
        "type": "object",
        "properties": {
          "script_id": {
            "type": "string"
          },
          "marker_id": {
            "type": "string"
          },
          "question_id": {
            "type": "string"
          },
          "mark": {
            "type": "integer"
          },
          "second_reading": {
            "type": "integer"
          },
          "gap": {
            "type": "integer",
            "description": "Second reading minus the marker's mark."
          },
          "senior_mark": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The latest decision's mark: the one that counts. Null while the flag waits."
          },
          "upheld": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Null until decided; then whether the senior marker changed the mark."
          },
          "decided_by": {
            "type": [
              "string",
              "null"
            ]
          },
          "decided_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "missed_follow_through",
              "mark_scheme_misread",
              "arithmetic",
              "second_reading_wrong",
              "other",
              null
            ],
            "description": "The latest decision's reason; null while the flag waits or when none was given."
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          },
          "decisions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarkingDecision"
            },
            "description": "Every decision on the script, oldest first; the last is the one that counts."
          },
          "assigned_to": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Sign-in subjects of the people assigned the flag's question or its marker, in order."
          },
          "claim": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "script_id": {
                "type": "string"
              },
              "claimed_by": {
                "type": "string",
                "description": "The claimer's sign-in subject."
              },
              "claimed_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "expires_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              }
            },
            "required": [
              "script_id",
              "claimed_by",
              "claimed_at",
              "expires_at"
            ],
            "description": "Who is working on the flag now, if anyone; set and released in the console."
          },
          "final_mark": {
            "type": [
              "integer",
              "null"
            ],
            "description": "A past sitting's later outcome for the script, where known; always null in a live session."
          },
          "final_stage": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/LaterStage"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "script_id",
          "marker_id",
          "question_id",
          "mark",
          "second_reading",
          "gap",
          "senior_mark",
          "upheld",
          "decided_by",
          "decided_at",
          "reason",
          "note",
          "decisions",
          "assigned_to",
          "claim",
          "final_mark",
          "final_stage"
        ]
      },
      "MarkingDecision": {
        "type": "object",
        "properties": {
          "senior_mark": {
            "type": "integer"
          },
          "decided_by": {
            "type": "string",
            "description": "Who recorded it: an operator's sign-in subject, or `partner:` and the reference your system sent."
          },
          "decided_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "missed_follow_through",
              "mark_scheme_misread",
              "arithmetic",
              "second_reading_wrong",
              "other",
              null
            ],
            "description": "Null for a decision recorded without a reason."
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "senior_mark",
          "decided_by",
          "decided_at",
          "reason",
          "note"
        ]
      },
      "LaterStage": {
        "type": [
          "string",
          "null"
        ],
        "enum": [
          "moderation",
          "review",
          "appeal",
          null
        ],
        "description": "`moderation`: a moderator changed or confirmed it. `review`: a review of marking after results. `appeal`: an appeal."
      },
      "MarkingAssignment": {
        "type": "object",
        "properties": {
          "assignment_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "assignee": {
            "type": "string",
            "description": "The member's sign-in subject."
          },
          "kind": {
            "type": "string",
            "enum": [
              "question",
              "marker"
            ]
          },
          "key": {
            "type": "string",
            "description": "The question ID or the marker ID."
          },
          "assigned_by": {
            "type": "string"
          },
          "assigned_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "assignment_id",
          "assignee",
          "kind",
          "key",
          "assigned_by",
          "assigned_at"
        ]
      },
      "MarkingQueue": {
        "type": "object",
        "properties": {
          "unassigned_waiting": {
            "type": "integer",
            "description": "Waiting flags whose question and marker nobody is assigned."
          },
          "by_assignee": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "assignee": {
                  "type": "string"
                },
                "waiting": {
                  "type": "integer"
                },
                "decided": {
                  "type": "integer"
                }
              },
              "required": [
                "assignee",
                "waiting",
                "decided"
              ]
            },
            "description": "For each person assigned anything: their flags still waiting and those decided. A flag assigned to two people counts for both."
          }
        },
        "required": [
          "unassigned_waiting",
          "by_assignee"
        ]
      },
      "LaterStageCount": {
        "type": "object",
        "properties": {
          "stage": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/LaterStage"
              },
              {
                "type": "null"
              }
            ]
          },
          "known": {
            "type": "integer"
          },
          "changed": {
            "type": "integer"
          },
          "caught": {
            "type": "integer"
          }
        },
        "required": [
          "stage",
          "known",
          "changed",
          "caught"
        ]
      },
      "LaterChange": {
        "type": "object",
        "properties": {
          "script_id": {
            "type": "string"
          },
          "marker_id": {
            "type": "string"
          },
          "question_id": {
            "type": "string"
          },
          "mark": {
            "type": "integer",
            "description": "The first marker's mark."
          },
          "second_reading": {
            "type": [
              "integer",
              "null"
            ]
          },
          "final_mark": {
            "type": "integer"
          },
          "final_stage": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "moderation",
              "review",
              "appeal",
              null
            ],
            "description": "Null where the stage was not stated."
          },
          "caught": {
            "type": "string",
            "enum": [
              "flagged",
              "within_gap",
              "not_read_twice"
            ],
            "description": "`flagged`: a flag would have put it before a senior marker before results. `within_gap`: the readings were within the allowed gap. `not_read_twice`: the script had no second reading, so nothing could flag it."
          }
        },
        "required": [
          "script_id",
          "marker_id",
          "question_id",
          "mark",
          "second_reading",
          "final_mark",
          "final_stage",
          "caught"
        ]
      },
      "MarkSchemeVersion": {
        "type": "object",
        "properties": {
          "scheme_id": {
            "type": "string"
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "title": {
            "type": "string"
          },
          "max_mark": {
            "type": "integer"
          },
          "points": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarkPoint"
            }
          },
          "guidance": {
            "type": [
              "string",
              "null"
            ]
          },
          "format": {
            "type": "string",
            "enum": [
              "mark-scheme-v1"
            ]
          },
          "content_sha256": {
            "type": "string",
            "description": "SHA-256 of the canonical content. Sending the same content again makes no new version."
          },
          "created_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "scheme_id",
          "version",
          "title",
          "max_mark",
          "points",
          "guidance",
          "format",
          "content_sha256",
          "created_at"
        ]
      },
      "MarkPoint": {
        "type": "object",
        "properties": {
          "point_id": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$"
          },
          "marks": {
            "type": "integer",
            "minimum": 1,
            "maximum": 1000
          },
          "kind": {
            "type": "string",
            "enum": [
              "method",
              "accuracy",
              "independent"
            ]
          },
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "depends_on": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "A method point this point needs: without it, this point is not given."
          },
          "follow_through": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "May be given on the candidate's own earlier figure."
          },
          "alternatives": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 500
            },
            "maxItems": 20,
            "description": "Other answers that earn the point."
          }
        },
        "required": [
          "point_id",
          "marks",
          "kind",
          "description"
        ],
        "additionalProperties": false
      },
      "MarkSchemeRequest": {
        "type": "object",
        "properties": {
          "scheme_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "max_mark": {
            "type": "integer",
            "minimum": 1,
            "maximum": 1000
          },
          "points": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarkPoint"
            },
            "minItems": 1,
            "maxItems": 100
          },
          "guidance": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 20000,
            "description": "Examiner guidance for the whole question, kept with the scheme."
          }
        },
        "required": [
          "scheme_id",
          "title",
          "max_mark",
          "points"
        ],
        "additionalProperties": false
      },
      "MarkSchemeList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarkSchemeSummary"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null when there are no more results."
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "MarkSchemeSummary": {
        "type": "object",
        "properties": {
          "scheme_id": {
            "type": "string"
          },
          "latest_version": {
            "type": "integer",
            "minimum": 1
          },
          "title": {
            "type": "string"
          },
          "max_mark": {
            "type": "integer"
          },
          "points": {
            "type": "integer",
            "description": "Points in the latest version."
          },
          "updated_at": {
            "type": "string",
            "description": "When the latest version was made.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "scheme_id",
          "latest_version",
          "title",
          "max_mark",
          "points",
          "updated_at"
        ]
      },
      "QuestionMarkSchemeLink": {
        "type": "object",
        "properties": {
          "scheme_id": {
            "type": "string"
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "linked_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "session_id": {
            "type": "string"
          },
          "question_id": {
            "type": "string"
          }
        },
        "required": [
          "scheme_id",
          "version",
          "linked_at",
          "session_id",
          "question_id"
        ]
      },
      "LinkMarkSchemeRequest": {
        "type": "object",
        "properties": {
          "scheme_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "version": {
            "type": "integer",
            "minimum": 1
          }
        },
        "required": [
          "scheme_id",
          "version"
        ],
        "additionalProperties": false
      },
      "MarksUploadResult": {
        "type": "object",
        "properties": {
          "session_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "added": {
            "type": "integer",
            "description": "Scripts stored by this request."
          },
          "unchanged": {
            "type": "integer",
            "description": "Scripts already held with exactly these values, so a request can be sent again safely."
          },
          "scripts_in_session": {
            "type": "integer"
          }
        },
        "required": [
          "session_id",
          "added",
          "unchanged",
          "scripts_in_session"
        ]
      },
      "SubmitScriptsRequest": {
        "type": "object",
        "properties": {
          "scripts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarkedScriptInput"
            },
            "minItems": 1,
            "maxItems": 1000
          }
        },
        "required": [
          "scripts"
        ],
        "additionalProperties": false
      },
      "MarkedScriptInput": {
        "type": "object",
        "properties": {
          "script_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "candidate_id": {
            "type": "string",
            "description": "The candidate's own pseudonymous ID in your system: a learner of the project. Never a name.",
            "example": "learner_demo_01"
          },
          "marker_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "question_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "mark": {
            "type": "integer",
            "minimum": 0,
            "description": "The marker's mark."
          },
          "second_reading": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "The second marker's mark for the same script. Leave it out, or null, for a script that was not read a second time: it stands as marked and is never flagged."
          },
          "sequence": {
            "type": "integer",
            "minimum": 1,
            "description": "The script's place in its marker's pile, for drift. Without it, scripts follow the order they are sent, after the marker's earlier ones."
          },
          "final_mark": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "A past sitting only: the mark the script finally carried after moderation, a review or an appeal. Leave it out, or null, where no later outcome is known; that is never read as unchanged. A live session refuses it."
          },
          "final_stage": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "moderation",
              "review",
              "appeal",
              null
            ],
            "description": "A past sitting only: where the final mark came from, where known."
          }
        },
        "required": [
          "script_id",
          "candidate_id",
          "marker_id",
          "question_id",
          "mark"
        ],
        "additionalProperties": false
      },
      "FlagDecisionResult": {
        "type": "object",
        "properties": {
          "decision_id": {
            "type": "string",
            "description": "Empty when `repeated`."
          },
          "script_id": {
            "type": "string"
          },
          "senior_mark": {
            "type": "integer"
          },
          "mark": {
            "type": "integer"
          },
          "changed": {
            "type": "boolean"
          },
          "decided_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "repeated": {
            "type": "boolean",
            "description": "True when this exact decision was already the latest, so nothing new was recorded."
          }
        },
        "required": [
          "decision_id",
          "script_id",
          "senior_mark",
          "mark",
          "changed",
          "decided_at",
          "repeated"
        ]
      },
      "PartnerFlagDecisionRequest": {
        "type": "object",
        "properties": {
          "script_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "senior_mark": {
            "type": "integer",
            "minimum": 0
          },
          "decided_by": {
            "type": "string",
            "description": "Your own reference for the senior marker who decided, such as a staff ID. Stored as `partner:` followed by it, as your system reported it; never a name or email address.",
            "example": "learner_demo_01"
          },
          "reason": {
            "type": "string",
            "enum": [
              "missed_follow_through",
              "mark_scheme_misread",
              "arithmetic",
              "second_reading_wrong",
              "other"
            ],
            "description": "Why they decided as they did. Optional through the API, so integrations written before reasons existed keep working; a decision without one counts as `not_given`. A reason that does not fit the decision returns 422 `reason_does_not_fit`."
          },
          "note": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "description": "Optional, up to 500 characters, as the senior marker wrote it. It appears in the session record and in the candidate's own export, so never put a candidate's name in it."
          }
        },
        "required": [
          "script_id",
          "senior_mark",
          "decided_by"
        ],
        "additionalProperties": false
      },
      "MarkingSessionRecord": {
        "type": "object",
        "properties": {
          "session_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "generated_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "open": {
            "type": "integer",
            "description": "Flagged scripts still waiting for a senior marker; the record is not final while above 0."
          },
          "sign_off": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "signed_off_by": {
                "type": "string",
                "description": "Who signed it off: the operator's sign-in subject."
              },
              "signed_off_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "scripts": {
                "type": "integer",
                "description": "Scripts in the session when it was signed off."
              },
              "second_read": {
                "type": "integer",
                "description": "Of those, scripts read a second time."
              },
              "flagged": {
                "type": "integer",
                "description": "Scripts flagged, every one decided."
              },
              "changed": {
                "type": "integer",
                "description": "Flagged scripts whose mark a senior marker changed."
              },
              "final_marks_sha256": {
                "type": "string",
                "pattern": "^[0-9a-f]{64}$",
                "description": "SHA-256, in hex, of the final-marks file exactly as it stood at sign-off. A file downloaded since matches it unless scripts were removed."
              },
              "scripts_removed_since": {
                "type": "integer",
                "description": "Scripts removed since sign-off by a candidate's erasure. None can be added."
              },
              "final_marks_unchanged": {
                "type": "boolean",
                "description": "Whether the final-marks file today is byte for byte the one signed off."
              }
            },
            "required": [
              "signed_off_by",
              "signed_off_at",
              "scripts",
              "second_read",
              "flagged",
              "changed",
              "final_marks_sha256",
              "scripts_removed_since",
              "final_marks_unchanged"
            ],
            "description": "The sign-off, once the session has one; the record then opens with it."
          },
          "markdown": {
            "type": "string"
          }
        },
        "required": [
          "session_id",
          "generated_at",
          "open",
          "sign_off",
          "markdown"
        ]
      },
      "MarkerPage": {
        "type": "object",
        "properties": {
          "session": {
            "$ref": "#/components/schemas/MarkingSession"
          },
          "version": {
            "type": "string"
          },
          "marker": {
            "$ref": "#/components/schemas/MarkerSummary"
          },
          "minimum_marker_scripts": {
            "type": "integer"
          },
          "minimum_question_scripts": {
            "type": "integer"
          },
          "stretch_size": {
            "type": "integer",
            "description": "Scripts read twice in each stretch of the pile."
          },
          "stretches": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/PileStretch"
            },
            "description": "Null until the marker has enough scripts read twice."
          },
          "questions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarkerQuestion"
            }
          },
          "scripts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarkerScript"
            },
            "description": "Every one of the marker's scripts, in pile order."
          }
        },
        "required": [
          "session",
          "version",
          "marker",
          "minimum_marker_scripts",
          "minimum_question_scripts",
          "stretch_size",
          "stretches",
          "questions",
          "scripts"
        ]
      },
      "PileStretch": {
        "type": "object",
        "properties": {
          "from": {
            "type": "integer",
            "description": "First pile position in the stretch."
          },
          "to": {
            "type": "integer",
            "description": "Last pile position in the stretch."
          },
          "second_read": {
            "type": "integer"
          },
          "difference": {
            "type": "number",
            "description": "Mean of (mark - second reading) / maximum mark over the stretch's scripts read twice."
          }
        },
        "required": [
          "from",
          "to",
          "second_read",
          "difference"
        ]
      },
      "MarkerQuestion": {
        "type": "object",
        "properties": {
          "question_id": {
            "type": "string"
          },
          "max_mark": {
            "type": "integer"
          },
          "scripts": {
            "type": "integer"
          },
          "second_read": {
            "type": "integer"
          },
          "flagged": {
            "type": "integer"
          },
          "difference": {
            "type": [
              "number",
              "null"
            ],
            "description": "Mean of the marker's mark minus the second reading, in marks; null below the minimum."
          }
        },
        "required": [
          "question_id",
          "max_mark",
          "scripts",
          "second_read",
          "flagged",
          "difference"
        ]
      },
      "MarkerScript": {
        "type": "object",
        "properties": {
          "script_id": {
            "type": "string"
          },
          "question_id": {
            "type": "string"
          },
          "sequence": {
            "type": "integer",
            "description": "The script's place in the marker's pile."
          },
          "mark": {
            "type": "integer"
          },
          "second_reading": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Null for a script not read a second time."
          },
          "flagged": {
            "type": "boolean"
          },
          "senior_mark": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The latest decision's mark on a flagged script; null while it waits."
          },
          "reason": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/DecisionReason"
              },
              {
                "type": "null"
              }
            ]
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          },
          "final_mark": {
            "type": [
              "integer",
              "null"
            ],
            "description": "A past sitting's later outcome, where known; null otherwise."
          },
          "final_stage": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/LaterStage"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "script_id",
          "question_id",
          "sequence",
          "mark",
          "second_reading",
          "flagged",
          "senior_mark",
          "reason",
          "note",
          "final_mark",
          "final_stage"
        ]
      },
      "DecisionReason": {
        "type": [
          "string",
          "null"
        ],
        "enum": [
          "missed_follow_through",
          "mark_scheme_misread",
          "arithmetic",
          "second_reading_wrong",
          "other",
          null
        ],
        "description": "Why the senior marker decided as they did. For a changed mark: `missed_follow_through` (the marker didn't credit work that followed from the candidate's own earlier figure), `mark_scheme_misread`, `arithmetic` (the marker's marks were added up wrongly), or `other`. For a kept mark: `second_reading_wrong`, or `other`."
      },
      "MarkerFeedback": {
        "type": "object",
        "properties": {
          "session_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "marker_id": {
            "type": "string"
          },
          "generated_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "markdown": {
            "type": "string"
          }
        },
        "required": [
          "session_id",
          "marker_id",
          "generated_at",
          "markdown"
        ]
      },
      "ExperimentRecord": {
        "type": "object",
        "properties": {
          "experiment_id": {
            "type": "string"
          },
          "rule_version": {
            "type": "string",
            "enum": [
              "experiment-record-v0"
            ]
          },
          "independent_response_rule": {
            "type": "string",
            "enum": [
              "unassisted_scored_response_v0"
            ]
          },
          "computed_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "windows": {
            "type": "object",
            "properties": {
              "exposure_days": {
                "type": "integer",
                "minimum": 1,
                "maximum": 90,
                "description": "Declared before the record is computed, and echoed back with it."
              },
              "outcome_days": {
                "type": "integer",
                "minimum": 1,
                "maximum": 90,
                "description": "Declared before the record is computed, and echoed back with it."
              }
            },
            "required": [
              "exposure_days",
              "outcome_days"
            ]
          },
          "assignment": {
            "type": "object",
            "properties": {
              "learners": {
                "type": "integer",
                "minimum": 0
              },
              "arms": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "arm": {
                      "type": "string"
                    },
                    "learners": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "first_assigned_at": {
                      "type": "string",
                      "description": "UTC instant in ISO 8601 format with millisecond precision.",
                      "example": "2026-09-13T09:00:00.000Z"
                    },
                    "last_assigned_at": {
                      "type": "string",
                      "description": "UTC instant in ISO 8601 format with millisecond precision.",
                      "example": "2026-09-13T09:00:00.000Z"
                    }
                  },
                  "required": [
                    "arm",
                    "learners",
                    "first_assigned_at",
                    "last_assigned_at"
                  ]
                }
              },
              "methods": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "randomized",
                    "stratified_randomized",
                    "rule_based",
                    "self_selected",
                    "manual",
                    "unknown"
                  ]
                }
              },
              "analysis_versions": {
                "type": "array",
                "items": {
                  "type": "string",
                  "maxLength": 64,
                  "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$"
                }
              }
            },
            "required": [
              "learners",
              "arms",
              "methods",
              "analysis_versions"
            ]
          },
          "exposure": {
            "type": "object",
            "properties": {
              "arms": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "arm": {
                      "type": "string"
                    },
                    "learners_with_delivery": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Assigned learners with at least one intervention event naming this experiment in its provenance, inside the exposure window after their assignment."
                    },
                    "deliveries": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "learners_with_other_arm_delivery": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Assigned learners whose delivery named a different arm. Counted as it was reported, never corrected."
                    },
                    "deliveries_before_assignment": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Interventions naming this experiment that reached the learner before they were assigned. Outside the window."
                    },
                    "deliveries_without_experiment": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Interventions in the window whose provenance named no experiment, so they cannot be attributed to this one."
                    }
                  },
                  "required": [
                    "arm",
                    "learners_with_delivery",
                    "deliveries",
                    "learners_with_other_arm_delivery",
                    "deliveries_before_assignment",
                    "deliveries_without_experiment"
                  ]
                }
              }
            },
            "required": [
              "arms"
            ]
          },
          "adherence": {
            "type": "object",
            "properties": {
              "arms": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "arm": {
                      "type": "string"
                    },
                    "intended_intervention_ids": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "What the assignments in this arm said should be delivered."
                    },
                    "learners_with_intended_delivery": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "learners_without": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "learners_without_intended_intervention": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Assigned learners whose assignment named no intended intervention, so adherence cannot be judged for them."
                    }
                  },
                  "required": [
                    "arm",
                    "intended_intervention_ids",
                    "learners_with_intended_delivery",
                    "learners_without",
                    "learners_without_intended_intervention"
                  ]
                }
              }
            },
            "required": [
              "arms"
            ]
          },
          "assessment": {
            "type": "object",
            "properties": {
              "arms": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "arm": {
                      "type": "string"
                    },
                    "learners_with_independent_response": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "independent_responses": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "learners_not_observed": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Assigned learners with no independent response in the outcome window. Neither failure nor success."
                    },
                    "other_responses": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Responses in the window that were assisted, of unknown assistance, unscored or partially scored. Reported apart, never merged with independent responses."
                    }
                  },
                  "required": [
                    "arm",
                    "learners_with_independent_response",
                    "independent_responses",
                    "learners_not_observed",
                    "other_responses"
                  ]
                }
              }
            },
            "required": [
              "arms"
            ]
          },
          "analysis": {
            "type": "object",
            "properties": {
              "design_status": {
                "type": "string",
                "enum": [
                  "descriptive"
                ],
                "description": "Counts of what was recorded. It explains nothing and predicts nothing."
              },
              "could_support": {
                "type": "string",
                "enum": [
                  "randomized_comparison",
                  "observational_only"
                ],
                "description": "What the recorded assignment methods could support if an analysis were declared and run. Nothing here is that analysis."
              },
              "effect_estimated": {
                "type": "boolean",
                "enum": [
                  false
                ]
              },
              "limitations": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "design_status",
              "could_support",
              "effect_estimated",
              "limitations"
            ]
          }
        },
        "required": [
          "experiment_id",
          "rule_version",
          "independent_response_rule",
          "computed_at",
          "windows",
          "assignment",
          "exposure",
          "adherence",
          "assessment",
          "analysis"
        ]
      },
      "EvaluationRunList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvaluationRun"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null when there are no more results."
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "EvaluationRun": {
        "type": "object",
        "properties": {
          "evaluation_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "status": {
            "$ref": "#/components/schemas/EvaluationRunStatus"
          },
          "design_status": {
            "type": "string",
            "enum": [
              "predictive"
            ],
            "description": "A guess made before the result, scored against what actually happened."
          },
          "method_version": {
            "$ref": "#/components/schemas/EvaluationMethodVersion"
          },
          "issued_from": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "issued_before": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "cutoff": {
            "type": "string",
            "description": "The time the run was asked for. It reads no outcome recorded after it, so later outcomes never change it.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "created_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "created_by": {
            "type": "string"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "manifest": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/EvaluationManifest"
              },
              {
                "type": "null"
              }
            ]
          },
          "manifest_sha256": {
            "type": [
              "string",
              "null"
            ]
          },
          "models": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvaluatedModel"
            }
          },
          "declared_comparison": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "candidate_model_version": {
                "type": "string",
                "enum": [
                  "recent-v1"
                ]
              },
              "primary_metric": {
                "type": "string",
                "enum": [
                  "brier_score",
                  "log_loss"
                ]
              },
              "minimum_improvement": {
                "type": "number",
                "minimum": 0,
                "maximum": 1,
                "description": "The smallest drop in the primary metric, candidate against baseline, that counts as better."
              }
            },
            "required": [
              "candidate_model_version",
              "primary_metric",
              "minimum_improvement"
            ],
            "additionalProperties": false,
            "description": "The comparison declared when the run was asked for, or null."
          },
          "comparison": {
            "$ref": "#/components/schemas/EvaluationComparison"
          },
          "withdrawn": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "reason": {
                "type": "string",
                "enum": [
                  "learner_erased"
                ]
              }
            },
            "required": [
              "at",
              "reason"
            ]
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "evaluation_id",
          "status",
          "design_status",
          "method_version",
          "issued_from",
          "issued_before",
          "cutoff",
          "created_at",
          "created_by",
          "completed_at",
          "manifest",
          "manifest_sha256",
          "models",
          "declared_comparison",
          "comparison",
          "withdrawn",
          "limitations"
        ]
      },
      "EvaluationRunStatus": {
        "type": "string",
        "enum": [
          "pending",
          "completed",
          "too_large",
          "withdrawn"
        ],
        "description": "`pending` until the drain computes it; `too_large` when its window held more than 100,000 predictions; `withdrawn` when a learner whose prediction it read was erased, its metrics removed."
      },
      "EvaluationMethodVersion": {
        "type": "string",
        "enum": [
          "prediction-evaluation-v1",
          "prediction-evaluation-v2"
        ],
        "description": "`prediction-evaluation-v1` runs report each model alone. `prediction-evaluation-v2` runs also compare a candidate with the baseline on the predictions frozen together."
      },
      "EvaluationManifest": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "method_version": {
            "$ref": "#/components/schemas/EvaluationMethodVersion"
          },
          "issued_from": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "issued_before": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "cutoff": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "predictions_read": {
            "type": "integer"
          },
          "models": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "model_version": {
                  "type": "string"
                },
                "model_kind": {
                  "type": "string"
                },
                "artifact_sha256": {
                  "type": "string"
                },
                "eligibility_version": {
                  "type": "string"
                },
                "target_definition": {
                  "type": "string"
                }
              },
              "required": [
                "model_version",
                "model_kind",
                "artifact_sha256",
                "eligibility_version",
                "target_definition"
              ]
            }
          },
          "minimum": {
            "type": "object",
            "properties": {
              "outcomes": {
                "type": "integer"
              },
              "learners": {
                "type": "integer"
              }
            },
            "required": [
              "outcomes",
              "learners"
            ]
          },
          "epsilon": {
            "type": "number"
          },
          "bins": {
            "type": "integer"
          },
          "resamples": {
            "type": "integer"
          },
          "level": {
            "type": "number"
          },
          "seed": {
            "type": "integer"
          },
          "declared_comparison": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "candidate_model_version": {
                "type": "string",
                "enum": [
                  "recent-v1"
                ]
              },
              "primary_metric": {
                "type": "string",
                "enum": [
                  "brier_score",
                  "log_loss"
                ]
              },
              "minimum_improvement": {
                "type": "number",
                "minimum": 0,
                "maximum": 1,
                "description": "The smallest drop in the primary metric, candidate against baseline, that counts as better."
              }
            },
            "required": [
              "candidate_model_version",
              "primary_metric",
              "minimum_improvement"
            ],
            "additionalProperties": false,
            "description": "Absent from `prediction-evaluation-v1` runs."
          }
        },
        "required": [
          "method_version",
          "issued_from",
          "issued_before",
          "cutoff",
          "predictions_read",
          "models",
          "minimum",
          "epsilon",
          "bins",
          "resamples",
          "level",
          "seed"
        ],
        "description": "Everything the run read and every setting it used; `manifest_sha256` is the SHA-256 of its canonical JSON."
      },
      "EvaluatedModel": {
        "type": "object",
        "properties": {
          "model_version": {
            "type": "string"
          },
          "target_definition": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "evaluated",
              "insufficient_sample"
            ],
            "description": "`insufficient_sample` below 30 matched outcomes or 10 learners: no metric is reported from a sample that small."
          },
          "issued": {
            "type": "integer"
          },
          "matched": {
            "type": "integer"
          },
          "censored": {
            "type": "integer"
          },
          "unresolved": {
            "type": "integer",
            "description": "Predictions with no outcome recorded by the cutoff, usually a window still open."
          },
          "coverage": {
            "type": [
              "number",
              "null"
            ],
            "description": "Matched over matched and censored; null when no window has closed."
          },
          "learners_with_outcome": {
            "type": "integer"
          },
          "outcome_rate": {
            "type": [
              "number",
              "null"
            ]
          },
          "brier_score": {
            "type": [
              "number",
              "null"
            ]
          },
          "brier_score_interval": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/EvaluationInterval"
              },
              {
                "type": "null"
              }
            ]
          },
          "log_loss": {
            "type": [
              "number",
              "null"
            ]
          },
          "log_loss_interval": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/EvaluationInterval"
              },
              {
                "type": "null"
              }
            ]
          },
          "auc": {
            "type": [
              "number",
              "null"
            ],
            "description": "Null unless both outcomes occurred."
          },
          "calibration": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CalibrationBin"
            }
          }
        },
        "required": [
          "model_version",
          "target_definition",
          "status",
          "issued",
          "matched",
          "censored",
          "unresolved",
          "coverage",
          "learners_with_outcome",
          "outcome_rate",
          "brier_score",
          "brier_score_interval",
          "log_loss",
          "log_loss_interval",
          "auc",
          "calibration"
        ]
      },
      "EvaluationInterval": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "lower": {
            "type": "number"
          },
          "upper": {
            "type": "number"
          },
          "level": {
            "type": "number"
          }
        },
        "required": [
          "lower",
          "upper",
          "level"
        ]
      },
      "CalibrationBin": {
        "type": "object",
        "properties": {
          "lower": {
            "type": "number"
          },
          "upper": {
            "type": "number"
          },
          "predictions": {
            "type": "integer"
          },
          "mean_predicted": {
            "type": [
              "number",
              "null"
            ]
          },
          "observed_rate": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "lower",
          "upper",
          "predictions",
          "mean_predicted",
          "observed_rate"
        ]
      },
      "EvaluationComparison": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "not_available"
                ]
              },
              "reason": {
                "type": "string",
                "enum": [
                  "one_model",
                  "not_computed"
                ],
                "description": "`one_model`: the run read predictions of one model only, or is a `prediction-evaluation-v1` run. `not_computed`: the run is pending, too large or withdrawn."
              }
            },
            "required": [
              "status",
              "reason"
            ]
          },
          {
            "type": "object",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "compared"
                ]
              },
              "baseline_model_version": {
                "type": "string"
              },
              "candidate_model_version": {
                "type": "string"
              },
              "target_definition": {
                "type": "string"
              },
              "pairs": {
                "type": "integer",
                "description": "Requests whose baseline and candidate predictions were both matched to the same response."
              },
              "learners": {
                "type": "integer"
              },
              "metrics": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ComparedMetric"
                }
              },
              "declared": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "primary_metric": {
                    "type": "string",
                    "enum": [
                      "brier_score",
                      "log_loss"
                    ]
                  },
                  "minimum_improvement": {
                    "type": "number"
                  }
                },
                "required": [
                  "primary_metric",
                  "minimum_improvement"
                ]
              },
              "verdict": {
                "type": "string",
                "enum": [
                  "candidate_better",
                  "candidate_worse",
                  "inconclusive",
                  "insufficient_sample",
                  "not_declared"
                ],
                "description": "`not_declared` when no comparison was declared with the run: the differences are then descriptive. `insufficient_sample` below 30 pairs or 10 learners, with no difference reported."
              },
              "explanation": {
                "type": "string"
              }
            },
            "required": [
              "status",
              "baseline_model_version",
              "candidate_model_version",
              "target_definition",
              "pairs",
              "learners",
              "metrics",
              "declared",
              "verdict",
              "explanation"
            ]
          }
        ]
      },
      "ComparedMetric": {
        "type": "object",
        "properties": {
          "metric": {
            "type": "string",
            "enum": [
              "brier_score",
              "log_loss"
            ]
          },
          "baseline_value": {
            "type": [
              "number",
              "null"
            ]
          },
          "candidate_value": {
            "type": [
              "number",
              "null"
            ]
          },
          "difference": {
            "type": [
              "number",
              "null"
            ],
            "description": "Candidate minus baseline: below zero, the candidate predicted better."
          },
          "difference_interval": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "lower": {
                "type": "number"
              },
              "upper": {
                "type": "number"
              },
              "level": {
                "type": "number"
              }
            },
            "required": [
              "lower",
              "upper",
              "level"
            ],
            "description": "From resampling learners, whose paired predictions move together."
          }
        },
        "required": [
          "metric",
          "baseline_value",
          "candidate_value",
          "difference",
          "difference_interval"
        ]
      },
      "ExperimentRecordReport": {
        "type": "object",
        "properties": {
          "report_version": {
            "type": "string",
            "enum": [
              "experiment-record-report-v1"
            ]
          },
          "experiment_id": {
            "type": "string"
          },
          "label": {
            "type": "string",
            "enum": [
              "descriptive_analysis"
            ]
          },
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "live"
            ],
            "description": "A sandbox project holds test data, and its report says so."
          },
          "as_of": {
            "type": "string",
            "description": "When the record was counted.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "record": {
            "$ref": "#/components/schemas/ExperimentRecord"
          },
          "markdown": {
            "type": "string",
            "description": "The report as a Markdown document, built from the record alone."
          }
        },
        "required": [
          "report_version",
          "experiment_id",
          "label",
          "environment",
          "as_of",
          "record",
          "markdown"
        ]
      },
      "JobStatus": {
        "type": "object",
        "properties": {
          "job_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "kind": {
            "type": "string",
            "enum": [
              "apply_event",
              "rebuild_learner_concept"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/ProcessingStatus"
          },
          "attempts": {
            "type": "integer",
            "minimum": 0
          },
          "max_attempts": {
            "type": "integer",
            "minimum": 1
          },
          "error_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Safe, stable failure code from the most recent attempt. Never contains learner data."
          },
          "retryable": {
            "type": "boolean",
            "description": "True while the job will be retried automatically. False once completed or failed."
          },
          "event_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "learner_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "concept_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "outcome": {
            "type": "string",
            "enum": [
              "state_updated",
              "no_concept_mapping",
              "learner_deleted",
              "not_yet_run"
            ],
            "description": "What the job did when it completed. `learner_deleted`: the learner was marked for deletion, so no state was computed."
          },
          "state_revision": {
            "type": [
              "integer",
              "null"
            ],
            "description": "State revision written by this job."
          },
          "created_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "updated_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "job_id",
          "kind",
          "status",
          "attempts",
          "max_attempts",
          "error_code",
          "retryable",
          "event_id",
          "learner_id",
          "concept_id",
          "outcome",
          "state_revision",
          "created_at",
          "updated_at",
          "completed_at"
        ]
      },
      "ProcessingStatus": {
        "type": "string",
        "enum": [
          "pending",
          "processing",
          "completed",
          "failed"
        ]
      },
      "LearnerStateList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LearnerConceptState"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null when there are no more results."
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "LearnerConceptState": {
        "type": "object",
        "properties": {
          "learner_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "concept_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "concept_label": {
            "type": "string"
          },
          "state_revision": {
            "type": "integer",
            "minimum": 0,
            "description": "Monotonic revision of the stored state. 0 means no stored state yet."
          },
          "computed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "as_of": {
            "type": "string",
            "description": "Instant at which `status` and staleness were evaluated (the time of this response).",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "status": {
            "$ref": "#/components/schemas/StateStatus"
          },
          "pending_updates": {
            "type": "boolean",
            "description": "True when accepted events for this learner-concept have not been applied yet."
          },
          "summary": {
            "$ref": "#/components/schemas/ObservedSummary"
          },
          "intake": {
            "$ref": "#/components/schemas/IntakeCounts"
          },
          "forecast": {
            "type": "null",
            "description": "Always null for observed-v0."
          },
          "uncertainty": {
            "$ref": "#/components/schemas/Uncertainty"
          },
          "flags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StateFlag"
            }
          },
          "design_status": {
            "type": "string",
            "enum": [
              "descriptive"
            ],
            "description": "Counts of what was recorded. It explains nothing and predicts nothing."
          },
          "model_version": {
            "type": "string",
            "enum": [
              "observed-v0"
            ]
          },
          "eligibility_version": {
            "$ref": "#/components/schemas/EligibilityVersion"
          },
          "mapping_revision": {
            "type": [
              "string",
              "null"
            ],
            "description": "Fingerprint of the item-concept mapping revisions used by this state revision."
          },
          "evidence_url": {
            "type": "string"
          }
        },
        "required": [
          "learner_id",
          "concept_id",
          "concept_label",
          "state_revision",
          "computed_at",
          "as_of",
          "status",
          "pending_updates",
          "summary",
          "intake",
          "forecast",
          "uncertainty",
          "flags",
          "design_status",
          "model_version",
          "eligibility_version",
          "mapping_revision",
          "evidence_url"
        ]
      },
      "StateStatus": {
        "type": "string",
        "enum": [
          "insufficient_evidence",
          "observed_only",
          "stale_evidence"
        ],
        "description": "`insufficient_evidence`: fewer than 5 eligible attempts or fewer than 2 distinct sessions (a disclosure threshold, not a validation threshold). `stale_evidence`: the latest eligible attempt is more than 30 days old; the summary is not decayed. `observed_only`: enough recent observations to summarize; still no forecast."
      },
      "ObservedSummary": {
        "type": "object",
        "properties": {
          "window": {
            "type": "string",
            "enum": [
              "latest_200_eligible_events"
            ]
          },
          "window_size": {
            "type": "number",
            "enum": [
              200
            ]
          },
          "eligible_attempts": {
            "type": "integer",
            "minimum": 0,
            "description": "Eligible attempts inside the window."
          },
          "correct_attempts": {
            "type": "integer",
            "minimum": 0
          },
          "incorrect_attempts": {
            "type": "integer",
            "minimum": 0
          },
          "observed_accuracy": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "correct_attempts / eligible_attempts within the window, rounded to 6 decimals for transport. Null when there are no eligible attempts. A description of past observations, not a probability."
          },
          "distinct_sessions": {
            "type": "integer",
            "minimum": 0,
            "description": "Distinct session IDs among window attempts. Attempts without a session ID do not count."
          },
          "distinct_items": {
            "type": "integer",
            "minimum": 0
          },
          "first_observed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "last_observed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "history_truncated": {
            "type": "boolean"
          },
          "eligible_attempts_outside_window": {
            "type": "integer",
            "minimum": 0
          },
          "constructs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConstructBreakdown"
            },
            "description": "The window split by construct type, so an immediate measurement and a delayed one are never read as the same thing. Responses whose product did not say are counted as `unreported`."
          }
        },
        "required": [
          "window",
          "window_size",
          "eligible_attempts",
          "correct_attempts",
          "incorrect_attempts",
          "observed_accuracy",
          "distinct_sessions",
          "distinct_items",
          "first_observed_at",
          "last_observed_at",
          "history_truncated",
          "eligible_attempts_outside_window",
          "constructs"
        ]
      },
      "ConstructBreakdown": {
        "type": "object",
        "properties": {
          "construct": {
            "type": "string",
            "enum": [
              "practice",
              "formative_check",
              "immediate_post_test",
              "delayed_check",
              "near_transfer",
              "far_transfer",
              "external_assessment",
              "unreported"
            ]
          },
          "eligible_attempts": {
            "type": "integer",
            "minimum": 0
          },
          "correct_attempts": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "construct",
          "eligible_attempts",
          "correct_attempts"
        ]
      },
      "IntakeCounts": {
        "type": "object",
        "properties": {
          "scanned_events": {
            "type": "integer",
            "minimum": 0,
            "description": "Stored events mapped to this concept that the computation read (all types)."
          },
          "response_events": {
            "type": "integer",
            "minimum": 0
          },
          "eligible_responses": {
            "type": "integer",
            "minimum": 0,
            "description": "All eligible responses read, including those outside the window."
          },
          "excluded_responses": {
            "type": "integer",
            "minimum": 0
          },
          "exclusions": {
            "type": "object",
            "properties": {
              "partial_credit": {
                "type": "integer",
                "minimum": 0
              },
              "unscored": {
                "type": "integer",
                "minimum": 0
              },
              "answer_revealed": {
                "type": "integer",
                "minimum": 0
              },
              "collaborated": {
                "type": "integer",
                "minimum": 0
              },
              "assistance_unknown": {
                "type": "integer",
                "minimum": 0
              },
              "assisted": {
                "type": "integer",
                "minimum": 0
              },
              "feedback_seen": {
                "type": "integer",
                "minimum": 0
              },
              "hint_before_response": {
                "type": "integer",
                "minimum": 0
              },
              "feedback_before_response": {
                "type": "integer",
                "minimum": 0
              },
              "repeat_in_session": {
                "type": "integer",
                "minimum": 0
              },
              "repeat_in_attempt": {
                "type": "integer",
                "minimum": 0
              }
            },
            "required": [
              "partial_credit",
              "unscored",
              "answer_revealed",
              "collaborated",
              "assistance_unknown",
              "assisted",
              "feedback_seen",
              "hint_before_response",
              "feedback_before_response",
              "repeat_in_session",
              "repeat_in_attempt"
            ]
          },
          "exposure_events": {
            "type": "integer",
            "minimum": 0
          },
          "hint_events": {
            "type": "integer",
            "minimum": 0
          },
          "feedback_events": {
            "type": "integer",
            "minimum": 0
          },
          "intervention_events": {
            "type": "integer",
            "minimum": 0
          },
          "superseded_events": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "scanned_events",
          "response_events",
          "eligible_responses",
          "excluded_responses",
          "exclusions",
          "exposure_events",
          "hint_events",
          "feedback_events",
          "intervention_events",
          "superseded_events"
        ],
        "description": "Counts over every stored event read for this learner-concept. Deliberately separate from the windowed summary so numerators and denominators are never mixed."
      },
      "Uncertainty": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "not_estimated"
            ]
          },
          "interval": {
            "type": "null"
          },
          "calibration_status": {
            "type": "string",
            "enum": [
              "unvalidated"
            ]
          }
        },
        "required": [
          "kind",
          "interval",
          "calibration_status"
        ],
        "description": "observed-v0 does not estimate uncertainty. No interval is invented."
      },
      "StateFlag": {
        "type": "string",
        "enum": [
          "no_eligible_evidence",
          "sparse_history",
          "insufficient_sessions",
          "stale_evidence",
          "session_missing",
          "history_truncated",
          "assistance_unreported",
          "mixed_construct_types",
          "scan_limit_reached",
          "history_unavailable"
        ],
        "description": "`no_eligible_evidence`: nothing counted yet. `sparse_history`: fewer than 5 eligible attempts. `insufficient_sessions`: fewer than 2 distinct sessions with a session ID. `stale_evidence`: latest eligible attempt older than 30 days. `session_missing`: some counted attempts had no session ID (reduced comparability). `history_truncated`: older eligible attempts fall outside the 200-attempt window. `assistance_unreported`: some responses were excluded because assistance was not reported. `mixed_construct_types`: the window mixes responses meant to measure different things, such as practice and a delayed check; read the breakdown before the headline number. `scan_limit_reached`: the recomputation read its maximum number of stored events. `history_unavailable`: older evidence for this learner and concept was removed under the retention policy, so the summary describes only what remains."
      },
      "EligibilityVersion": {
        "type": "string",
        "enum": [
          "eligibility-v1",
          "eligibility-v2"
        ],
        "description": "Version of the rules that decided which responses counted. A state keeps the version it was computed under until it is rebuilt."
      },
      "LearnerConceptStateDetail": {
        "type": "object",
        "properties": {
          "learner_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "concept_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "concept_label": {
            "type": "string"
          },
          "state_revision": {
            "type": "integer",
            "minimum": 0,
            "description": "Monotonic revision of the stored state. 0 means no stored state yet."
          },
          "computed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "as_of": {
            "type": "string",
            "description": "Instant at which `status` and staleness were evaluated (the time of this response).",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "status": {
            "$ref": "#/components/schemas/StateStatus"
          },
          "pending_updates": {
            "type": "boolean",
            "description": "True when accepted events for this learner-concept have not been applied yet."
          },
          "summary": {
            "$ref": "#/components/schemas/ObservedSummary"
          },
          "intake": {
            "$ref": "#/components/schemas/IntakeCounts"
          },
          "forecast": {
            "type": "null",
            "description": "Always null for observed-v0."
          },
          "uncertainty": {
            "$ref": "#/components/schemas/Uncertainty"
          },
          "flags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StateFlag"
            }
          },
          "design_status": {
            "type": "string",
            "enum": [
              "descriptive"
            ],
            "description": "Counts of what was recorded. It explains nothing and predicts nothing."
          },
          "model_version": {
            "type": "string",
            "enum": [
              "observed-v0"
            ]
          },
          "eligibility_version": {
            "$ref": "#/components/schemas/EligibilityVersion"
          },
          "mapping_revision": {
            "type": [
              "string",
              "null"
            ],
            "description": "Fingerprint of the item-concept mapping revisions used by this state revision."
          },
          "evidence_url": {
            "type": "string"
          },
          "evidence_quality": {
            "$ref": "#/components/schemas/EvidenceQuality"
          }
        },
        "required": [
          "learner_id",
          "concept_id",
          "concept_label",
          "state_revision",
          "computed_at",
          "as_of",
          "status",
          "pending_updates",
          "summary",
          "intake",
          "forecast",
          "uncertainty",
          "flags",
          "design_status",
          "model_version",
          "eligibility_version",
          "mapping_revision",
          "evidence_url",
          "evidence_quality"
        ]
      },
      "EvidenceQuality": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "enum": [
              "evidence-quality-v1"
            ]
          },
          "observations": {
            "type": "integer",
            "minimum": 0,
            "description": "Retained responses on this concept that are not superseded."
          },
          "truncated": {
            "type": "boolean",
            "description": "True when more than 1000 responses exist and only the most recent were counted."
          },
          "independent_observations": {
            "type": "integer",
            "minimum": 0,
            "description": "Responses that counted as independent evidence under the eligibility rules named on the state."
          },
          "by_assistance": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Counts by the value the partner reported. A value nobody reported is absent rather than zero, except `unreported`, which counts responses that named no value at all."
          },
          "by_construct_type": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            },
            "description": "What each response was meant to measure, where the partner said so: practice, a formative check, an immediate or delayed post-test, near or far transfer, an external assessment."
          },
          "by_source": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Which system reported the responses."
          },
          "independence_reported": {
            "type": "integer",
            "minimum": 0,
            "description": "Responses that stated whether they were independent of the teaching being evaluated. The rest are unknown, not independent."
          },
          "oldest_observation": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "newest_observation": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "longest_reported_delay_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "The longest planned delay any response reported, so persistence over time is visible as a fact rather than assumed."
          },
          "could_support": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DesignStatus"
            },
            "description": "The kinds of conclusion this evidence could support if an analysis were run. Never a claim that one was."
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "version",
          "observations",
          "truncated",
          "independent_observations",
          "by_assistance",
          "by_construct_type",
          "by_source",
          "independence_reported",
          "oldest_observation",
          "newest_observation",
          "longest_reported_delay_seconds",
          "could_support",
          "limitations"
        ]
      },
      "DesignStatus": {
        "type": "string",
        "enum": [
          "descriptive",
          "predictive",
          "observational_association",
          "adjusted_observational",
          "quasi_experimental",
          "randomized"
        ],
        "description": "How a result was arrived at. `descriptive`: counts of what was recorded. `predictive`: a statement made before the outcome, scored against what followed. `observational_association`: two things moved together in data nobody assigned. `adjusted_observational`: an association with a declared adjustment, which this build does not implement and never returns. `quasi_experimental`: a design that approximates assignment. `randomized`: chance decided the arm, under an analysis declared in advance. Only the last two could support a causal reading, and only with a declared analysis."
      },
      "StateRevisionList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StateRevision"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null when there are no more results."
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "StateRevision": {
        "type": "object",
        "properties": {
          "revision": {
            "type": "integer",
            "minimum": 1
          },
          "computed_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "status": {
            "$ref": "#/components/schemas/StateStatus"
          },
          "summary": {
            "type": "object",
            "properties": {
              "eligible_attempts": {
                "type": "integer",
                "minimum": 0
              },
              "correct_attempts": {
                "type": "integer",
                "minimum": 0
              },
              "observed_accuracy": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "distinct_sessions": {
                "type": "integer",
                "minimum": 0
              }
            },
            "required": [
              "eligible_attempts",
              "correct_attempts",
              "observed_accuracy",
              "distinct_sessions"
            ]
          },
          "flags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StateFlag"
            }
          },
          "model_version": {
            "type": "string"
          },
          "eligibility_version": {
            "type": "string"
          },
          "mapping_revision": {
            "type": [
              "string",
              "null"
            ]
          },
          "evidence_received": {
            "type": "object",
            "properties": {
              "events": {
                "type": "integer",
                "minimum": 0
              },
              "event_ids": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "maxItems": 20,
                "description": "The first twenty, in the order they were stored."
              }
            },
            "required": [
              "events",
              "event_ids"
            ],
            "description": "Events on this learner and concept stored after the revision before this one and applied by this one. A correction appears as its replacement. For the first revision kept, everything it applied."
          },
          "changes": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "status": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "from": {
                    "$ref": "#/components/schemas/StateStatus"
                  },
                  "to": {
                    "$ref": "#/components/schemas/StateStatus"
                  }
                },
                "required": [
                  "from",
                  "to"
                ]
              },
              "eligible_attempts": {
                "type": "integer"
              },
              "correct_attempts": {
                "type": "integer"
              },
              "distinct_sessions": {
                "type": "integer"
              },
              "flags_added": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/StateFlag"
                }
              },
              "flags_removed": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/StateFlag"
                }
              },
              "versions_changed": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "model_version",
                    "eligibility_version"
                  ]
                }
              }
            },
            "required": [
              "status",
              "eligible_attempts",
              "correct_attempts",
              "distinct_sessions",
              "flags_added",
              "flags_removed",
              "versions_changed"
            ],
            "description": "Against the revision before. Null for the first revision kept: the history begins there, or retention removed what came before."
          }
        },
        "required": [
          "revision",
          "computed_at",
          "status",
          "summary",
          "flags",
          "model_version",
          "eligibility_version",
          "mapping_revision",
          "evidence_received",
          "changes"
        ]
      },
      "EvidenceList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvidenceItem"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null when there are no more results."
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "EvidenceItem": {
        "type": "object",
        "properties": {
          "event_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "event_type": {
            "$ref": "#/components/schemas/EventType"
          },
          "occurred_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "received_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "source": {
            "type": "string"
          },
          "course_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "session_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "item_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "item_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "attempt_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "attempt_number": {
            "type": [
              "integer",
              "null"
            ]
          },
          "response_kind": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ResponseKind"
              },
              {
                "type": "null"
              }
            ]
          },
          "correct": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "score": {
            "type": [
              "number",
              "null"
            ]
          },
          "assistance": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Assistance"
              },
              {
                "type": "null"
              }
            ]
          },
          "hint_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "feedback_seen": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "answer_revealed": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "As reported by the source. Null means it was not reported, never that it was false."
          },
          "collaborated": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "As reported by the source. Null means it was not reported, never that it was false."
          },
          "assistance_source": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/AssistanceSource"
              },
              {
                "type": "null"
              }
            ]
          },
          "construct_type": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ConstructType"
              },
              {
                "type": "null"
              }
            ]
          },
          "assessment_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "assessment_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "intended_delay_seconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "independent_of_teaching": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "As reported by the source. Null means it was not reported."
          },
          "latency_ms": {
            "type": [
              "integer",
              "null"
            ]
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ]
          },
          "intervention_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "provenance": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "intervention_kind": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/InterventionKind"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "provider": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "model_family": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "model_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "policy_version": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "prompt_version": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "experiment_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "arm": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "human_review": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "none",
                  "reviewed",
                  "unknown",
                  null
                ]
              }
            },
            "required": [
              "intervention_kind",
              "provider",
              "model_family",
              "model_id",
              "policy_version",
              "prompt_version",
              "experiment_id",
              "arm",
              "human_review"
            ],
            "description": "Intervention events only: the provenance the source reported, each field null when not reported. Null for other events and for interventions sent without provenance."
          },
          "concept": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "concept_id": {
                "type": "string"
              },
              "concept_label": {
                "type": "string"
              },
              "role": {
                "type": "string",
                "enum": [
                  "primary",
                  "secondary"
                ]
              },
              "mapping_revision": {
                "type": "integer"
              }
            },
            "required": [
              "concept_id",
              "concept_label",
              "role",
              "mapping_revision"
            ],
            "description": "Approved primary concept mapping applied to this event, or null if unmapped."
          },
          "flags": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "unmapped_item",
                "superseded",
                "session_missing"
              ]
            }
          },
          "eligibility": {
            "$ref": "#/components/schemas/EvidenceEligibility"
          }
        },
        "required": [
          "event_id",
          "event_type",
          "occurred_at",
          "received_at",
          "source",
          "course_id",
          "session_id",
          "item_id",
          "item_version",
          "attempt_id",
          "attempt_number",
          "response_kind",
          "correct",
          "score",
          "assistance",
          "hint_count",
          "feedback_seen",
          "answer_revealed",
          "collaborated",
          "assistance_source",
          "construct_type",
          "assessment_id",
          "assessment_version",
          "intended_delay_seconds",
          "independent_of_teaching",
          "latency_ms",
          "confidence",
          "intervention_id",
          "provenance",
          "concept",
          "flags",
          "eligibility"
        ]
      },
      "InterventionKind": {
        "type": [
          "string",
          "null"
        ],
        "enum": [
          "human",
          "static_content",
          "generic_tutor",
          "ai_tutor",
          "retrieval_practice",
          "explanation",
          "hint",
          "feedback",
          "customer_defined",
          "unknown",
          null
        ]
      },
      "EvidenceEligibility": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "eligible",
              "excluded",
              "pending",
              "not_applicable"
            ],
            "description": "`eligible`: counted as baseline evidence. `excluded`: a response that did not count (see reason). `pending`: not yet processed. `not_applicable`: not a response, or the item has no approved concept mapping."
          },
          "reason": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ExclusionReason"
              },
              {
                "type": "null"
              }
            ]
          },
          "in_window": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether this eligible attempt is inside the latest-200 window of its state revision."
          },
          "eligibility_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "state_revision": {
            "type": [
              "integer",
              "null"
            ],
            "description": "State revision that last evaluated this observation."
          }
        },
        "required": [
          "status",
          "reason",
          "in_window",
          "eligibility_version",
          "state_revision"
        ]
      },
      "ExclusionReason": {
        "type": [
          "string",
          "null"
        ],
        "enum": [
          "partial_credit",
          "unscored",
          "answer_revealed",
          "collaborated",
          "assistance_unknown",
          "assisted",
          "feedback_seen",
          "hint_before_response",
          "feedback_before_response",
          "repeat_in_session",
          "repeat_in_attempt",
          null
        ],
        "description": "`partial_credit`/`unscored`: not a binary outcome. `answer_revealed`: the correct answer was shown before or during the attempt. `collaborated`: the learner worked on the attempt with another person. `assistance_unknown`: assistance not reported or reported as unknown. `assisted`: hints or external help were used. `feedback_seen`: feedback was visible for the attempt. `hint_before_response`/`feedback_before_response`: a linked hint or feedback event preceded the response in the same attempt. `repeat_in_session`: not the first response to this item in its session. `repeat_in_attempt`: not the first response to this item in its attempt; this is the rule that applies when no session ID is sent."
      },
      "CourseRecord": {
        "type": "object",
        "properties": {
          "course_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "label": {
            "type": "string"
          },
          "locale": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "course_id",
          "label",
          "locale",
          "created_at"
        ]
      },
      "CourseUpsertRequest": {
        "type": "object",
        "properties": {
          "course_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "cloud_foundations"
          },
          "label": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "example": "Cloud foundations"
          },
          "locale": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 35,
            "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$",
            "description": "BCP 47 tag of the course language."
          }
        },
        "required": [
          "course_id",
          "label"
        ],
        "additionalProperties": false,
        "description": "Registers a stable course identity. Re-sending the same identity changes nothing."
      },
      "ConceptRecord": {
        "type": "object",
        "properties": {
          "concept_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "label": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "retired"
            ]
          },
          "created_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "concept_id",
          "label",
          "description",
          "status",
          "created_at"
        ]
      },
      "ConceptUpsertRequest": {
        "type": "object",
        "properties": {
          "concept_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "networking.ip"
          },
          "label": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "example": "IP addressing"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000,
            "description": "Optional note for operators. Not shown to learners by this service."
          }
        },
        "required": [
          "concept_id",
          "label"
        ],
        "additionalProperties": false,
        "description": "Registers a concept in the project namespace. Concepts are the unit state is summarized on."
      },
      "ItemVersionRecord": {
        "type": "object",
        "properties": {
          "item_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "item_version": {
            "type": "string"
          },
          "response_kind": {
            "$ref": "#/components/schemas/ResponseKind"
          },
          "rubric_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "registered_via": {
            "type": "string",
            "enum": [
              "catalog",
              "first_event"
            ],
            "description": "`first_event` means ingestion created this version minimally before it was registered."
          },
          "created_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "item_id",
          "item_version",
          "response_kind",
          "rubric_version",
          "registered_via",
          "created_at"
        ]
      },
      "ItemRegisterRequest": {
        "type": "object",
        "properties": {
          "item_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "question_012"
          },
          "item_version": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "Version of the item content. A content edit needs a new version, never an edit.",
            "example": "1"
          },
          "response_kind": {
            "$ref": "#/components/schemas/ResponseKind"
          },
          "rubric_version": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "Required for partial_credit, and not accepted for other kinds."
          },
          "metadata": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/EventMetadata"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "item_id",
          "item_version",
          "response_kind"
        ],
        "additionalProperties": false,
        "description": "Registers one immutable item version. A different definition for a version already registered returns 409; it is never applied."
      },
      "MappingRecord": {
        "type": "object",
        "properties": {
          "item_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "item_version": {
            "type": "string"
          },
          "concept_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "role": {
            "type": "string",
            "enum": [
              "primary"
            ]
          },
          "mapping_revision": {
            "type": "integer",
            "minimum": 1,
            "description": "Server-generated. Send it back as expected_mapping_revision to change this mapping."
          },
          "source": {
            "$ref": "#/components/schemas/MappingSource"
          },
          "approved_by": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "api_key",
                  "operator",
                  "system"
                ]
              },
              "id": {
                "type": "string",
                "description": "The credential that approved it, not a body field."
              }
            },
            "required": [
              "kind",
              "id"
            ]
          },
          "approved_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "rebuild": {
            "type": "object",
            "properties": {
              "requested": {
                "type": "boolean"
              },
              "events_mapped": {
                "type": "integer",
                "minimum": 0,
                "description": "Retained events that had no concept and now have this one."
              },
              "job_ids": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "uuid"
                },
                "description": "Rebuild jobs for the affected learner-concept pairs. Poll GET /v1/jobs/{id}."
              }
            },
            "required": [
              "requested",
              "events_mapped",
              "job_ids"
            ],
            "description": "What the rebuild did. Without it, the mapping applies to events stored from now on."
          }
        },
        "required": [
          "item_id",
          "item_version",
          "concept_id",
          "role",
          "mapping_revision",
          "source",
          "approved_by",
          "approved_at",
          "rebuild"
        ]
      },
      "MappingSource": {
        "type": "string",
        "enum": [
          "customer_reviewed",
          "customer_imported"
        ],
        "description": "`customer_reviewed`: a person at the customer approved it. `customer_imported`: taken from the customer’s existing content metadata without review."
      },
      "MappingCreateRequest": {
        "type": "object",
        "properties": {
          "item_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "item_version": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "Version of the item content. A content edit needs a new version, never an edit.",
            "example": "1"
          },
          "concept_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "role": {
            "type": "string",
            "enum": [
              "primary"
            ],
            "description": "Only the primary concept updates the numerical summary in v1. Secondary concepts are not accepted yet."
          },
          "source": {
            "$ref": "#/components/schemas/MappingSource"
          },
          "expected_mapping_revision": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "The revision this change is based on: null for the first mapping of an item version, otherwise the current one. A stale value returns 409 and changes nothing."
          },
          "rebuild_retained_history": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Attribute retained events on this item version that have no concept yet, then rebuild the affected learner-concept states. Events already attributed to a concept keep that attribution."
          }
        },
        "required": [
          "item_id",
          "item_version",
          "concept_id",
          "role",
          "source"
        ],
        "additionalProperties": false,
        "description": "Creates the next approved primary mapping revision. The approver is taken from the authenticated credential; an `approved_by` field in the body is rejected."
      },
      "CheckPlan": {
        "type": "object",
        "properties": {
          "rule_version": {
            "type": "string",
            "enum": [
              "next-check-v0"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "shadow"
            ]
          },
          "validation_status": {
            "type": "string",
            "enum": [
              "unvalidated"
            ]
          },
          "parameters": {
            "type": "object",
            "properties": {
              "max_checks": {
                "type": "integer"
              },
              "persistence_delay_days": {
                "type": "integer"
              },
              "follow_up_days": {
                "type": "integer"
              }
            },
            "required": [
              "max_checks",
              "persistence_delay_days",
              "follow_up_days"
            ]
          },
          "pending_updates": {
            "type": "boolean",
            "description": "True when accepted events for this learner were not yet applied when the plan was issued."
          },
          "concepts_considered": {
            "type": "integer",
            "minimum": 0
          },
          "checks": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "position": {
                  "type": "integer",
                  "minimum": 1
                },
                "concept_id": {
                  "type": "string",
                  "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
                  "example": "learner_demo_01"
                },
                "concept_label": {
                  "type": "string"
                },
                "reason": {
                  "$ref": "#/components/schemas/CheckReason"
                },
                "evidence": {
                  "$ref": "#/components/schemas/CheckEvidence"
                },
                "candidate_items": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CandidateItem"
                  },
                  "maxItems": 3,
                  "description": "Up to three binary item versions mapped to the concept: items new to the learner first, then the least recently answered. Your platform chooses what to show."
                },
                "responses_requested": {
                  "type": "number",
                  "enum": [
                    1
                  ]
                },
                "follow_up": {
                  "$ref": "#/components/schemas/CheckFollowUp"
                }
              },
              "required": [
                "position",
                "concept_id",
                "concept_label",
                "reason",
                "evidence",
                "candidate_items",
                "responses_requested",
                "follow_up"
              ]
            }
          },
          "not_scheduled": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "reason": {
                  "$ref": "#/components/schemas/CheckReason"
                },
                "concepts": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "required": [
                "reason",
                "concepts"
              ]
            },
            "description": "Gaps left out by the burden budget, counted by reason."
          },
          "no_check_needed": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "concept_id": {
                  "type": "string",
                  "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
                  "example": "learner_demo_01"
                },
                "concept_label": {
                  "type": "string"
                },
                "last_independent_at": {
                  "type": "string",
                  "description": "UTC instant in ISO 8601 format with millisecond precision.",
                  "example": "2026-09-13T09:00:00.000Z"
                },
                "check_due_after": {
                  "type": "string",
                  "description": "UTC instant in ISO 8601 format with millisecond precision.",
                  "example": "2026-09-13T09:00:00.000Z"
                }
              },
              "required": [
                "concept_id",
                "concept_label",
                "last_independent_at",
                "check_due_after"
              ]
            }
          },
          "data_gaps": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "concept_id": {
                  "type": "string",
                  "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
                  "example": "learner_demo_01"
                },
                "concept_label": {
                  "type": "string"
                },
                "gap": {
                  "$ref": "#/components/schemas/CheckDataGap"
                }
              },
              "required": [
                "concept_id",
                "concept_label",
                "gap"
              ]
            }
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "plan_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "learner_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "issued_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "evidence_watermark": {
            "type": "string",
            "description": "Commit-order position of the newest stored event for this learner at issue time."
          },
          "follow_up_as_of": {
            "type": "string",
            "description": "When the follow-up was derived. The plan itself never changes after issue.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "rule_version",
          "mode",
          "validation_status",
          "parameters",
          "pending_updates",
          "concepts_considered",
          "checks",
          "not_scheduled",
          "no_check_needed",
          "data_gaps",
          "limitations",
          "plan_id",
          "learner_id",
          "issued_at",
          "evidence_watermark",
          "follow_up_as_of"
        ]
      },
      "CheckReason": {
        "type": "string",
        "enum": [
          "no_independent_evidence",
          "assisted_since_independent_evidence",
          "insufficient_independent_evidence",
          "persistence_check_due",
          "stale_independent_evidence"
        ],
        "description": "Why an independent check is suggested, in the order the rule ranks them. `no_independent_evidence`: responses or exposure exist but none counted as independent. `assisted_since_independent_evidence`: assisted, unknown-assistance or feedback-affected responses occurred after the latest independent one. `insufficient_independent_evidence`: fewer than 5 counted attempts or fewer than 2 sessions. `persistence_check_due`: enough recent independent evidence, but the latest is older than the declared persistence delay. `stale_independent_evidence`: the latest independent evidence is more than 30 days old."
      },
      "CheckEvidence": {
        "type": "object",
        "properties": {
          "state_revision": {
            "type": "integer",
            "minimum": 0
          },
          "status": {
            "$ref": "#/components/schemas/StateStatus"
          },
          "independent_responses": {
            "type": "integer",
            "minimum": 0,
            "description": "Every counted (eligible) response read for this concept, inside or outside the window."
          },
          "counted_in_window": {
            "type": "integer",
            "minimum": 0
          },
          "distinct_sessions": {
            "type": "integer",
            "minimum": 0
          },
          "last_independent_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "not_independent_responses": {
            "type": "integer",
            "minimum": 0,
            "description": "Responses excluded because assistance was used, unreported or unknown, or feedback or a hint came first."
          },
          "last_not_independent_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "state_revision",
          "status",
          "independent_responses",
          "counted_in_window",
          "distinct_sessions",
          "last_independent_at",
          "not_independent_responses",
          "last_not_independent_at"
        ]
      },
      "CandidateItem": {
        "type": "object",
        "properties": {
          "item_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "item_version": {
            "type": "string"
          },
          "novelty": {
            "$ref": "#/components/schemas/ItemNovelty"
          },
          "last_answered_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "item_id",
          "item_version",
          "novelty",
          "last_answered_at"
        ]
      },
      "ItemNovelty": {
        "type": "string",
        "enum": [
          "new_to_learner",
          "previously_answered"
        ],
        "description": "Whether the learner had responded to this item version before. A correct response on a repeated item may reflect a remembered answer rather than the skill."
      },
      "CheckFollowUp": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "observed",
              "awaiting",
              "not_observed"
            ],
            "description": "`observed`: an independent response on the concept occurred in the window. `awaiting`: the window is open, or responses in it are not yet evaluated. `not_observed`: the window closed without one; this is missing follow-up, neither failure nor success."
          },
          "window_ends_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "observed": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "event_id": {
                "type": "string",
                "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
                "example": "learner_demo_01"
              },
              "item_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "item_version": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "occurred_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "correct": {
                "type": "boolean"
              },
              "delay_hours": {
                "type": "number",
                "minimum": 0,
                "description": "Observed delay between the plan and this response."
              },
              "item_novelty": {
                "$ref": "#/components/schemas/ItemNovelty"
              },
              "candidate_item": {
                "type": "boolean"
              },
              "construct_type": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "practice",
                  "formative_check",
                  "immediate_post_test",
                  "delayed_check",
                  "near_transfer",
                  "far_transfer",
                  "external_assessment",
                  null
                ],
                "description": "What the product said this response measured, or null when it did not say."
              },
              "intended_delay_seconds": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "The delay the product planned for this measurement, when it reported one."
              },
              "intervening_not_independent_responses": {
                "type": "integer",
                "minimum": 0,
                "description": "Responses on the concept between the plan and this one that were not independent: assisted, of unknown assistance, revealed, shared or feedback-affected."
              },
              "intervening_uncounted_responses": {
                "type": "integer",
                "minimum": 0,
                "description": "Other responses on the concept between the plan and this one that did not count, such as repeats in a session or partial credit."
              }
            },
            "required": [
              "event_id",
              "item_id",
              "item_version",
              "occurred_at",
              "correct",
              "delay_hours",
              "item_novelty",
              "candidate_item",
              "construct_type",
              "intended_delay_seconds",
              "intervening_not_independent_responses",
              "intervening_uncounted_responses"
            ]
          },
          "responses_in_window": {
            "type": "integer",
            "minimum": 0,
            "description": "Every response on the concept in the window, counted or not: the learner time the follow-up used."
          },
          "not_independent_responses_in_window": {
            "type": "integer",
            "minimum": 0
          },
          "responses_awaiting_evaluation": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "status",
          "window_ends_at",
          "observed",
          "responses_in_window",
          "not_independent_responses_in_window",
          "responses_awaiting_evaluation"
        ]
      },
      "CheckDataGap": {
        "type": "string",
        "enum": [
          "sessions_not_reported",
          "no_scoreable_items"
        ],
        "description": "A gap no check can close. `sessions_not_reported`: enough counted attempts exist, but events carry no session IDs, so the two-session disclosure rule can never be met. `no_scoreable_items`: no binary item version is mapped to the concept, so a response could not count under eligibility-v1."
      },
      "CreateCheckPlanRequest": {
        "type": "object",
        "properties": {
          "max_checks": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "description": "Burden budget: the most checks to schedule, each one scored response. Default 5."
          },
          "persistence_delay_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 29,
            "description": "Days after the latest independent evidence at which a persistence check becomes due. Default 7. A declared choice, not a validated interval."
          },
          "follow_up_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 60,
            "description": "Days after issue during which an independent response counts as follow-up. Default 7."
          }
        },
        "additionalProperties": false
      },
      "NextStep": {
        "type": "object",
        "properties": {
          "learner_id": {
            "type": "string"
          },
          "capability": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "suggested",
              "disabled"
            ],
            "description": "`suggested`: a suggestion is below. `disabled`: suggestions are off for this project, and no record was read."
          },
          "disabled_reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "switched_off",
              "live_project",
              null
            ]
          },
          "suggestion": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "check_first",
                  "use_tested_version",
                  "run_an_experiment"
                ],
                "description": "`check_first`: one unaided check before choosing how to teach. `use_tested_version`: a randomized experiment covering this skill supports a version on a later result. `run_an_experiment`: nothing tested yet; teach as now and test the options."
              },
              "reason_codes": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "NO_UNAIDED_RESULT_YET",
                    "NO_CHECK_SINCE_TEACHING",
                    "FAIR_TEST_SUPPORTS_VERSION",
                    "NO_TESTED_VERSION"
                  ]
                },
                "minItems": 1
              },
              "version": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "experiment_id": {
                    "type": "string"
                  },
                  "arm": {
                    "type": "string",
                    "description": "The version a fair test supports."
                  },
                  "versus": {
                    "type": "string",
                    "description": "What it was compared with. When the comparison group itself is supported, the version it leads by the least."
                  },
                  "intervention_policy_ref": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "main_result": {
                    "type": "object",
                    "properties": {
                      "role": {
                        "$ref": "#/components/schemas/EvidenceRole"
                      },
                      "measurement_depth": {
                        "$ref": "#/components/schemas/MeasurementDepth"
                      },
                      "difference": {
                        "type": "number",
                        "description": "Its average lead on the main result over what it was compared with."
                      },
                      "lower_bound": {
                        "type": "number",
                        "description": "The smallest lead the 95% interval allows over what it was compared with. When the comparison group is supported, its smallest lead over any other version."
                      },
                      "interval": {
                        "oneOf": [
                          {
                            "type": "object",
                            "properties": {
                              "kind": {
                                "type": "string",
                                "enum": [
                                  "bootstrap_percentile"
                                ]
                              },
                              "level": {
                                "type": "number",
                                "enum": [
                                  0.95
                                ]
                              },
                              "lower": {
                                "type": "number"
                              },
                              "upper": {
                                "type": "number"
                              },
                              "resamples": {
                                "type": "integer"
                              },
                              "seed": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "kind",
                              "level",
                              "lower",
                              "upper",
                              "resamples",
                              "seed"
                            ]
                          },
                          {
                            "type": "object",
                            "properties": {
                              "kind": {
                                "type": "string",
                                "enum": [
                                  "not_estimated"
                                ]
                              },
                              "reason": {
                                "type": "string",
                                "enum": [
                                  "insufficient_sample",
                                  "immature"
                                ]
                              }
                            },
                            "required": [
                              "kind",
                              "reason"
                            ]
                          }
                        ]
                      }
                    },
                    "required": [
                      "role",
                      "measurement_depth",
                      "difference",
                      "lower_bound",
                      "interval"
                    ]
                  },
                  "causal_strength": {
                    "$ref": "#/components/schemas/CausalStrength"
                  },
                  "computed_at": {
                    "type": "string",
                    "description": "UTC instant in ISO 8601 format with millisecond precision.",
                    "example": "2026-09-13T09:00:00.000Z"
                  }
                },
                "required": [
                  "experiment_id",
                  "arm",
                  "versus",
                  "intervention_policy_ref",
                  "main_result",
                  "causal_strength",
                  "computed_at"
                ],
                "description": "The tested version, for `use_tested_version` only."
              }
            },
            "required": [
              "kind",
              "reason_codes",
              "version"
            ]
          },
          "basis": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "unaided_results": {
                "type": "integer",
                "minimum": 0,
                "description": "Results on this skill from direct evidence the learner gave on their own (`assistance: unassisted`)."
              },
              "last_unaided_at": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "last_taught_at": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "experiments_considered": {
                "type": "integer",
                "minimum": 0,
                "description": "Started experiments on this skill, or on any skill, with a current result, up to 20."
              },
              "experiments_waiting": {
                "type": "integer",
                "minimum": 0,
                "description": "Started experiments whose result is still being worked out. They are left out until it is."
              }
            },
            "required": [
              "unaided_results",
              "last_unaided_at",
              "last_taught_at",
              "experiments_considered",
              "experiments_waiting"
            ]
          },
          "as_of": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "mode": {
            "type": "string",
            "enum": [
              "shadow"
            ],
            "description": "Shown to your system and the console only. Nothing is sent to a learner, and nothing is stored."
          },
          "delivered": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "rule_version": {
            "type": "string",
            "enum": [
              "next-step-v1"
            ]
          },
          "validation_status": {
            "type": "string",
            "enum": [
              "unvalidated"
            ],
            "description": "No study has shown that following these suggestions helps learners."
          },
          "input_hash": {
            "type": [
              "string",
              "null"
            ],
            "description": "SHA-256 of the canonical inputs the rule read. Null when suggestions are off."
          }
        },
        "required": [
          "learner_id",
          "capability",
          "status",
          "disabled_reason",
          "suggestion",
          "basis",
          "as_of",
          "mode",
          "delivered",
          "rule_version",
          "validation_status",
          "input_hash"
        ]
      },
      "EvidenceRole": {
        "type": "string",
        "enum": [
          "immediate",
          "immediate_transfer",
          "delayed_retention",
          "delayed_transfer",
          "external"
        ],
        "description": "`immediate`: a direct check within a day of delivery. `immediate_transfer`: the same on an item the learner had not seen. `delayed_retention`: a direct check at least a day later; a practiced item is allowed and reported. `delayed_transfer`: at least a day later, on an unseen or transfer item. `external`: an external assessment, at least a day later."
      },
      "Prediction": {
        "type": "object",
        "properties": {
          "prediction_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "learner_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "concept_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "item_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "item_version": {
            "type": "string"
          },
          "target_definition": {
            "$ref": "#/components/schemas/TargetDefinition"
          },
          "horizon_seconds": {
            "type": "integer"
          },
          "issued_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "feature_cutoff": {
            "type": "string",
            "description": "No evidence recorded after this instant was available to the model. Evidence that arrives later cannot change this prediction.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "expires_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "model_version": {
            "$ref": "#/components/schemas/PredictionModelVersion"
          },
          "model_kind": {
            "type": "string"
          },
          "model_artifact_hash": {
            "type": "string",
            "description": "SHA-256 of the model definition, so a result can never be attributed to a different rule."
          },
          "evidence_level": {
            "type": "string",
            "enum": [
              "unvalidated",
              "retrospective",
              "prospective"
            ]
          },
          "model_card_path": {
            "type": "string",
            "description": "The model card's file in OutFigure's own repository."
          },
          "model_card_url": {
            "type": "string",
            "format": "uri",
            "description": "The same model card on the public site: what the model predicts, what it reads, when it declines to predict and what it must not be used for. Absent from responses stored before it existed."
          },
          "mode": {
            "type": "string",
            "enum": [
              "shadow"
            ],
            "description": "Nothing is delivered to a learner, and no decision is automated from this."
          },
          "design_status": {
            "type": "string",
            "enum": [
              "predictive"
            ],
            "description": "A guess made before the result, scored against what actually happened."
          },
          "p_correct": {
            "type": "number",
            "exclusiveMinimum": 0,
            "exclusiveMaximum": 1
          },
          "uncertainty": {
            "$ref": "#/components/schemas/PredictionUncertainty"
          },
          "features": {
            "type": "object",
            "additionalProperties": {},
            "description": "Exactly what the model read, frozen at the cutoff."
          },
          "state_revision": {
            "type": [
              "integer",
              "null"
            ]
          },
          "eligibility_version": {
            "type": "string"
          },
          "prediction_set_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111",
            "description": "The request that froze this prediction and its shadows; equal to `prediction_id`. An outcome recorded for it is recorded for every prediction in the set. Absent from responses stored before sets existed."
          },
          "shadow_model_versions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PredictionModelVersion"
            },
            "description": "Other models frozen beside this one on the same evidence, issue time and window, so evaluations can compare them on the same predictions. They are never returned as the prediction and never shown to a learner."
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Plain statements of what this number is not."
          }
        },
        "required": [
          "prediction_id",
          "learner_id",
          "concept_id",
          "item_id",
          "item_version",
          "target_definition",
          "horizon_seconds",
          "issued_at",
          "feature_cutoff",
          "expires_at",
          "model_version",
          "model_kind",
          "model_artifact_hash",
          "evidence_level",
          "model_card_path",
          "mode",
          "design_status",
          "p_correct",
          "uncertainty",
          "features",
          "state_revision",
          "eligibility_version",
          "limitations"
        ]
      },
      "TargetDefinition": {
        "type": "string",
        "enum": [
          "next_eligible_response_v1"
        ],
        "description": "`next_eligible_response_v1`: the first eligible response (binary, explicitly unassisted, first for that item in its session) by this learner on this concept and item version, within the horizon. It is a next-response target inside a window, not a retention measurement."
      },
      "PredictionModelVersion": {
        "type": "string",
        "enum": [
          "baseline-v1",
          "recent-v1"
        ],
        "description": "`baseline-v1`: the learner's own eligible accuracy on the concept, smoothed as (correct + 1) / (attempts + 2); the same rule the offline audit evaluates as `learner_accuracy`. `recent-v1`: the same smoothing over the learner's five most recent eligible responses on the concept, a candidate compared with the baseline. Both unvalidated and shadow mode."
      },
      "PredictionUncertainty": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "not_estimated"
            ]
          },
          "interval": {
            "type": "null"
          },
          "calibration_status": {
            "type": "string",
            "enum": [
              "unvalidated"
            ]
          }
        },
        "required": [
          "kind",
          "interval",
          "calibration_status"
        ],
        "description": "No interval is published until a documented method exists and has been checked. Unvalidated means exactly that."
      },
      "PredictionRequest": {
        "type": "object",
        "properties": {
          "learner_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "concept_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "item_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "item_version": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "Version of the item content. A content edit needs a new version, never an edit.",
            "example": "1"
          },
          "target_definition": {
            "$ref": "#/components/schemas/TargetDefinition"
          },
          "horizon_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 3600,
            "maximum": 7776000,
            "description": "Window length. Defaults to 604800 (seven days)."
          },
          "model_version": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "baseline-v1",
              "recent-v1",
              null
            ],
            "description": "The prediction returned. Defaults to `baseline-v1`. The model must support the target. Every other model that supports it is frozen beside it as a shadow, on the same evidence, for comparison."
          }
        },
        "required": [
          "learner_id",
          "concept_id",
          "item_id",
          "item_version",
          "target_definition"
        ],
        "additionalProperties": false,
        "description": "Asks for a forecast about a future response. The server sets the issue time and the feature cutoff; a client cannot backdate either, and no outcome label is accepted here."
      },
      "Outcome": {
        "type": "object",
        "properties": {
          "outcome_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "prediction_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "status": {
            "type": "string",
            "enum": [
              "matched",
              "censored"
            ],
            "description": "`matched`: an eligible response in the window was linked. `censored`: the window closed without one, which is missing evidence and never a wrong answer."
          },
          "outcome_event_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "observed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "correct": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "matched_by": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "api",
              "expiry",
              null
            ]
          },
          "censor_reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "no_eligible_response_in_window",
              null
            ]
          },
          "matched_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "outcome_id",
          "prediction_id",
          "status",
          "outcome_event_id",
          "observed_at",
          "correct",
          "matched_by",
          "censor_reason",
          "matched_at"
        ]
      },
      "OutcomeRequest": {
        "type": "object",
        "properties": {
          "prediction_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "outcome_event_id": {
            "type": "string",
            "description": "A response event already stored in this project. The service reads it.",
            "example": "learner_demo_01"
          }
        },
        "required": [
          "prediction_id",
          "outcome_event_id"
        ],
        "additionalProperties": false,
        "description": "Links a stored response event to a prediction. Correctness, time, learner, item version and eligibility all come from the stored event; the body cannot override any of them."
      },
      "DataRequest": {
        "type": "object",
        "properties": {
          "data_request_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "kind": {
            "type": "string",
            "enum": [
              "export",
              "erasure"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "completed",
              "failed"
            ],
            "description": "`pending`: an erasure has blocked access and is still removing data; the service finishes it. `completed`: done. `failed`: the service could not finish and will not retry."
          },
          "created_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Exports only: after this instant the file is deleted and the download returns 410.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "download": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "url": {
                "type": "string"
              },
              "sha256": {
                "type": "string",
                "description": "SHA-256 of the whole file, hex."
              },
              "records": {
                "type": "object",
                "additionalProperties": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            },
            "required": [
              "url",
              "sha256",
              "records"
            ],
            "description": "Present while an export file can still be downloaded."
          },
          "removed": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Erasures only: rows removed, by class (events, states, check plans, predictions, outcomes, jobs, stored responses, the learner). Null until the erasure completes."
          }
        },
        "required": [
          "data_request_id",
          "kind",
          "status",
          "created_at",
          "completed_at",
          "expires_at",
          "download",
          "removed"
        ],
        "description": "One export or erasure. It never includes the learner's external ID, so it remains readable after the learner is erased."
      },
      "ExportRequest": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "jsonl"
            ],
            "description": "JSON Lines: a manifest line, then one record per line."
          }
        },
        "required": [
          "format"
        ],
        "additionalProperties": false
      },
      "OutcomeIngestReceipt": {
        "type": "object",
        "properties": {
          "recorded_count": {
            "type": "integer",
            "minimum": 0
          },
          "duplicate_count": {
            "type": "integer",
            "minimum": 0
          },
          "contracts_opened": {
            "type": "integer",
            "minimum": 0
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer",
                  "minimum": 0
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "recorded",
                    "duplicate"
                  ]
                },
                "contract_id": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uuid",
                  "example": "11111111-1111-4111-8111-111111111111"
                }
              },
              "required": [
                "index",
                "status",
                "contract_id"
              ]
            },
            "description": "One result per submitted record, in request order. Your IDs are not echoed, so a stored response never carries them past an erasure."
          }
        },
        "required": [
          "recorded_count",
          "duplicate_count",
          "contracts_opened",
          "results"
        ]
      },
      "InterventionInput": {
        "type": "object",
        "properties": {
          "schema_version": {
            "type": "string",
            "enum": [
              "1.0"
            ]
          },
          "intervention_id": {
            "type": "string",
            "description": "Unique per project. Resending identical content is a duplicate; different content under the same ID returns 409.",
            "example": "int_demo_0001"
          },
          "learner_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "capability": {
            "type": "string",
            "description": "The capability the intervention was meant to change, as your catalog names it. The same identifier a concept is registered under.",
            "example": "fractions.add_unlike_denominators"
          },
          "class": {
            "$ref": "#/components/schemas/InterventionClass"
          },
          "delivered_at": {
            "type": "string",
            "description": "When the learner received it. Only delivered interventions are recorded; a proposal that never reached the learner is not an intervention.",
            "example": "2026-09-13T09:00:00Z"
          },
          "source": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "example": "tutor_service"
          },
          "session_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "agent_id": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "Which of your agents acted."
          },
          "model_provider": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "Optional. Some teams do not disclose their model stack, and need not."
          },
          "model_name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$"
          },
          "prompt_policy_version": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "Opaque, immutable ID of the prompt or policy. Never the prompt text."
          },
          "content_hash": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^sha256:[0-9a-f]{64}$",
            "description": "Hash of the delivered content, `sha256:` and 64 lowercase hex digits. Lets two deliveries be compared without the content being sent."
          },
          "content_ref": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the content lives in your system. OutFigure stores the reference, never the content.",
            "example": "learner_demo_01"
          },
          "turn_count": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 10000
          },
          "duration_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 86400000
          },
          "assistance_level": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "How much the intervention did for the learner, in your own vocabulary.",
            "example": "guided"
          },
          "strategy_tags": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "maxLength": 64,
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$"
            },
            "maxItems": 16
          },
          "experiment": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "experiment_id": {
                "$ref": "#/components/schemas/ExperimentId"
              },
              "arm": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$"
              }
            },
            "required": [
              "experiment_id",
              "arm"
            ],
            "additionalProperties": false,
            "description": "The experiment and arm this delivery belonged to. This is the exposure record: an assignment alone is never read as exposure."
          },
          "contract": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "template": {
                "$ref": "#/components/schemas/ContractTemplate"
              }
            },
            "required": [
              "template"
            ],
            "additionalProperties": false,
            "description": "Open an Outcome Contract for this intervention with a predefined evidence plan, in the same request."
          }
        },
        "required": [
          "schema_version",
          "intervention_id",
          "learner_id",
          "capability",
          "class",
          "delivered_at",
          "source"
        ],
        "additionalProperties": false
      },
      "InterventionClass": {
        "type": "string",
        "enum": [
          "socratic_question",
          "hint",
          "worked_example",
          "contrast_case",
          "error_explanation",
          "concept_explanation",
          "retrieval_prompt",
          "practice_item_selection",
          "difficulty_adjustment",
          "prerequisite_review",
          "metacognitive_prompt",
          "feedback_corrective",
          "feedback_elaborative",
          "solution_reveal",
          "content_recommendation",
          "human_escalation",
          "other"
        ],
        "description": "The teaching move, from a stable taxonomy. Your own finer labels go in `strategy_tags`. `other` when none fits; it is compared only with itself."
      },
      "ContractTemplate": {
        "type": "string",
        "enum": [
          "durable_capability_gain",
          "retention_7d",
          "immediate_only"
        ],
        "description": "A predefined evidence plan. `durable_capability_gain`: an immediate check, then retention and transfer 4 to 10 days later. `retention_7d`: an immediate check, then retention 6 to 8 days later. `immediate_only`: one check within 30 minutes, which can never show more than MD2."
      },
      "EvidenceInput": {
        "type": "object",
        "properties": {
          "schema_version": {
            "type": "string",
            "enum": [
              "1.0"
            ]
          },
          "evidence_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "ev_demo_0001"
          },
          "learner_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "capability": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "observed_at": {
            "type": "string",
            "description": "ISO 8601 instant with an explicit offset (`Z` or `±hh:mm`), at most millisecond precision. Stored and returned in UTC.",
            "example": "2026-09-13T09:00:00Z"
          },
          "evidence_type": {
            "$ref": "#/components/schemas/EvidenceType"
          },
          "score": {
            "type": "number"
          },
          "scale": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ],
            "description": "The score range as `[minimum, maximum]`.",
            "example": [
              0,
              1
            ]
          },
          "assistance": {
            "$ref": "#/components/schemas/EvidenceAssistance"
          },
          "is_unseen_item": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "True when the learner had never seen this item. Missing means unknown, which cannot support a transfer claim."
          },
          "is_transfer": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "True when the task applies the capability in a new form or context. Missing means unknown."
          },
          "item_ref": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "scorer": {
            "type": "object",
            "properties": {
              "type": {
                "$ref": "#/components/schemas/ScorerType"
              },
              "version": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$"
              }
            },
            "required": [
              "type"
            ],
            "additionalProperties": false,
            "description": "The scoring method and its version."
          },
          "reliability": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
                "example": "cronbach_alpha"
              },
              "value": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              }
            },
            "required": [
              "kind",
              "value"
            ],
            "additionalProperties": false
          },
          "session_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "source": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$"
          }
        },
        "required": [
          "schema_version",
          "evidence_id",
          "learner_id",
          "capability",
          "observed_at",
          "evidence_type",
          "score",
          "scale",
          "assistance",
          "scorer",
          "source"
        ],
        "additionalProperties": false
      },
      "EvidenceType": {
        "type": "string",
        "enum": [
          "assessment_item",
          "constructed_response",
          "skill_demonstration",
          "simulation",
          "external_assessment",
          "state_model",
          "behavioral",
          "engagement",
          "model_judged"
        ],
        "description": "What was observed. Direct performance: `assessment_item`, `constructed_response`, `skill_demonstration`, `simulation`, `external_assessment`. Proxy, which can never show more than MD1: `state_model` (an inference, not an observation), `behavioral`, `engagement`, `model_judged`."
      },
      "EvidenceAssistance": {
        "type": "string",
        "enum": [
          "unassisted",
          "hinted",
          "solution_revealed",
          "collaborative",
          "external",
          "unknown"
        ],
        "description": "Help the learner had on this observation. Only `unassisted` is independent evidence. `unknown` is never read as unassisted."
      },
      "ScorerType": {
        "type": "string",
        "enum": [
          "deterministic",
          "human_rubric",
          "model_rubric",
          "external",
          "unknown"
        ],
        "description": "Who or what scored it. A score from `model_rubric` is treated as proxy evidence until its agreement with human or external scoring is validated."
      },
      "StateInput": {
        "type": "object",
        "properties": {
          "schema_version": {
            "type": "string",
            "enum": [
              "1.0"
            ]
          },
          "state_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "learner_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "capability": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "observed_at": {
            "type": "string",
            "description": "ISO 8601 instant with an explicit offset (`Z` or `±hh:mm`), at most millisecond precision. Stored and returned in UTC.",
            "example": "2026-09-13T09:00:00Z"
          },
          "state_value": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Estimated mastery in [0, 1], as your model reports it."
          },
          "state_label": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "example": "partial"
          },
          "distribution": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string",
                  "maxLength": 64,
                  "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$"
                },
                "p": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 1
                }
              },
              "required": [
                "label",
                "p"
              ],
              "additionalProperties": false
            },
            "minItems": 2,
            "maxItems": 32
          },
          "uncertainty": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1
          },
          "source_adapter": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "Which model produced the state.",
            "example": "customer_kt_model"
          },
          "model_version": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$"
          }
        },
        "required": [
          "schema_version",
          "state_id",
          "learner_id",
          "capability",
          "observed_at",
          "source_adapter"
        ],
        "additionalProperties": false
      },
      "OutcomeContract": {
        "type": "object",
        "properties": {
          "contract_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "schema_version": {
            "type": "string",
            "enum": [
              "1.0"
            ]
          },
          "learner_id": {
            "type": "string"
          },
          "capability": {
            "type": "string"
          },
          "intervention_id": {
            "type": "string"
          },
          "template": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ContractTemplate"
              },
              {
                "type": "null"
              }
            ]
          },
          "evidence_plan": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvidencePlanEntry"
            }
          },
          "baseline": {
            "type": "object",
            "properties": {
              "state_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "lookback_seconds": {
                "type": "integer"
              }
            },
            "required": [
              "state_id",
              "lookback_seconds"
            ]
          },
          "intended_outcome": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "capability_gain"
                ]
              },
              "direction": {
                "type": "string",
                "enum": [
                  "increase"
                ]
              },
              "minimum_effect": {
                "type": [
                  "number",
                  "null"
                ]
              }
            },
            "required": [
              "type",
              "direction",
              "minimum_effect"
            ]
          },
          "attribution_plan": {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "observational",
                  "randomized"
                ]
              },
              "experiment_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "mode",
              "experiment_id"
            ]
          },
          "policy": {
            "type": "object",
            "properties": {
              "high_stakes": {
                "type": "boolean"
              },
              "human_review_required": {
                "type": "boolean"
              }
            },
            "required": [
              "high_stakes",
              "human_review_required"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "closed"
            ],
            "description": "`open` while any window can still receive evidence. `closed` once every window and the late-arrival grace have passed."
          },
          "delivered_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "windows_close_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "expires_at": {
            "type": "string",
            "description": "When the contract stops waiting: the last window plus 3 days for late arrivals.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "latest_receipt_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "created_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "contract_id",
          "schema_version",
          "learner_id",
          "capability",
          "intervention_id",
          "template",
          "evidence_plan",
          "baseline",
          "intended_outcome",
          "attribution_plan",
          "policy",
          "status",
          "delivered_at",
          "windows_close_at",
          "expires_at",
          "latest_receipt_id",
          "created_at"
        ]
      },
      "EvidencePlanEntry": {
        "type": "object",
        "properties": {
          "role": {
            "$ref": "#/components/schemas/EvidenceRole"
          },
          "window": {
            "type": "object",
            "properties": {
              "min_seconds": {
                "type": "integer",
                "minimum": 0,
                "maximum": 15552000
              },
              "max_seconds": {
                "type": "integer",
                "minimum": 1,
                "maximum": 15552000
              }
            },
            "required": [
              "min_seconds",
              "max_seconds"
            ],
            "additionalProperties": false,
            "description": "Seconds after delivery, inclusive at both ends."
          },
          "min_observations": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20
          },
          "assistance": {
            "$ref": "#/components/schemas/AssistanceRule"
          }
        },
        "required": [
          "role",
          "window",
          "min_observations",
          "assistance"
        ],
        "additionalProperties": false
      },
      "AssistanceRule": {
        "type": "string",
        "enum": [
          "unassisted_required",
          "unassisted_preferred"
        ],
        "description": "`unassisted_required`: only `unassisted` evidence counts. `unassisted_preferred`, allowed on immediate roles only: `unknown` assistance also counts and is flagged. Evidence with known help never counts under either rule, and delayed and external roles always require `unassisted`."
      },
      "ContractCreateRequest": {
        "type": "object",
        "properties": {
          "intervention_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          },
          "template": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ContractTemplate"
              },
              {
                "type": "null"
              }
            ]
          },
          "evidence_plan": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/EvidencePlanEntry"
            },
            "minItems": 1,
            "maxItems": 5
          },
          "baseline": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "state_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "A state observation to read as the baseline, if your model issued one.",
                "example": "learner_demo_01"
              },
              "lookback_seconds": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "maximum": 15552000,
                "description": "Without a state, direct unassisted evidence this far before delivery is the baseline. Default 30 days."
              }
            },
            "additionalProperties": false
          },
          "minimum_effect": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "The smallest change that would matter to you, recorded before any evidence is read. It changes no calculation."
          },
          "policy": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "high_stakes": {
                "type": "boolean"
              },
              "human_review_required": {
                "type": "boolean"
              }
            },
            "required": [
              "high_stakes",
              "human_review_required"
            ],
            "additionalProperties": false,
            "description": "Recorded with the contract for a later decision layer. It changes nothing in this build."
          }
        },
        "required": [
          "intervention_id"
        ],
        "additionalProperties": false
      },
      "OutcomeReceiptList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutcomeReceipt"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null when there are no more results."
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "OutcomeReceipt": {
        "type": "object",
        "properties": {
          "receipt_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "contract_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "supersedes_receipt_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "schema_version": {
            "type": "string",
            "enum": [
              "1.0"
            ]
          },
          "intervention_id": {
            "type": "string"
          },
          "learner_id": {
            "type": "string"
          },
          "capability": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/ReceiptStatus"
          },
          "observed_outcome": {
            "type": "object",
            "properties": {
              "direction": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "positive",
                  "negative",
                  "unchanged",
                  null
                ],
                "description": "The sign of the observed change. Null when there is no baseline or no result."
              },
              "baseline_metric": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "baseline_source": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "state_observation",
                  "prior_evidence",
                  null
                ]
              },
              "baseline_n": {
                "type": "integer",
                "minimum": 0
              },
              "post_metric": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "post_role": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "immediate",
                  "immediate_transfer",
                  "delayed_retention",
                  "delayed_transfer",
                  "external",
                  null
                ],
                "description": "The deepest satisfied role, whose metric is the post metric."
              },
              "delta": {
                "type": [
                  "number",
                  "null"
                ]
              }
            },
            "required": [
              "direction",
              "baseline_metric",
              "baseline_source",
              "baseline_n",
              "post_metric",
              "post_role",
              "delta"
            ]
          },
          "durability": {
            "type": "object",
            "properties": {
              "immediate": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "immediate_n": {
                "type": "integer",
                "minimum": 0
              },
              "delayed": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "delayed_n": {
                "type": "integer",
                "minimum": 0
              },
              "median_delay_seconds": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            },
            "required": [
              "immediate",
              "immediate_n",
              "delayed",
              "delayed_n",
              "median_delay_seconds"
            ]
          },
          "roles": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RoleResult"
            }
          },
          "measurement_depth": {
            "$ref": "#/components/schemas/MeasurementDepth"
          },
          "causal_strength": {
            "$ref": "#/components/schemas/CausalStrength"
          },
          "design_status": {
            "$ref": "#/components/schemas/DesignStatus"
          },
          "uncertainty": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "wilson"
                    ]
                  },
                  "level": {
                    "type": "number",
                    "enum": [
                      0.95
                    ]
                  },
                  "lower": {
                    "type": "number"
                  },
                  "upper": {
                    "type": "number"
                  },
                  "n": {
                    "type": "integer"
                  }
                },
                "required": [
                  "kind",
                  "level",
                  "lower",
                  "upper",
                  "n"
                ]
              },
              {
                "type": "object",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "not_estimated"
                    ]
                  },
                  "reason": {
                    "type": "string",
                    "enum": [
                      "no_observations",
                      "non_binary_scores",
                      "individual_change"
                    ]
                  }
                },
                "required": [
                  "kind",
                  "reason"
                ]
              }
            ],
            "description": "On the post metric. A change is never given one."
          },
          "evidence": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConsideredEvidence"
            }
          },
          "experiment": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "experiment_id": {
                "type": "string"
              },
              "arm": {
                "type": "string"
              }
            },
            "required": [
              "experiment_id",
              "arm"
            ]
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReceiptWarning"
            }
          },
          "evaluator": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            },
            "required": [
              "name",
              "version"
            ]
          },
          "linker_version": {
            "type": "string"
          },
          "evidence_watermark": {
            "type": [
              "string",
              "null"
            ],
            "description": "The latest arrival time among the evidence this version read.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "as_of": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "issued_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "receipt_id",
          "contract_id",
          "version",
          "supersedes_receipt_id",
          "schema_version",
          "intervention_id",
          "learner_id",
          "capability",
          "status",
          "observed_outcome",
          "durability",
          "roles",
          "measurement_depth",
          "causal_strength",
          "design_status",
          "uncertainty",
          "evidence",
          "experiment",
          "warnings",
          "evaluator",
          "linker_version",
          "evidence_watermark",
          "as_of",
          "issued_at"
        ]
      },
      "RoleResult": {
        "type": "object",
        "properties": {
          "role": {
            "$ref": "#/components/schemas/EvidenceRole"
          },
          "window": {
            "type": "object",
            "properties": {
              "min_seconds": {
                "type": "integer"
              },
              "max_seconds": {
                "type": "integer"
              }
            },
            "required": [
              "min_seconds",
              "max_seconds"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "satisfied",
              "unsatisfied",
              "open"
            ],
            "description": "`satisfied`: enough eligible evidence arrived. `open`: not yet, and its window has not closed. `unsatisfied`: its window closed without enough."
          },
          "eligible_n": {
            "type": "integer",
            "minimum": 0
          },
          "unassisted_n": {
            "type": "integer",
            "minimum": 0
          },
          "metric": {
            "type": [
              "number",
              "null"
            ],
            "description": "Mean normalized score of the eligible evidence, 0 to 1. Null without any."
          },
          "interval": {
            "$ref": "#/components/schemas/OutcomeInterval"
          }
        },
        "required": [
          "role",
          "window",
          "status",
          "eligible_n",
          "unassisted_n",
          "metric",
          "interval"
        ]
      },
      "OutcomeInterval": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "wilson"
                ]
              },
              "level": {
                "type": "number",
                "enum": [
                  0.95
                ]
              },
              "lower": {
                "type": "number"
              },
              "upper": {
                "type": "number"
              },
              "n": {
                "type": "integer"
              }
            },
            "required": [
              "kind",
              "level",
              "lower",
              "upper",
              "n"
            ]
          },
          {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "not_estimated"
                ]
              },
              "reason": {
                "type": "string",
                "enum": [
                  "no_observations",
                  "non_binary_scores",
                  "individual_change"
                ]
              }
            },
            "required": [
              "kind",
              "reason"
            ]
          }
        ],
        "description": "A 95% Wilson score interval where every score is 0 or 1; otherwise not estimated, and the reason says why. Never a guessed range."
      },
      "ConsideredEvidence": {
        "type": "object",
        "properties": {
          "evidence_id": {
            "type": "string"
          },
          "role": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/EvidenceRole"
              },
              {
                "type": "null"
              }
            ]
          },
          "reason": {
            "$ref": "#/components/schemas/LinkReason"
          },
          "offset_seconds": {
            "type": "integer",
            "description": "Seconds after delivery; negative if before."
          }
        },
        "required": [
          "evidence_id",
          "role",
          "reason",
          "offset_seconds"
        ]
      },
      "LinkReason": {
        "type": "string",
        "enum": [
          "eligible",
          "eligible_assistance_unknown",
          "before_delivery",
          "outside_window",
          "assisted",
          "assistance_unknown",
          "item_seen_before",
          "item_novelty_unknown",
          "not_external",
          "proxy_evidence",
          "model_scored"
        ],
        "description": "Why a piece of evidence did or did not count toward a role. Every candidate the linker considered is listed with its reason, so an exclusion is never silent."
      },
      "ReceiptWarning": {
        "type": "string",
        "enum": [
          "NO_FOLLOWUP_EVIDENCE",
          "INSUFFICIENT_ELIGIBLE_EVIDENCE",
          "CONFOUNDED_BY_ASSISTANCE",
          "CAPABILITY_MAPPING_AMBIGUOUS",
          "ASSESSMENT_RELIABILITY_UNKNOWN",
          "MISSING_BASELINE",
          "TEMPORAL_ASSOCIATION_ONLY",
          "DELAYED_WINDOW_NOT_MATURE",
          "PRACTICED_ITEMS_ONLY",
          "ASSISTANCE_UNKNOWN",
          "PROXY_EVIDENCE_ONLY",
          "MODEL_SCORED_EVIDENCE",
          "INDIVIDUAL_CHANGE_NOT_ATTRIBUTABLE",
          "REVISED_BY_LATE_EVIDENCE"
        ],
        "description": "What this receipt cannot support, stated rather than hidden. See the methodology."
      },
      "Experiment": {
        "type": "object",
        "properties": {
          "experiment_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "hypothesis": {
            "type": "string"
          },
          "capability": {
            "type": [
              "string",
              "null"
            ]
          },
          "randomization_unit": {
            "type": "string",
            "enum": [
              "learner",
              "cluster"
            ]
          },
          "arms": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "arm": {
                  "type": "string"
                },
                "allocation": {
                  "type": "integer"
                },
                "intervention_policy_ref": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "arm",
                "allocation",
                "intervention_policy_ref"
              ]
            }
          },
          "control_arm": {
            "type": "string"
          },
          "primary_outcome": {
            "$ref": "#/components/schemas/ExperimentOutcomeSpec"
          },
          "secondary_outcomes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExperimentOutcomeSpec"
            }
          },
          "minimum_meaningful_effect": {
            "type": [
              "number",
              "null"
            ]
          },
          "planned_units_per_arm": {
            "type": [
              "integer",
              "null"
            ]
          },
          "guardrails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExperimentGuardrailSpec"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "running",
              "stopped"
            ]
          },
          "config_hash": {
            "type": [
              "string",
              "null"
            ],
            "description": "SHA-256 of the declaration, fixed when the experiment started."
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "stopped_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "created_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "experiment_id",
          "name",
          "hypothesis",
          "capability",
          "randomization_unit",
          "arms",
          "control_arm",
          "primary_outcome",
          "secondary_outcomes",
          "minimum_meaningful_effect",
          "planned_units_per_arm",
          "guardrails",
          "status",
          "config_hash",
          "started_at",
          "stopped_at",
          "created_at"
        ]
      },
      "ExperimentOutcomeSpec": {
        "type": "object",
        "properties": {
          "role": {
            "$ref": "#/components/schemas/EvidenceRole"
          },
          "metric": {
            "type": "string",
            "enum": [
              "mean_normalized_score"
            ],
            "description": "Each learner contributes the mean normalized score of their receipt for this role."
          }
        },
        "required": [
          "role",
          "metric"
        ],
        "additionalProperties": false
      },
      "ExperimentGuardrailSpec": {
        "type": "object",
        "properties": {
          "guardrail_id": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "example": "immediate_floor"
          },
          "role": {
            "$ref": "#/components/schemas/EvidenceRole"
          },
          "metric": {
            "type": "string",
            "enum": [
              "mean_normalized_score"
            ],
            "description": "Each learner contributes the mean normalized score of their receipts for this role, as for outcomes."
          },
          "tolerance": {
            "type": "number",
            "minimum": 0,
            "maximum": 0.5,
            "description": "The largest drop below control, on the 0-to-1 scale, that would not count as harm. 0 treats any drop the data can show as harm.",
            "example": 0.05
          }
        },
        "required": [
          "guardrail_id",
          "role",
          "metric",
          "tolerance"
        ],
        "additionalProperties": false,
        "description": "Declared before the start and frozen with it. The guardrail triggers for an arm when a look shows the arm below control by more than `tolerance`, beyond chance at that look's level."
      },
      "ExperimentCreateRequest": {
        "type": "object",
        "properties": {
          "experiment_id": {
            "$ref": "#/components/schemas/ExperimentId"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "hypothesis": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "capability": {
            "type": [
              "string",
              "null"
            ],
            "description": "When set, only receipts on this capability are compared. When absent, every exposed intervention counts, whatever it taught.",
            "example": "learner_demo_01"
          },
          "randomization_unit": {
            "type": "string",
            "enum": [
              "learner",
              "cluster"
            ],
            "description": "`learner` is supported. `cluster` is refused until cluster analysis ships."
          },
          "arms": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExperimentArmInput"
            },
            "minItems": 2,
            "maxItems": 5
          },
          "control_arm": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$"
          },
          "primary_outcome": {
            "$ref": "#/components/schemas/ExperimentOutcomeSpec"
          },
          "secondary_outcomes": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/ExperimentOutcomeSpec"
            },
            "maxItems": 4
          },
          "minimum_meaningful_effect": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Declared before the start and frozen with it. It changes no calculation."
          },
          "planned_units_per_arm": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 20,
            "maximum": 1000000,
            "description": "Learners per arm the experiment is planned to reach. With it, each guardrail looks at a quarter, half, three quarters and all of it; without it, once."
          },
          "guardrails": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/ExperimentGuardrailSpec"
            },
            "maxItems": 4,
            "description": "Outcomes no arm may fall below control on by more than a tolerance, checked while the experiment runs at looks declared in advance (docs/adr/0028-experiment-guardrails.md)."
          }
        },
        "required": [
          "experiment_id",
          "name",
          "hypothesis",
          "randomization_unit",
          "arms",
          "control_arm",
          "primary_outcome"
        ],
        "additionalProperties": false
      },
      "ExperimentArmInput": {
        "type": "object",
        "properties": {
          "arm": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "example": "contrast_cases"
          },
          "allocation": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10000,
            "description": "Share of assignments in basis points. All arms sum to 10,000.",
            "example": 5000
          },
          "intervention_policy_ref": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$",
            "description": "The policy or prompt release this arm delivers, as your system names it."
          }
        },
        "required": [
          "arm",
          "allocation"
        ],
        "additionalProperties": false
      },
      "AssignResult": {
        "type": "object",
        "properties": {
          "assignment_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "experiment_id": {
            "type": "string"
          },
          "arm": {
            "type": "string"
          },
          "assigned_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "method": {
            "type": "string",
            "enum": [
              "randomized",
              "recorded_earlier"
            ],
            "description": "`randomized`: OutFigure assigned the arm by keyed hash. `recorded_earlier`: an assignment for this learner already existed and is returned unchanged; an assignment is never moved."
          },
          "bucket": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 9999,
            "description": "The hash bucket that chose the arm, for audit. Null for an earlier record."
          }
        },
        "required": [
          "assignment_id",
          "experiment_id",
          "arm",
          "assigned_at",
          "method",
          "bucket"
        ]
      },
      "AssignRequest": {
        "type": "object",
        "properties": {
          "learner_id": {
            "type": "string",
            "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
            "example": "learner_demo_01"
          }
        },
        "required": [
          "learner_id"
        ],
        "additionalProperties": false
      },
      "ExperimentResult": {
        "type": "object",
        "properties": {
          "experiment_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "not_started",
              "immature",
              "insufficient_sample",
              "ready"
            ]
          },
          "analyzer": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            },
            "required": [
              "name",
              "version"
            ]
          },
          "config_hash": {
            "type": [
              "string",
              "null"
            ]
          },
          "design_status": {
            "$ref": "#/components/schemas/DesignStatus"
          },
          "causal_strength": {
            "$ref": "#/components/schemas/CausalStrength"
          },
          "population": {
            "type": "string",
            "enum": [
              "exposed_in_assigned_arm"
            ],
            "description": "Learners with a delivered intervention in the arm they were assigned. Not an intention-to-treat analysis; exposure rates are reported so the difference can be judged."
          },
          "primary": {
            "type": "object",
            "properties": {
              "role": {
                "$ref": "#/components/schemas/EvidenceRole"
              },
              "metric": {
                "type": "string",
                "enum": [
                  "mean_normalized_score"
                ]
              },
              "measurement_depth": {
                "$ref": "#/components/schemas/MeasurementDepth"
              },
              "arms": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "arm": {
                      "type": "string"
                    },
                    "assigned": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "exposed": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Assigned learners with a delivered intervention naming this experiment and arm."
                    },
                    "exposed_other_arm": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Assigned learners who received another arm. Left out of the comparison."
                    },
                    "with_outcome": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "matured": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Exposed learners whose outcome window has closed or been satisfied."
                    },
                    "mean": {
                      "type": [
                        "number",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "arm",
                    "assigned",
                    "exposed",
                    "exposed_other_arm",
                    "with_outcome",
                    "matured",
                    "mean"
                  ]
                }
              },
              "effects": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "arm": {
                      "type": "string"
                    },
                    "versus": {
                      "type": "string"
                    },
                    "difference": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Arm mean minus control mean."
                    },
                    "interval": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "kind": {
                              "type": "string",
                              "enum": [
                                "bootstrap_percentile"
                              ]
                            },
                            "level": {
                              "type": "number",
                              "enum": [
                                0.95
                              ]
                            },
                            "lower": {
                              "type": "number"
                            },
                            "upper": {
                              "type": "number"
                            },
                            "resamples": {
                              "type": "integer"
                            },
                            "seed": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "kind",
                            "level",
                            "lower",
                            "upper",
                            "resamples",
                            "seed"
                          ]
                        },
                        {
                          "type": "object",
                          "properties": {
                            "kind": {
                              "type": "string",
                              "enum": [
                                "not_estimated"
                              ]
                            },
                            "reason": {
                              "type": "string",
                              "enum": [
                                "insufficient_sample",
                                "immature"
                              ]
                            }
                          },
                          "required": [
                            "kind",
                            "reason"
                          ]
                        }
                      ]
                    }
                  },
                  "required": [
                    "arm",
                    "versus",
                    "difference",
                    "interval"
                  ]
                }
              }
            },
            "required": [
              "role",
              "metric",
              "measurement_depth",
              "arms",
              "effects"
            ]
          },
          "secondary": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "role": {
                  "$ref": "#/components/schemas/EvidenceRole"
                },
                "metric": {
                  "type": "string",
                  "enum": [
                    "mean_normalized_score"
                  ]
                },
                "measurement_depth": {
                  "$ref": "#/components/schemas/MeasurementDepth"
                },
                "arms": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "arm": {
                        "type": "string"
                      },
                      "assigned": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "exposed": {
                        "type": "integer",
                        "minimum": 0,
                        "description": "Assigned learners with a delivered intervention naming this experiment and arm."
                      },
                      "exposed_other_arm": {
                        "type": "integer",
                        "minimum": 0,
                        "description": "Assigned learners who received another arm. Left out of the comparison."
                      },
                      "with_outcome": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "matured": {
                        "type": "integer",
                        "minimum": 0,
                        "description": "Exposed learners whose outcome window has closed or been satisfied."
                      },
                      "mean": {
                        "type": [
                          "number",
                          "null"
                        ]
                      }
                    },
                    "required": [
                      "arm",
                      "assigned",
                      "exposed",
                      "exposed_other_arm",
                      "with_outcome",
                      "matured",
                      "mean"
                    ]
                  }
                },
                "effects": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "arm": {
                        "type": "string"
                      },
                      "versus": {
                        "type": "string"
                      },
                      "difference": {
                        "type": [
                          "number",
                          "null"
                        ],
                        "description": "Arm mean minus control mean."
                      },
                      "interval": {
                        "oneOf": [
                          {
                            "type": "object",
                            "properties": {
                              "kind": {
                                "type": "string",
                                "enum": [
                                  "bootstrap_percentile"
                                ]
                              },
                              "level": {
                                "type": "number",
                                "enum": [
                                  0.95
                                ]
                              },
                              "lower": {
                                "type": "number"
                              },
                              "upper": {
                                "type": "number"
                              },
                              "resamples": {
                                "type": "integer"
                              },
                              "seed": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "kind",
                              "level",
                              "lower",
                              "upper",
                              "resamples",
                              "seed"
                            ]
                          },
                          {
                            "type": "object",
                            "properties": {
                              "kind": {
                                "type": "string",
                                "enum": [
                                  "not_estimated"
                                ]
                              },
                              "reason": {
                                "type": "string",
                                "enum": [
                                  "insufficient_sample",
                                  "immature"
                                ]
                              }
                            },
                            "required": [
                              "kind",
                              "reason"
                            ]
                          }
                        ]
                      }
                    },
                    "required": [
                      "arm",
                      "versus",
                      "difference",
                      "interval"
                    ]
                  }
                }
              },
              "required": [
                "role",
                "metric",
                "measurement_depth",
                "arms",
                "effects"
              ]
            }
          },
          "guardrails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GuardrailResult"
            }
          },
          "srm": {
            "type": "object",
            "properties": {
              "chi_square": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "df": {
                "type": "integer"
              },
              "p_value": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "threshold": {
                "type": "number"
              },
              "flagged": {
                "type": "boolean"
              }
            },
            "required": [
              "chi_square",
              "df",
              "p_value",
              "threshold",
              "flagged"
            ]
          },
          "missingness": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "arm": {
                  "type": "string"
                },
                "exposed": {
                  "type": "integer"
                },
                "without_outcome": {
                  "type": "integer"
                },
                "fraction": {
                  "type": [
                    "number",
                    "null"
                  ]
                }
              },
              "required": [
                "arm",
                "exposed",
                "without_outcome",
                "fraction"
              ]
            }
          },
          "exposures_without_assignment": {
            "type": "integer",
            "minimum": 0
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "SAMPLE_RATIO_MISMATCH",
                "DIFFERENTIAL_MISSINGNESS",
                "DIFFERENTIAL_EXPOSURE",
                "CROSS_ARM_EXPOSURE",
                "EXPOSURE_WITHOUT_ASSIGNMENT",
                "OUTCOME_WINDOW_NOT_MATURE",
                "GUARDRAIL_TRIGGERED"
              ]
            }
          },
          "data_watermark": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "computed_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "freshness": {
            "type": "string",
            "enum": [
              "current",
              "updating"
            ],
            "description": "`current`: computed from the records as they stand. `updating`: records have changed since `computed_at`; a new result is being computed, and this one describes the records as they were then."
          }
        },
        "required": [
          "experiment_id",
          "status",
          "analyzer",
          "config_hash",
          "design_status",
          "causal_strength",
          "population",
          "primary",
          "secondary",
          "guardrails",
          "srm",
          "missingness",
          "exposures_without_assignment",
          "warnings",
          "data_watermark",
          "computed_at",
          "freshness"
        ]
      },
      "GuardrailResult": {
        "type": "object",
        "properties": {
          "guardrail_id": {
            "type": "string"
          },
          "role": {
            "$ref": "#/components/schemas/EvidenceRole"
          },
          "metric": {
            "type": "string",
            "enum": [
              "mean_normalized_score"
            ]
          },
          "tolerance": {
            "type": "number"
          },
          "measurement_depth": {
            "$ref": "#/components/schemas/MeasurementDepth"
          },
          "looks_planned": {
            "type": "integer",
            "minimum": 1,
            "maximum": 4
          },
          "looks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GuardrailLook"
            },
            "description": "Every look taken, in order. A look is taken once, the first time a stored result reaches it; a look passed before a result could take it is skipped."
          },
          "status": {
            "type": "string",
            "enum": [
              "not_checked",
              "monitoring",
              "clear",
              "triggered"
            ]
          }
        },
        "required": [
          "guardrail_id",
          "role",
          "metric",
          "tolerance",
          "measurement_depth",
          "looks_planned",
          "looks",
          "status"
        ]
      },
      "GuardrailLook": {
        "type": "object",
        "properties": {
          "look": {
            "type": "integer",
            "minimum": 1,
            "maximum": 4
          },
          "required_per_arm": {
            "type": "integer",
            "minimum": 1,
            "description": "Learners with a matured outcome every arm needed for this look."
          },
          "level": {
            "type": "number",
            "description": "One-sided confidence of this look: 1 minus 5% divided by the planned looks."
          },
          "resamples": {
            "type": "integer"
          },
          "taken_at": {
            "type": "string",
            "description": "When the result that took this look was computed.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "arms": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "arm": {
                  "type": "string"
                },
                "versus": {
                  "type": "string"
                },
                "with_outcome": {
                  "type": "integer",
                  "minimum": 0
                },
                "control_with_outcome": {
                  "type": "integer",
                  "minimum": 0
                },
                "difference": {
                  "type": "number",
                  "description": "Arm mean minus control mean, at this look."
                },
                "upper_bound": {
                  "type": "number",
                  "description": "One-sided upper bound on the difference at `level`, from a learner bootstrap."
                },
                "triggered": {
                  "type": "boolean",
                  "description": "Whether the upper bound lies below minus the tolerance."
                },
                "seed": {
                  "type": "string"
                }
              },
              "required": [
                "arm",
                "versus",
                "with_outcome",
                "control_with_outcome",
                "difference",
                "upper_bound",
                "triggered",
                "seed"
              ]
            }
          },
          "triggered": {
            "type": "boolean"
          }
        },
        "required": [
          "look",
          "required_per_arm",
          "level",
          "resamples",
          "taken_at",
          "arms",
          "triggered"
        ]
      },
      "ReleaseCheck": {
        "type": "object",
        "properties": {
          "experiment_id": {
            "type": "string"
          },
          "versus": {
            "type": "string",
            "description": "The comparison group every version is measured against."
          },
          "versions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReleaseVersionCheck"
            },
            "description": "One decision for each version other than the comparison group, in the declared order."
          },
          "margin": {
            "type": [
              "number",
              "null"
            ],
            "description": "The experiment's declared `minimum_meaningful_effect`: the largest shortfall on the main result that does not matter. Null when none was declared."
          },
          "mode": {
            "type": "string",
            "enum": [
              "advisory"
            ],
            "description": "A decision for a release process to read. OutFigure holds back or releases nothing itself."
          },
          "rule_version": {
            "type": "string",
            "enum": [
              "release-gate-v1"
            ]
          },
          "validation_status": {
            "type": "string",
            "enum": [
              "unvalidated"
            ],
            "description": "The rule is fixed and documented, but no study has shown its decisions lead to better releases."
          },
          "evidence": {
            "type": "object",
            "properties": {
              "computed_at": {
                "type": "string",
                "description": "UTC instant in ISO 8601 format with millisecond precision.",
                "example": "2026-09-13T09:00:00.000Z"
              },
              "config_hash": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "analyzer": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "version": {
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "version"
                ]
              },
              "causal_strength": {
                "$ref": "#/components/schemas/CausalStrength"
              },
              "freshness": {
                "type": "string",
                "enum": [
                  "current",
                  "updating"
                ]
              }
            },
            "required": [
              "computed_at",
              "config_hash",
              "analyzer",
              "causal_strength",
              "freshness"
            ]
          },
          "input_hash": {
            "type": "string",
            "description": "SHA-256 of the canonical inputs the rule read (its version, the declaration it used and the result), so the same decision can be worked out again."
          }
        },
        "required": [
          "experiment_id",
          "versus",
          "versions",
          "margin",
          "mode",
          "rule_version",
          "validation_status",
          "evidence",
          "input_hash"
        ]
      },
      "ReleaseVersionCheck": {
        "type": "object",
        "properties": {
          "arm": {
            "type": "string",
            "description": "The version this decision is about."
          },
          "decision": {
            "type": "string",
            "enum": [
              "ALLOW",
              "ALLOW_WITH_CONSTRAINTS",
              "DEFER",
              "REVIEW",
              "DENY"
            ],
            "description": "`ALLOW`: ahead on the main result. `ALLOW_WITH_CONSTRAINTS`: no clear difference, any shortfall within the declared `minimum_meaningful_effect`. `DEFER`: the result cannot decide yet. `REVIEW`: the experiment itself needs a person to look first. `DENY`: behind on the main result or a safety check."
          },
          "reason_codes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "EXPERIMENT_NOT_STARTED",
                "SAFETY_CHECK_TRIGGERED",
                "SPLIT_BROKEN",
                "UNEVEN_MISSING_RESULTS",
                "UNEVEN_TEACHING",
                "CHECKS_STILL_TO_COME",
                "TOO_FEW_LEARNERS",
                "RESULT_BEING_UPDATED",
                "NOT_ESTIMATED",
                "AHEAD_ON_MAIN_RESULT",
                "BEHIND_ON_MAIN_RESULT",
                "NO_WORSE_THAN_MARGIN",
                "NO_CLEAR_DIFFERENCE",
                "NO_MARGIN_DECLARED"
              ]
            },
            "minItems": 1,
            "description": "The reason that decided, first, then every other problem the rule found, so nothing wrong is hidden behind the first."
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "enum": [
                    "BEHIND_ON_OTHER_RESULT",
                    "AHEAD_ON_OTHER_RESULT"
                  ]
                },
                "role": {
                  "$ref": "#/components/schemas/EvidenceRole"
                }
              },
              "required": [
                "code",
                "role"
              ]
            },
            "description": "Other results ahead or behind. They never change the decision."
          },
          "constraints": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "KEEP_SAFETY_CHECKS"
              ]
            }
          },
          "main_result": {
            "type": "object",
            "properties": {
              "role": {
                "$ref": "#/components/schemas/EvidenceRole"
              },
              "measurement_depth": {
                "$ref": "#/components/schemas/MeasurementDepth"
              },
              "difference": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "interval": {
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "kind": {
                        "type": "string",
                        "enum": [
                          "bootstrap_percentile"
                        ]
                      },
                      "level": {
                        "type": "number",
                        "enum": [
                          0.95
                        ]
                      },
                      "lower": {
                        "type": "number"
                      },
                      "upper": {
                        "type": "number"
                      },
                      "resamples": {
                        "type": "integer"
                      },
                      "seed": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "kind",
                      "level",
                      "lower",
                      "upper",
                      "resamples",
                      "seed"
                    ]
                  },
                  {
                    "type": "object",
                    "properties": {
                      "kind": {
                        "type": "string",
                        "enum": [
                          "not_estimated"
                        ]
                      },
                      "reason": {
                        "type": "string",
                        "enum": [
                          "insufficient_sample",
                          "immature"
                        ]
                      }
                    },
                    "required": [
                      "kind",
                      "reason"
                    ]
                  }
                ]
              }
            },
            "required": [
              "role",
              "measurement_depth",
              "difference",
              "interval"
            ]
          }
        },
        "required": [
          "arm",
          "decision",
          "reason_codes",
          "notes",
          "constraints",
          "main_result"
        ]
      },
      "ExperimentReport": {
        "type": "object",
        "properties": {
          "report_version": {
            "type": "string",
            "enum": [
              "experiment-report-v1",
              "experiment-report-v2"
            ],
            "description": "The report format. `experiment-report-v2` adds the Guardrails section; this build writes it."
          },
          "experiment_id": {
            "type": "string"
          },
          "label": {
            "$ref": "#/components/schemas/ReportLabel"
          },
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "live"
            ],
            "description": "A sandbox project holds test data, and its report says so."
          },
          "as_of": {
            "type": "string",
            "description": "When the result was computed. The report is built from that result alone.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "experiment": {
            "$ref": "#/components/schemas/Experiment"
          },
          "result": {
            "$ref": "#/components/schemas/ExperimentResult"
          },
          "assessment": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "role": {
                  "$ref": "#/components/schemas/EvidenceRole"
                },
                "plans": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "min_seconds": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "max_seconds": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "assistance": {
                        "$ref": "#/components/schemas/AssistanceRule"
                      },
                      "min_observations": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "contracts": {
                        "type": "integer",
                        "minimum": 0
                      }
                    },
                    "required": [
                      "min_seconds",
                      "max_seconds",
                      "assistance",
                      "min_observations",
                      "contracts"
                    ]
                  }
                }
              },
              "required": [
                "role",
                "plans"
              ]
            },
            "description": "What was measured for each compared outcome: the evidence plans of the contracts of learners in their assigned arm, with how many contracts used each. Plans are fixed when a contract opens."
          },
          "readings": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "role": {
                  "$ref": "#/components/schemas/EvidenceRole"
                },
                "arm": {
                  "type": "string"
                },
                "versus": {
                  "type": "string"
                },
                "interval": {
                  "$ref": "#/components/schemas/IntervalReading"
                },
                "minimum_effect": {
                  "$ref": "#/components/schemas/MinimumEffectReading"
                }
              },
              "required": [
                "role",
                "arm",
                "versus",
                "interval",
                "minimum_effect"
              ]
            }
          },
          "divergences": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "arm": {
                  "type": "string"
                },
                "versus": {
                  "type": "string"
                },
                "earlier_role": {
                  "$ref": "#/components/schemas/EvidenceRole"
                },
                "earlier": {
                  "$ref": "#/components/schemas/IntervalReading"
                },
                "later_role": {
                  "$ref": "#/components/schemas/EvidenceRole"
                },
                "later": {
                  "$ref": "#/components/schemas/IntervalReading"
                }
              },
              "required": [
                "arm",
                "versus",
                "earlier_role",
                "earlier",
                "later_role",
                "later"
              ]
            },
            "description": "Arms whose difference reads one way straight after the intervention and another way at least a day later, both estimated. Each is two readings side by side, not a conclusion."
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "markdown": {
            "type": "string",
            "description": "The report as a Markdown document, built from the fields above. Text the partner supplied is escaped."
          }
        },
        "required": [
          "report_version",
          "experiment_id",
          "label",
          "environment",
          "as_of",
          "experiment",
          "result",
          "assessment",
          "readings",
          "divergences",
          "limitations",
          "markdown"
        ]
      },
      "ReportLabel": {
        "type": "string",
        "enum": [
          "randomized_experiment",
          "observational_association",
          "descriptive_analysis"
        ],
        "description": "`randomized_experiment`: chance decided the arm and a difference was estimated. `observational_association`: a difference was estimated, but the sample-ratio check failed, so it is read as an association. `descriptive_analysis`: no difference is estimated yet; the report counts what was recorded."
      },
      "IntervalReading": {
        "type": "string",
        "enum": [
          "above_zero",
          "below_zero",
          "includes_zero",
          "not_estimated"
        ]
      },
      "MinimumEffectReading": {
        "type": "string",
        "enum": [
          "at_or_above_minimum",
          "at_or_below_negative_minimum",
          "within_minimum",
          "inconclusive",
          "no_minimum_declared",
          "not_estimated"
        ],
        "description": "Against the minimum meaningful effect declared before the start, m. `at_or_above_minimum`: the whole interval is at or above +m. `at_or_below_negative_minimum`: at or below -m. `within_minimum`: strictly between -m and +m. `inconclusive`: the interval reaches both inside and outside that band."
      },
      "OutcomeSummary": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "enum": [
              "outcome-summary-v1"
            ]
          },
          "design_status": {
            "type": "string",
            "enum": [
              "descriptive"
            ]
          },
          "as_of": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "window_days": {
            "type": "integer"
          },
          "volume": {
            "type": "object",
            "properties": {
              "interventions": {
                "type": "integer",
                "minimum": 0
              },
              "evidence": {
                "type": "integer",
                "minimum": 0
              },
              "states": {
                "type": "integer",
                "minimum": 0
              }
            },
            "required": [
              "interventions",
              "evidence",
              "states"
            ]
          },
          "contracts": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer",
                "minimum": 0
              },
              "open": {
                "type": "integer",
                "minimum": 0
              },
              "closed": {
                "type": "integer",
                "minimum": 0
              }
            },
            "required": [
              "total",
              "open",
              "closed"
            ]
          },
          "coverage": {
            "type": "object",
            "properties": {
              "outcome_linked": {
                "type": "object",
                "properties": {
                  "numerator": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "denominator": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "rate": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "Null when the denominator is zero."
                  }
                },
                "required": [
                  "numerator",
                  "denominator",
                  "rate"
                ],
                "description": "Closed contracts whose latest receipt reached MD2 or deeper, over closed contracts: interventions followed by usable direct evidence."
              },
              "durable": {
                "type": "object",
                "properties": {
                  "numerator": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "denominator": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "rate": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "Null when the denominator is zero."
                  }
                },
                "required": [
                  "numerator",
                  "denominator",
                  "rate"
                ],
                "description": "Closed contracts whose latest receipt reached MD3 or MD4, over closed contracts."
              }
            },
            "required": [
              "outcome_linked",
              "durable"
            ]
          },
          "measurement_depth": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "depth": {
                  "$ref": "#/components/schemas/MeasurementDepth"
                },
                "receipts": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "required": [
                "depth",
                "receipts"
              ]
            }
          },
          "causal_strength": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "strength": {
                  "type": "string",
                  "enum": [
                    "CS0",
                    "CS1"
                  ]
                },
                "receipts": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "required": [
                "strength",
                "receipts"
              ]
            }
          },
          "immediate": {
            "type": "object",
            "properties": {
              "mean": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "n": {
                "type": "integer",
                "minimum": 0,
                "description": "Receipts with this metric."
              }
            },
            "required": [
              "mean",
              "n"
            ],
            "description": "Mean immediate metric across latest receipts that have one."
          },
          "delayed": {
            "type": "object",
            "properties": {
              "mean": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "n": {
                "type": "integer",
                "minimum": 0,
                "description": "Receipts with this metric."
              }
            },
            "required": [
              "mean",
              "n"
            ],
            "description": "Mean delayed metric across latest receipts that have one."
          },
          "by_intervention": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "class": {
                  "$ref": "#/components/schemas/InterventionClass"
                },
                "prompt_policy_version": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "contracts": {
                  "type": "integer",
                  "minimum": 0
                },
                "immediate": {
                  "$ref": "#/components/schemas/SummaryMean"
                },
                "delayed": {
                  "$ref": "#/components/schemas/SummaryMean"
                }
              },
              "required": [
                "class",
                "prompt_policy_version",
                "contracts",
                "immediate",
                "delayed"
              ]
            },
            "description": "The same figures grouped by intervention class and policy release. A difference here is an observational association: a randomized experiment is what can compare them."
          }
        },
        "required": [
          "version",
          "design_status",
          "as_of",
          "window_days",
          "volume",
          "contracts",
          "coverage",
          "measurement_depth",
          "causal_strength",
          "immediate",
          "delayed",
          "by_intervention"
        ]
      },
      "SummaryMean": {
        "type": "object",
        "properties": {
          "mean": {
            "type": [
              "number",
              "null"
            ]
          },
          "n": {
            "type": "integer",
            "minimum": 0,
            "description": "Receipts with this metric."
          }
        },
        "required": [
          "mean",
          "n"
        ]
      },
      "CohortReport": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "enum": [
              "cohorts-v1"
            ]
          },
          "by": {
            "$ref": "#/components/schemas/CohortGrouping"
          },
          "design_status": {
            "type": "string",
            "enum": [
              "observational_association"
            ]
          },
          "causal_strength": {
            "type": "string",
            "enum": [
              "CS1"
            ]
          },
          "as_of": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "groups": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CohortGroup"
            }
          },
          "groups_total": {
            "type": "integer",
            "minimum": 0
          },
          "truncated": {
            "type": "boolean",
            "description": "True when there are more than 50 groups; the largest by contracts are listed."
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "version",
          "by",
          "design_status",
          "causal_strength",
          "as_of",
          "groups",
          "groups_total",
          "truncated",
          "limitations"
        ]
      },
      "CohortGrouping": {
        "type": "string",
        "enum": [
          "class",
          "release",
          "baseline"
        ],
        "description": "`class`: by intervention class. `release`: by class and policy release. `baseline`: by class and the band of the receipt's baseline score."
      },
      "CohortGroup": {
        "type": "object",
        "properties": {
          "class": {
            "$ref": "#/components/schemas/InterventionClass"
          },
          "prompt_policy_version": {
            "type": [
              "string",
              "null"
            ],
            "description": "The policy release, when grouped by release. Null otherwise, or when none was sent."
          },
          "baseline_band": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "low",
              "middle",
              "high",
              "none",
              null
            ],
            "description": "The baseline band, when grouped by baseline. Null otherwise."
          },
          "contracts": {
            "type": "integer",
            "minimum": 0,
            "description": "Closed contracts in the group."
          },
          "learners": {
            "type": "integer",
            "minimum": 0
          },
          "immediate": {
            "$ref": "#/components/schemas/CohortEstimate"
          },
          "delayed": {
            "$ref": "#/components/schemas/CohortEstimate"
          },
          "delayed_followed": {
            "type": "object",
            "properties": {
              "numerator": {
                "type": "integer",
                "minimum": 0
              },
              "denominator": {
                "type": "integer",
                "minimum": 0
              },
              "rate": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Null when the denominator is zero."
              }
            },
            "required": [
              "numerator",
              "denominator",
              "rate"
            ],
            "description": "Closed contracts whose plan asked for a result a day or more later and got one, over those whose plan asked. Missing is coverage, never failure."
          }
        },
        "required": [
          "class",
          "prompt_policy_version",
          "baseline_band",
          "contracts",
          "learners",
          "immediate",
          "delayed",
          "delayed_followed"
        ]
      },
      "CohortEstimate": {
        "type": "object",
        "properties": {
          "learners": {
            "type": "integer",
            "minimum": 0,
            "description": "Learners with a result here."
          },
          "receipts": {
            "type": "integer",
            "minimum": 0,
            "description": "Receipts with a result here."
          },
          "mean": {
            "type": [
              "number",
              "null"
            ],
            "description": "Mean over learners of each learner's mean score here, 0 to 1. Null without any."
          },
          "interval": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "t_over_learners"
                    ]
                  },
                  "level": {
                    "type": "number",
                    "enum": [
                      0.95
                    ]
                  },
                  "lower": {
                    "type": "number"
                  },
                  "upper": {
                    "type": "number"
                  },
                  "clipped": {
                    "type": "boolean",
                    "description": "Whether an end was clipped to the 0 to 1 scale."
                  }
                },
                "required": [
                  "kind",
                  "level",
                  "lower",
                  "upper",
                  "clipped"
                ]
              },
              {
                "type": "object",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "not_estimated"
                    ]
                  },
                  "reason": {
                    "type": "string",
                    "enum": [
                      "no_observations",
                      "insufficient_sample"
                    ]
                  }
                },
                "required": [
                  "kind",
                  "reason"
                ]
              }
            ]
          }
        },
        "required": [
          "learners",
          "receipts",
          "mean",
          "interval"
        ]
      },
      "CapabilityList": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "enum": [
              "capabilities-v1"
            ]
          },
          "design_status": {
            "type": "string",
            "enum": [
              "descriptive"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CapabilitySummary"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "version",
          "design_status",
          "data",
          "next_cursor"
        ]
      },
      "CapabilitySummary": {
        "type": "object",
        "properties": {
          "capability": {
            "type": "string"
          },
          "label": {
            "type": [
              "string",
              "null"
            ],
            "description": "The label the catalog gives it; null when it is not registered there."
          },
          "registered": {
            "type": "boolean",
            "description": "Whether the catalog registers it. An unregistered capability is still counted, and the data-quality report flags it."
          },
          "interventions": {
            "type": "integer",
            "minimum": 0,
            "description": "Interventions recorded on it, all time."
          },
          "evidence": {
            "type": "integer",
            "minimum": 0,
            "description": "Evidence recorded on it, all time."
          },
          "contracts": {
            "type": "object",
            "properties": {
              "open": {
                "type": "integer",
                "minimum": 0
              },
              "closed": {
                "type": "integer",
                "minimum": 0
              }
            },
            "required": [
              "open",
              "closed"
            ]
          },
          "coverage": {
            "type": "object",
            "properties": {
              "outcome_linked": {
                "type": "object",
                "properties": {
                  "numerator": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "denominator": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "rate": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "Null when the denominator is zero."
                  }
                },
                "required": [
                  "numerator",
                  "denominator",
                  "rate"
                ],
                "description": "Closed contracts on this capability whose latest receipt reached MD2 or deeper, over its closed contracts."
              },
              "durable": {
                "type": "object",
                "properties": {
                  "numerator": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "denominator": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "rate": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "Null when the denominator is zero."
                  }
                },
                "required": [
                  "numerator",
                  "denominator",
                  "rate"
                ],
                "description": "Closed contracts on this capability whose latest receipt reached MD3 or MD4, over its closed contracts."
              }
            },
            "required": [
              "outcome_linked",
              "durable"
            ]
          },
          "last_delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "last_observed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "capability",
          "label",
          "registered",
          "interventions",
          "evidence",
          "contracts",
          "coverage",
          "last_delivered_at",
          "last_observed_at"
        ]
      },
      "DataQualityReport": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "enum": [
              "data-quality-v1"
            ]
          },
          "as_of": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "window_days": {
            "type": "integer"
          },
          "checks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DataQualityCheck"
            }
          }
        },
        "required": [
          "version",
          "as_of",
          "window_days",
          "checks"
        ]
      },
      "DataQualityCheck": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "duplicate_submissions",
              "late_arrival",
              "unregistered_capability",
              "assistance_contamination",
              "missing_baseline",
              "no_followup",
              "followup_not_mature",
              "item_reuse_blocks_transfer",
              "exposure_without_assignment",
              "sample_ratio_mismatch",
              "differential_missingness"
            ]
          },
          "title": {
            "type": "string"
          },
          "definition": {
            "type": "string",
            "description": "What is counted, in words."
          },
          "numerator": {
            "type": "integer",
            "minimum": 0
          },
          "denominator": {
            "type": "integer",
            "minimum": 0
          },
          "rate": {
            "type": [
              "number",
              "null"
            ],
            "description": "Numerator over denominator. Null with nothing to count."
          },
          "window_days": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The window the records were drawn from. Null when the check reads current state."
          },
          "thresholds": {
            "type": "object",
            "properties": {
              "warning": {
                "type": "number"
              },
              "critical": {
                "type": "number"
              }
            },
            "required": [
              "warning",
              "critical"
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "ok",
              "warning",
              "critical",
              "not_applicable"
            ]
          }
        },
        "required": [
          "code",
          "title",
          "definition",
          "numerator",
          "denominator",
          "rate",
          "window_days",
          "thresholds",
          "state"
        ]
      },
      "WebhookSecretIssued": {
        "type": "object",
        "properties": {
          "endpoint": {
            "$ref": "#/components/schemas/WebhookEndpoint"
          },
          "secret": {
            "type": [
              "string",
              "null"
            ],
            "description": "Shown once. Store it on your server; OutFigure cannot show it again. The signing key is this string as UTF-8. Null when this response replays an earlier request with the same Idempotency-Key: the secret is never stored where a replay could read it, so rotate it if the first response was lost.",
            "example": "whsec_3q2-7wAbCdEfGhIjKlMnOpQrStUvWxYz0123456789_-A"
          }
        },
        "required": [
          "endpoint",
          "secret"
        ]
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "endpoint_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "url": {
            "type": "string"
          },
          "event_types": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled"
            ]
          },
          "secret_created_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "previous_secret_valid_until": {
            "type": [
              "string",
              "null"
            ],
            "description": "After a rotation, notices carry a signature from the previous secret until then.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "created_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "disabled_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "endpoint_id",
          "url",
          "event_types",
          "description",
          "status",
          "secret_created_at",
          "previous_secret_valid_until",
          "created_at",
          "disabled_at"
        ]
      },
      "WebhookEventType": {
        "type": "string",
        "enum": [
          "outcome.receipt.created",
          "outcome.receipt.updated",
          "outcome.contract.expired",
          "experiment.data_ready",
          "experiment.sample_ratio_mismatch",
          "experiment.guardrail_triggered",
          "experiment.release_advice_changed",
          "data_quality.degraded"
        ],
        "description": "`outcome.receipt.created`: the first receipt for a contract. `outcome.receipt.updated`: a later version superseded it. `outcome.contract.expired`: every window and the late-arrival grace have passed, so the latest receipt is final unless evidence is corrected. `experiment.data_ready`: an experiment's stored result has become ready to read: every exposed learner's window has closed and each arm has enough outcomes, so its differences are estimated. `experiment.sample_ratio_mismatch`: a stored result's assignment counts have started to depart from the declared allocation, so its comparison is read as an association until the mismatch is explained. `experiment.guardrail_triggered`: a guardrail look found an arm below control by more than the guardrail's tolerance; sent once per guardrail. `experiment.release_advice_changed`: a newly stored result changes the release check's advice for at least one version (ADR 0064); before the first stored result every version counts as held back. `data_quality.degraded`: a data-quality check, run in the drain at most every ten minutes after records arrive, found at least one check in a worse state than the check before. Each is sent when a newly stored result first says so, not on every recomputation."
      },
      "WebhookEndpointCreateRequest": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "maxLength": 2048,
            "description": "Where notices are sent. `https` only; an address that resolves to a private, loopback, link-local or metadata address is refused, and redirects are never followed.",
            "example": "https://api.example.com/outfigure/webhooks"
          },
          "event_types": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "minItems": 1,
            "maxItems": 8
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          }
        },
        "required": [
          "url",
          "event_types"
        ],
        "additionalProperties": false
      },
      "WebhookEndpointList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEndpoint"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null when there are no more results."
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "WebhookDeliveryList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDelivery"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null when there are no more results."
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "delivery_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "endpoint_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "event_type": {
            "$ref": "#/components/schemas/WebhookEventType"
          },
          "notice": {
            "$ref": "#/components/schemas/WebhookNotice"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivered",
              "failed"
            ],
            "description": "`pending`: waiting for its next attempt. `delivered`: the endpoint answered 2xx. `failed`: every attempt failed; replay it after fixing the endpoint."
          },
          "attempts": {
            "type": "integer",
            "minimum": 0
          },
          "next_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "last_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "last_status_code": {
            "type": [
              "integer",
              "null"
            ]
          },
          "last_error_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the last attempt failed without a 2xx answer: `http_status`, `timeout`, `connection_failed`, `destination_not_allowed` or `redirect_not_followed`."
          },
          "created_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "delivery_id",
          "endpoint_id",
          "event_type",
          "notice",
          "status",
          "attempts",
          "next_attempt_at",
          "last_attempt_at",
          "last_status_code",
          "last_error_code",
          "created_at",
          "delivered_at"
        ]
      },
      "OutcomeImport": {
        "type": "object",
        "properties": {
          "import_id": {
            "type": "string",
            "format": "uuid",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "record": {
            "type": "string",
            "enum": [
              "interventions",
              "evidence",
              "events"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "completed",
              "failed"
            ],
            "description": "`pending` and `processing` while rows are read; `completed` once every row is accounted for; `failed` when the import could not continue, with `failure_code`. The file is removed as soon as it is completed or failed."
          },
          "source_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "rows": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer"
              },
              "processed": {
                "type": "integer"
              },
              "recorded": {
                "type": "integer",
                "description": "New records stored."
              },
              "duplicate": {
                "type": "integer",
                "description": "Rows identical to a record already stored, or to an earlier row: stored once."
              },
              "invalid": {
                "type": "integer",
                "description": "Rows that were not recorded; see the errors."
              }
            },
            "required": [
              "total",
              "processed",
              "recorded",
              "duplicate",
              "invalid"
            ]
          },
          "errors_total": {
            "type": "integer"
          },
          "failure_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "UTC instant in ISO 8601 format with millisecond precision.",
            "example": "2026-09-13T09:00:00.000Z"
          }
        },
        "required": [
          "import_id",
          "record",
          "status",
          "source_name",
          "rows",
          "errors_total",
          "failure_code",
          "created_at",
          "completed_at"
        ]
      },
      "ImportCreateRequest": {
        "type": "object",
        "properties": {
          "record": {
            "type": "string",
            "enum": [
              "interventions",
              "evidence",
              "events"
            ],
            "description": "Which records the rows hold. Import evidence before the interventions it follows, so each contract is first evaluated with its evidence. `events` are learning events in the canonical event contract; register the catalog first so responses reach their concepts."
          },
          "mapping": {
            "type": "object",
            "additionalProperties": {},
            "description": "How the columns become record fields: the CSV mapping format of the Outcome Audit and the readiness report, with `record` matching the import (docs/guides/outcome-audit.md). A mapping without `record` reads events."
          },
          "csv": {
            "type": "string",
            "minLength": 1,
            "description": "The file as text, header first, at most 2097152 bytes of UTF-8."
          },
          "source_name": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 200,
            "description": "A name for the file, shown with the import.",
            "example": "tutor_logs_2026_08.csv"
          }
        },
        "required": [
          "record",
          "mapping",
          "csv"
        ],
        "additionalProperties": false
      },
      "OutcomeImportList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutcomeImport"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null when there are no more results."
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      }
    },
    "parameters": {}
  },
  "paths": {
    "/health/live": {
      "get": {
        "summary": "Process liveness",
        "description": "Reports only that the process can respond. No infrastructure details.",
        "responses": {
          "200": {
            "description": "Alive",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok"
                      ]
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/events": {
      "post": {
        "summary": "Send one learner event",
        "description": "Stores one event durably and schedules processing. 202 means stored, not yet reflected in state. Exact duplicates are acknowledged and not counted again. Requires scope `events:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "schema_version": "1.0",
                "event_id": "evt_demo_0001",
                "event_type": "response",
                "learner_id": "learner_demo_01",
                "occurred_at": "2026-09-13T09:00:00Z",
                "source": "partner_backend",
                "course_id": "cloud_foundations",
                "session_id": "session_demo_01",
                "item_id": "question_012",
                "item_version": "1",
                "attempt_id": "attempt_demo_01",
                "attempt_number": 1,
                "response_kind": "binary",
                "correct": true,
                "latency_ms": 18400,
                "hint_count": 0,
                "assistance": "none",
                "confidence": null,
                "feedback_seen": false
              },
              "schema": {
                "$ref": "#/components/schemas/LearningEvent"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Stored. Poll the returned jobs for processing status.",
            "content": {
              "application/json": {
                "example": {
                  "receipt_id": "11111111-1111-4111-8111-111111111111",
                  "accepted_count": 1,
                  "duplicate_count": 0,
                  "job_ids": [
                    "22222222-2222-4222-8222-222222222222"
                  ],
                  "processing_status": "pending",
                  "request_id": "req_demo_01"
                },
                "schema": {
                  "$ref": "#/components/schemas/IngestionReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events/batch": {
      "post": {
        "summary": "Send up to 25 events atomically",
        "description": "The whole batch is validated before anything is stored. Any invalid event rejects the batch with indexed details; duplicate event IDs within one batch are rejected. Requires scope `events:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "events": [
                  {
                    "schema_version": "1.0",
                    "event_id": "evt_demo_0001",
                    "event_type": "response",
                    "learner_id": "learner_demo_01",
                    "occurred_at": "2026-09-13T09:00:00Z",
                    "source": "partner_backend",
                    "course_id": "cloud_foundations",
                    "session_id": "session_demo_01",
                    "item_id": "question_012",
                    "item_version": "1",
                    "attempt_id": "attempt_demo_01",
                    "attempt_number": 1,
                    "response_kind": "binary",
                    "correct": true,
                    "latency_ms": 18400,
                    "hint_count": 0,
                    "assistance": "none",
                    "confidence": null,
                    "feedback_seen": false
                  },
                  {
                    "schema_version": "1.0",
                    "event_id": "evt_demo_0002",
                    "event_type": "hint",
                    "learner_id": "learner_demo_01",
                    "occurred_at": "2026-09-13T09:02:10Z",
                    "source": "partner_backend",
                    "session_id": "session_demo_01",
                    "item_id": "question_013",
                    "item_version": "1",
                    "attempt_id": "attempt_demo_02"
                  }
                ]
              },
              "schema": {
                "type": "object",
                "properties": {
                  "events": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/LearningEvent"
                    },
                    "minItems": 1,
                    "maxItems": 25
                  }
                },
                "required": [
                  "events"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Stored atomically.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IngestionReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events/corrections": {
      "post": {
        "summary": "Correct a stored event",
        "description": "Stores a complete replacement for an event already stored, linked to it, and rebuilds the affected states. The replacement has a new event_id and keeps the learner, event type, item and item version of the event it replaces; nothing else is inherited. The replaced event stays in the evidence record, marked `superseded`, and no longer counts. Predictions keep the evidence they froze, and an outcome already recorded against the replaced event is kept, not rewritten. Only the latest version of an event can be corrected: correcting one that already has a correction returns 409. Requires scope `events:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "supersedes_event_id": "evt_demo_0001",
                "replacement": {
                  "schema_version": "1.0",
                  "event_id": "evt_demo_0001_corrected",
                  "event_type": "response",
                  "learner_id": "learner_demo_01",
                  "occurred_at": "2026-09-13T09:00:00Z",
                  "source": "partner_backend",
                  "course_id": "cloud_foundations",
                  "session_id": "session_demo_01",
                  "item_id": "question_012",
                  "item_version": "1",
                  "attempt_id": "attempt_demo_01",
                  "attempt_number": 1,
                  "response_kind": "binary",
                  "correct": false,
                  "latency_ms": 18400,
                  "hint_count": 0,
                  "assistance": "none",
                  "confidence": null,
                  "feedback_seen": false
                }
              },
              "schema": {
                "$ref": "#/components/schemas/CorrectionRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Stored. Poll the returned jobs for processing status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IngestionReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/experiments/assignments": {
      "post": {
        "summary": "Record experiment assignments",
        "description": "Records which experiment and arm learners were assigned to, when and by which method, and what was intended. Assignments are kept apart from intervention events, which record what was actually delivered, and they change no state. One assignment per learner per experiment: the same content again is acknowledged as a duplicate, and a different arm or method returns 409. Up to 100 per request, atomic. Results come back in request order without learner IDs. Requires scope `experiments:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "assignments": [
                  {
                    "learner_id": "learner_demo_01",
                    "experiment_id": "exp_worked_examples",
                    "arm": "treatment",
                    "assigned_at": "2026-09-13T08:55:00Z",
                    "assignment_method": "randomized",
                    "intended_intervention_id": "worked_example_set_3",
                    "analysis_version": "analysis-plan-1",
                    "source": "experiment_service"
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/AssignmentBatch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Every assignment was already recorded with identical content.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssignmentReceipt"
                }
              }
            }
          },
          "201": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssignmentReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/learners/{learner_id}/assignments": {
      "get": {
        "summary": "A learner's experiment assignments",
        "description": "Assignments recorded for this learner, newest first. They describe intended treatment only; delivered teaching is in the evidence route as intervention events. Requires scope `evidence:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "External learner ID, URL-encoded.",
              "example": "learner_demo_01"
            },
            "required": true,
            "description": "External learner ID, URL-encoded.",
            "name": "learner_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "description": "Default 50, maximum 100."
            },
            "required": false,
            "description": "Default 50, maximum 100.",
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Assignments",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssignmentList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/judgment/sessions": {
      "post": {
        "summary": "Open a judgment session",
        "description": "Opens a scoring session beside an interview you already run, and returns the items the candidate is shown. The planted flaw in an item, and the rubric that judges it, are never part of the response. A candidate identifier is opaque: no name or address is accepted. Requires scope `judgment:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "candidate_id": "cand_7f3a",
                "domain": "python_backend",
                "role": "Senior backend engineer",
                "external_session_ref": "interview_4821",
                "source_tool": "existing_interview_tool"
              },
              "schema": {
                "$ref": "#/components/schemas/SessionCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The same request was already accepted."
          },
          "201": {
            "description": "Session opened, with the items to put to the candidate.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "session_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "candidate_id": {
                      "type": "string"
                    },
                    "domain": {
                      "type": "string"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "item_id": {
                            "type": "string"
                          },
                          "task_type": {
                            "type": "string"
                          },
                          "difficulty": {
                            "type": "integer"
                          },
                          "prompt": {
                            "type": "string"
                          },
                          "artifact": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "constraint_shift": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "challenge": {
                            "type": [
                              "string",
                              "null"
                            ]
                          }
                        },
                        "required": [
                          "item_id",
                          "task_type",
                          "difficulty",
                          "prompt",
                          "artifact",
                          "constraint_shift",
                          "challenge"
                        ]
                      }
                    }
                  },
                  "required": [
                    "session_id",
                    "candidate_id",
                    "domain",
                    "items"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/judgment/sessions/{session_id}/transcript": {
      "post": {
        "summary": "Add answers to a session",
        "description": "Adds the answers from the interview you ran. One answer per item: the same transcript sent twice is one answer, not two. A session whose profile has been issued is closed, and a later answer is refused rather than folded into a reading that is already frozen. Requires scope `judgment:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "session_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TranscriptIngestRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The same request was already accepted."
          },
          "202": {
            "description": "Stored.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "session_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "accepted": {
                      "type": "integer"
                    },
                    "duplicates": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "session_id",
                    "accepted",
                    "duplicates"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/judgment/sessions/{session_id}/profile": {
      "post": {
        "summary": "Issue the judgment profile",
        "description": "Reads the session once, against the rubric each item was written with, and freezes the result with its evidence cutoff, rubric version and scorer version. Asking again returns the profile that exists rather than computing a new one, because a reading that can be recomputed after an outcome is known is not a prediction. Requires scope `judgment:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "session_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "responses": {
          "200": {
            "description": "The profile was already issued and is returned unchanged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JudgmentProfile"
                }
              }
            }
          },
          "201": {
            "description": "Profile issued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JudgmentProfile"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "Read a judgment profile",
        "description": "The frozen profile and the verdicts behind it: for each answer, what it was judged to show and the rubric line that decided it. Every reading points at these, so no figure exists that cannot be traced to an answer. Requires scope `judgment:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "session_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Profile with its evidence.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "profile_id": {
                      "type": "string",
                      "format": "uuid",
                      "example": "11111111-1111-4111-8111-111111111111"
                    },
                    "session_id": {
                      "type": "string",
                      "format": "uuid",
                      "example": "11111111-1111-4111-8111-111111111111"
                    },
                    "candidate_id": {
                      "type": "string",
                      "description": "Opaque, case-sensitive identifier chosen by the integrating platform. 1–128 UTF-8 bytes, no control characters. Never a name or email address.",
                      "example": "learner_demo_01"
                    },
                    "issued_at": {
                      "type": "string",
                      "description": "UTC instant in ISO 8601 format with millisecond precision.",
                      "example": "2026-09-13T09:00:00.000Z"
                    },
                    "evidence_cutoff": {
                      "type": "string",
                      "description": "UTC instant in ISO 8601 format with millisecond precision.",
                      "example": "2026-09-13T09:00:00.000Z"
                    },
                    "rubric_version": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 60
                    },
                    "scorer_version": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 60
                    },
                    "judge_model": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "minLength": 1,
                      "maxLength": 120
                    },
                    "judge_prompt_version": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "minLength": 1,
                      "maxLength": 60
                    },
                    "readings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DimensionReading"
                      }
                    },
                    "responses_scored": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "design_status": {
                      "type": "string",
                      "enum": [
                        "descriptive"
                      ],
                      "description": "Counts of what was recorded. It explains nothing and predicts nothing."
                    },
                    "evidence": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ResponseVerdict"
                      }
                    }
                  },
                  "required": [
                    "profile_id",
                    "session_id",
                    "candidate_id",
                    "issued_at",
                    "evidence_cutoff",
                    "rubric_version",
                    "scorer_version",
                    "judge_model",
                    "judge_prompt_version",
                    "readings",
                    "responses_scored",
                    "design_status",
                    "evidence"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/judgment/sessions/{session_id}/outcome": {
      "post": {
        "summary": "Record what happened after a profile",
        "description": "Records a panel judgment, a hire decision or a later performance rating against a profile that was issued first. An outcome observed before the profile was frozen is refused, because it cannot test it. One outcome of each kind per profile. Requires scope `judgment:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "session_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "kind": "panel_judgment",
                "value": 0.75,
                "scale": "Mean of three blind reviewers, 0 to 1",
                "observed_at": "2026-09-25T15:00:00Z",
                "source": "panel_a",
                "blind": true
              },
              "schema": {
                "$ref": "#/components/schemas/JudgmentOutcomeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The same request was already accepted."
          },
          "201": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "outcome_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "profile_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "kind": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "outcome_id",
                    "profile_id",
                    "kind"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/judgment/calibration": {
      "get": {
        "summary": "Predicted against observed, for this project",
        "description": "How closely the readings matched what was recorded afterwards, for one outcome kind. Coverage is part of the result, only blind outcomes enter the comparison, and below the minimum sample no agreement number is reported at all. Descriptive: it never states that a reading caused an outcome. Requires scope `judgment:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "panel_judgment",
                "hire_decision",
                "performance_rating"
              ],
              "description": "Defaults to panel_judgment."
            },
            "required": false,
            "description": "Defaults to panel_judgment.",
            "name": "outcome_kind",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Calibration report.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalibrationReport"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/marking-sessions": {
      "post": {
        "summary": "Start a marking session",
        "description": "One paper: its questions, each with a maximum mark and the allowed gap between the marker's mark and the second reading before a script is flagged. The questions are fixed once the session starts. Only a second marker's marks are accepted as the second reading for now; `second_reader_model` is refused with `second_reader_unavailable`. Requires scope `marking:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateMarkingSessionRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkingSession"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "List marking sessions",
        "description": "Newest first, with their counts. Requires scope `marking:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Marking sessions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkingSessionList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/marking-sessions/{session_id}": {
      "get": {
        "summary": "Read a marking session's report",
        "description": "Worked out from the stored marks on every read: flagged scripts with every senior decision, each marker's pattern (`markers-v1`), and agreement between markers and the second reading per question (`agreement-v1`), with counts only below the minimum samples. `sign_off` is null until an owner or admin signs the session off in the console; it then gives who and when, the counts at that moment, the SHA-256 of the final-marks file as it stood, and whether today's file still matches it. Requires scope `marking:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "The marking session, as returned when it was started.",
              "example": "22222222-2222-4222-8222-222222222222"
            },
            "required": true,
            "description": "The marking session, as returned when it was started.",
            "name": "session_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkingSessionReport"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/mark-schemes": {
      "post": {
        "summary": "Add a mark scheme, or a new version of one",
        "description": "A question's mark scheme in the `mark-scheme-v1` shape: the maximum mark and the points it is given for, each with its marks and kind (method, accuracy or independent), a method point it depends on, whether it may be given on the candidate's own earlier figure, and acceptable alternatives. The points must cover the maximum, a dependency must name a method point, and point IDs must be unique; a problem stores nothing and is named in `details`. A scheme is never edited: a changed scheme under the same `scheme_id` becomes the next version (201), and the latest content sent again makes nothing new (200). Mark schemes are the organisation's material, not learner data: erasure and retention leave them, and a learner's export doesn't include them. Requires scope `marking:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarkSchemeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The same as the latest version; nothing new was made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkSchemeVersion"
                }
              }
            }
          },
          "201": {
            "description": "A new version.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkSchemeVersion"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "List mark schemes",
        "description": "The latest version of each, in order of ID. Requires scope `marking:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Mark schemes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkSchemeList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/mark-schemes/{scheme_id}": {
      "get": {
        "summary": "Read a mark scheme's latest version",
        "description": "Requires scope `marking:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "The mark scheme's ID, as you chose it.",
              "example": "q1-deferred-tax"
            },
            "required": true,
            "description": "The mark scheme's ID, as you chose it.",
            "name": "scheme_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The latest version",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkSchemeVersion"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/mark-schemes/{scheme_id}/versions/{version}": {
      "get": {
        "summary": "Read one version of a mark scheme",
        "description": "Every version stays readable, as it was made. Requires scope `marking:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "The mark scheme's ID, as you chose it.",
              "example": "q1-deferred-tax"
            },
            "required": true,
            "description": "The mark scheme's ID, as you chose it.",
            "name": "scheme_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "example": "1"
            },
            "required": true,
            "name": "version",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The version",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkSchemeVersion"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/marking-sessions/{session_id}/questions/{question_id}/mark-scheme": {
      "post": {
        "summary": "Mark a session's question against a mark scheme version",
        "description": "Links a version to the question. Linking again replaces it from then on, and the earlier link stays on record; the session's report shows the current one. The version's maximum mark must match the question's (422 `mark_scheme_max_mark_mismatch`), and a signed-off session refuses (409 `session_signed_off`). Requires scope `marking:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "The marking session, as returned when it was started.",
              "example": "22222222-2222-4222-8222-222222222222"
            },
            "required": true,
            "description": "The marking session, as returned when it was started.",
            "name": "session_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "example": "q1"
            },
            "required": true,
            "name": "question_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkMarkSchemeRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Linked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuestionMarkSchemeLink"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/marking-sessions/{session_id}/scripts": {
      "post": {
        "summary": "Send scripts with a second reading",
        "description": "Up to 1000 scripts a request, 10000 a session. All or nothing: any problem stores nothing, and each problem names the script's `index` and field, never a value. A script already held with exactly the same values counts as `unchanged`; one held with different values is a 409, since marks are kept as first sent. A signed-off session takes no more scripts: 409 `session_signed_off`. Each candidate is a learner of the project: an erased candidate's scripts are refused, and a live project needs its data-use authorization first. Requires scope `marking:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "The marking session, as returned when it was started.",
              "example": "22222222-2222-4222-8222-222222222222"
            },
            "required": true,
            "description": "The marking session, as returned when it was started.",
            "name": "session_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmitScriptsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stored, or already held.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarksUploadResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/marking-sessions/{session_id}/decisions": {
      "post": {
        "summary": "Record a senior marker's decision",
        "description": "A decision made in your own system, on any script in the session. Decisions are added, never edited: a later one supersedes and both stay on record. `decided_by` is your reference for the senior marker, stored as `partner:` followed by it. Sending the latest decision again records nothing new and returns 200 with `repeated`. A signed-off session takes no more decisions: 409 `session_signed_off`. Nothing here changes a mark by itself; the senior marker's mark is the one that counts. Requires scope `marking:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "The marking session, as returned when it was started.",
              "example": "22222222-2222-4222-8222-222222222222"
            },
            "required": true,
            "description": "The marking session, as returned when it was started.",
            "name": "session_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PartnerFlagDecisionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Already the latest decision; nothing new was recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlagDecisionResult"
                }
              }
            }
          },
          "201": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlagDecisionResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/marking-sessions/{session_id}/record": {
      "get": {
        "summary": "Read a marking session's record",
        "description": "The quality-assurance record as Markdown, built from the stored marks and decisions alone: summary, questions, agreement, markers, and every flagged script with its decision, who decided and when. It names scripts and markers, never a candidate. It opens with \"Signed off\" once the session is, noting any scripts removed since, or with \"Not final\" while any flag waits. Requires scope `marking:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "The marking session, as returned when it was started.",
              "example": "22222222-2222-4222-8222-222222222222"
            },
            "required": true,
            "description": "The marking session, as returned when it was started.",
            "name": "session_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkingSessionRecord"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/marking-sessions/{session_id}/markers/{marker_id}": {
      "get": {
        "summary": "Read one marker's page",
        "description": "One marker's scripts in the order they marked them, their pattern against the second reading, how that difference moved through their pile in stretches of scripts read twice (null until they have enough), and each question they marked. It names scripts, never a candidate. Requires scope `marking:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "The marking session, as returned when it was started.",
              "example": "22222222-2222-4222-8222-222222222222"
            },
            "required": true,
            "description": "The marking session, as returned when it was started.",
            "name": "session_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "description": "The marker as your system identifies them, as sent with their scripts.",
              "example": "marker-a"
            },
            "required": true,
            "description": "The marker as your system identifies them, as sent with their scripts.",
            "name": "marker_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The marker's page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkerPage"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/marking-sessions/{session_id}/markers/{marker_id}/feedback": {
      "get": {
        "summary": "Read feedback for one marker",
        "description": "Feedback written to one marker, as Markdown, to share with them: what the second reading found, their pattern, how their marks moved through their pile, each question, and what senior markers decided on their flagged scripts and why, with their notes. It names scripts, never a candidate, and not who decided. Requires scope `marking:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "The marking session, as returned when it was started.",
              "example": "22222222-2222-4222-8222-222222222222"
            },
            "required": true,
            "description": "The marking session, as returned when it was started.",
            "name": "session_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "description": "The marker as your system identifies them, as sent with their scripts.",
              "example": "marker-a"
            },
            "required": true,
            "description": "The marker as your system identifies them, as sent with their scripts.",
            "name": "marker_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The feedback",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkerFeedback"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/marking-sessions/{session_id}/final-marks.csv": {
      "get": {
        "summary": "Download the final marks",
        "description": "One row per script with the mark that counts: the marker's mark where the script was not flagged, the senior marker's latest decision where there is one, and a blank while a flag waits (`status` is `not_flagged`, `single_marked` for a script never read a second time, `kept`, `changed` or `waiting`). Its SHA-256 is in `X-Content-SHA256`, to compare with a sign-off's `final_marks_sha256`. Columns: `script_id`, `candidate_id`, `marker_id`, `question_id`, `max_mark`, `mark`, `second_reading`, `gap`, `flagged`, `status`, `final_mark`, `decided_by`, `decided_at`, `decisions`. Text starting with `=`, `+`, `-`, `@` or a tab is prefixed with an apostrophe so a spreadsheet cannot run it. It names candidates, so it requires scope `data:export`, and each download is recorded in the audit log.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "The marking session, as returned when it was started.",
              "example": "22222222-2222-4222-8222-222222222222"
            },
            "required": true,
            "description": "The marking session, as returned when it was started.",
            "name": "session_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The final marks, as CSV.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/experiments/{experiment_id}/record": {
      "get": {
        "summary": "How one experiment was assigned, delivered and followed",
        "description": "Counts one experiment from stored records and keeps them apart: who was assigned to each arm, what was delivered under that experiment, whether the intended intervention reached the assigned learners, and what independent evidence followed. It estimates no effect and never explains an individual learner: a difference between arms here is a count, not a result. Both windows are declared in the request and echoed back. An experiment with more than 5,000 assignments returns 422 rather than a partial record. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "The experiment as your system identifies it.",
              "example": "exp_worked_examples"
            },
            "required": true,
            "description": "The experiment as your system identifies it.",
            "name": "experiment_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90,
              "description": "Days after assignment in which a delivery counts. Default 7."
            },
            "required": false,
            "description": "Days after assignment in which a delivery counts. Default 7.",
            "name": "exposure_window_days",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90,
              "description": "Days after assignment in which an independent response counts. Default 30."
            },
            "required": false,
            "description": "Days after assignment in which an independent response counts. Default 30.",
            "name": "outcome_window_days",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The record as it stands now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExperimentRecord"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/evaluations": {
      "get": {
        "summary": "Evaluation runs over the predictions this project issued",
        "description": "Runs newest first, each frozen when it was asked for: the predictions issued in its window, scored against the outcomes recorded by its cutoff, with coverage, Brier score, log loss, AUC where both outcomes occurred, calibration bins and learner-clustered intervals. A run is created from the console by an owner or admin and computed by the drain; a pending run has no metrics yet. Its design status is `predictive`; it never promotes a model. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Runs, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvaluationRunList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/evaluations/{evaluation_id}": {
      "get": {
        "summary": "One evaluation run, with its manifest",
        "description": "The run with its metrics per model and target, the manifest of what it read and the settings it used, and the SHA-256 of that manifest. A withdrawn run keeps its window and the reason, without metrics. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "The run, as returned when it was created or listed.",
              "example": "44444444-4444-4444-8444-444444444444"
            },
            "required": true,
            "description": "The run, as returned when it was created or listed.",
            "name": "evaluation_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The run",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvaluationRun"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/experiments/{experiment_id}/record/report": {
      "get": {
        "summary": "The record set out as a document to share",
        "description": "The same record, with the same windows, set out as a report: the counts by arm in fixed tables and sentences, the limitations and the rule versions, with a Markdown copy in `markdown`. Its label is always descriptive; it adds no estimate and no claim to the record. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "The experiment as your system identifies it.",
              "example": "exp_worked_examples"
            },
            "required": true,
            "description": "The experiment as your system identifies it.",
            "name": "experiment_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90,
              "description": "Days after assignment in which a delivery counts. Default 7."
            },
            "required": false,
            "description": "Days after assignment in which a delivery counts. Default 7.",
            "name": "exposure_window_days",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90,
              "description": "Days after assignment in which an independent response counts. Default 30."
            },
            "required": false,
            "description": "Days after assignment in which an independent response counts. Default 30.",
            "name": "outcome_window_days",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The report, built from the record as it stands now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExperimentRecordReport"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/jobs/{job_id}": {
      "get": {
        "summary": "Processing job status",
        "description": "Status of a processing job created by an ingestion request in the same project. Requires scope `events:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "job_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Job status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobStatus"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/learners/{learner_id}/state": {
      "get": {
        "summary": "Concept summaries for a learner",
        "description": "observed-v0 summaries for every concept with mapped evidence for this learner, ordered by concept ID. Side-effect free. Requires scope `state:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "External learner ID, URL-encoded.",
              "example": "learner_demo_01"
            },
            "required": true,
            "description": "External learner ID, URL-encoded.",
            "name": "learner_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "description": "Default 50, maximum 100."
            },
            "required": false,
            "description": "Default 50, maximum 100.",
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated states",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LearnerStateList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/learners/{learner_id}/concepts/{concept_id}": {
      "get": {
        "summary": "One learner-concept state",
        "description": "The latest completed state revision with `pending_updates`, and what the evidence behind it is: counts by reported assistance, construct type and source, how much of it was independent, the oldest and newest observation, and the kinds of conclusion it could support. A registered learner and concept without evidence return an insufficient_evidence projection (revision 0). Requires scope `state:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "External learner ID, URL-encoded.",
              "example": "learner_demo_01"
            },
            "required": true,
            "description": "External learner ID, URL-encoded.",
            "name": "learner_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "example": "networking.dns"
            },
            "required": true,
            "name": "concept_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "State",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LearnerConceptStateDetail"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/learners/{learner_id}/concepts/{concept_id}/revisions": {
      "get": {
        "summary": "How a learner-concept state changed",
        "description": "Every stored revision of one state, newest first, each with what changed from the revision before (status, counted and correct attempts, sessions, flags, rule and mapping versions) and the events it applied. Revisions are kept as stored; erasure removes them with the state, and retention removes those of a pair whose evidence aged out, whose history starts again at its rebuild. A registered learner and concept with nothing stored return an empty list. Requires scope `state:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "External learner ID, URL-encoded.",
              "example": "learner_demo_01"
            },
            "required": true,
            "description": "External learner ID, URL-encoded.",
            "name": "learner_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "example": "networking.dns"
            },
            "required": true,
            "name": "concept_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Revisions, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StateRevisionList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/learners/{learner_id}/evidence": {
      "get": {
        "summary": "Observations behind the state",
        "description": "Stored observations for a learner, newest first (occurred_at, then event_id), with concept provenance and eligibility. Unknown filters return 400. Requires scope `evidence:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "External learner ID, URL-encoded.",
              "example": "learner_demo_01"
            },
            "required": true,
            "description": "External learner ID, URL-encoded.",
            "name": "learner_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "concept_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "response",
                "exposure",
                "hint",
                "feedback",
                "intervention"
              ]
            },
            "required": false,
            "name": "event_type",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            },
            "required": false,
            "name": "include_superseded",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated evidence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvidenceList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/catalog/courses": {
      "post": {
        "summary": "Register a course",
        "description": "Registers a stable course identity in this project. Requires scope `catalog:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "course_id": "cloud_foundations",
                "label": "Cloud foundations",
                "locale": "en"
              },
              "schema": {
                "$ref": "#/components/schemas/CourseUpsertRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "This identity already exists. Nothing changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseRecord"
                }
              }
            }
          },
          "201": {
            "description": "Registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseRecord"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/catalog/concepts": {
      "post": {
        "summary": "Register a concept",
        "description": "Registers a concept in this project. Concepts are the unit learner state is summarized on; they are yours, not a shared curriculum. Requires scope `catalog:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "concept_id": "networking.ip",
                "label": "IP addressing",
                "description": "Basic IP addressing concepts"
              },
              "schema": {
                "$ref": "#/components/schemas/ConceptUpsertRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "This identity already exists. Nothing changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConceptRecord"
                }
              }
            }
          },
          "201": {
            "description": "Registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConceptRecord"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/catalog/items": {
      "post": {
        "summary": "Register an item version",
        "description": "Registers one immutable item version. A different response kind or rubric for a version already registered returns 409 and changes nothing; edit content by registering a new version. Requires scope `catalog:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "item_id": "question_012",
                "item_version": "1",
                "response_kind": "binary"
              },
              "schema": {
                "$ref": "#/components/schemas/ItemRegisterRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "This version is already registered with the same definition.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ItemVersionRecord"
                }
              }
            }
          },
          "201": {
            "description": "Registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ItemVersionRecord"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/catalog/mappings": {
      "post": {
        "summary": "Approve a concept mapping",
        "description": "Creates the next approved primary mapping revision for an item version. The approver is taken from the credential. A stale `expected_mapping_revision` returns 409. With `rebuild_retained_history`, retained events on this item version that have no concept are attributed to it and the affected learner-concept states are rebuilt; events already attributed to a concept keep that attribution. Requires scope `catalog:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "item_id": "question_012",
                "item_version": "1",
                "concept_id": "networking.ip",
                "role": "primary",
                "source": "customer_reviewed",
                "expected_mapping_revision": null,
                "rebuild_retained_history": false
              },
              "schema": {
                "$ref": "#/components/schemas/MappingCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "This item version already maps to this concept. Nothing changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MappingRecord"
                }
              }
            }
          },
          "201": {
            "description": "A new mapping revision.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MappingRecord"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/learners/{learner_id}/check-plans": {
      "post": {
        "summary": "Issue a shadow-mode check plan",
        "description": "Applies the documented next-check-v0 rule to the learner's current state and stores the result, unchanged from then on. Each check asks for one independent response on a concept, with a reason and the evidence behind it. Shadow mode: nothing is delivered to the learner, and the rule is unvalidated. Send `{}` for the defaults. Requires scope `checks:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "External learner ID, URL-encoded.",
              "example": "learner_demo_01"
            },
            "required": true,
            "description": "External learner ID, URL-encoded.",
            "name": "learner_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "max_checks": 3
              },
              "schema": {
                "$ref": "#/components/schemas/CreateCheckPlanRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Stored. Follow-up is derived from evidence stored after issue; a replay with the same Idempotency-Key and body returns the stored response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckPlan"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 4 KiB. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/learners/{learner_id}/next-step": {
      "get": {
        "summary": "Suggest the next step (shadow mode)",
        "description": "For one learner and one skill, which of three things the records support doing next, by the fixed rule `next-step-v1`: `check_first` when the learner has no result on the skill from answering on their own (direct evidence with `assistance: unassisted`), or was taught it after their last one; `use_tested_version` when a randomized experiment on the skill, or on every skill, supports a version on a later result (MD3 or deeper), read through its release check from a stored, current result; otherwise `run_an_experiment`. A tested version is a group result and never a promise for this learner. Shadow mode: nothing is sent to a learner and nothing is stored. Off for every project until an owner or admin turns it on in the console, and only a sandbox project can; while off, the response says `disabled` and no learner record is read. Unvalidated. Requires scope `suggestions:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "External learner ID, URL-encoded.",
              "example": "learner_demo_01"
            },
            "required": true,
            "description": "External learner ID, URL-encoded.",
            "name": "learner_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "description": "The skill, as your records name it."
            },
            "required": true,
            "description": "The skill, as your records name it.",
            "name": "capability",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A suggestion, or `disabled` while suggestions are off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NextStep"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/check-plans/{plan_id}": {
      "get": {
        "summary": "A check plan with its follow-up",
        "description": "The plan exactly as issued, with each check's follow-up derived now: `observed` (an independent response on the concept in the window), `awaiting` or `not_observed`. Missing follow-up is never treated as failure. Side-effect free. Requires scope `state:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "plan_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Check plan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckPlan"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/predictions": {
      "post": {
        "summary": "Issue a prospective prediction",
        "description": "Predicts the learner's next eligible response on one item version, then freezes everything the model read. The server sets the issue time and the feature cutoff, so evidence recorded later cannot change this prediction, and a client cannot backdate one. Shadow mode and unvalidated: nothing is delivered to a learner. Too little independent evidence returns 422 `insufficient_evidence` rather than a made-up number. Requires scope `predictions:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "learner_id": "learner_demo_01",
                "concept_id": "networking.ip",
                "item_id": "question_012",
                "item_version": "1",
                "target_definition": "next_eligible_response_v1",
                "horizon_seconds": 604800
              },
              "schema": {
                "$ref": "#/components/schemas/PredictionRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Issued and stored, unchanged from now on.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Prediction"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/outcomes": {
      "post": {
        "summary": "Link an observed response to a prediction",
        "description": "Records what actually happened, by naming a response event already stored in this project. The service reads that event and checks it: same learner, item version and concept, eligible, not superseded, and inside the window after the prediction was issued. Correctness and time come from the stored event, never from this request. One outcome per prediction. A window that closes with no eligible response is censored automatically, which is missing evidence and not a wrong answer. Requires scope `outcomes:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "prediction_id": "33333333-3333-4333-8333-333333333333",
                "outcome_event_id": "evt_demo_0002"
              },
              "schema": {
                "$ref": "#/components/schemas/OutcomeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "This event is already this prediction outcome. Nothing changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Outcome"
                }
              }
            }
          },
          "201": {
            "description": "Linked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Outcome"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/learners/{learner_id}/exports": {
      "post": {
        "summary": "Export a learner's records",
        "description": "Generates a JSON Lines file of this learner's retained records in this project: events with their eligibility, states, check plans, and predictions with their outcomes. The first line is a manifest with counts and a SHA-256 of the record lines. The file can be downloaded for 24 hours and holds no other learner and no secret. Requires scope `data:export`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "External learner ID, URL-encoded.",
              "example": "learner_demo_01"
            },
            "required": true,
            "description": "External learner ID, URL-encoded.",
            "name": "learner_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "format": "jsonl"
              },
              "schema": {
                "$ref": "#/components/schemas/ExportRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Generated. Download it from `download.url` until `expires_at`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DataRequest"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/learners/{learner_id}": {
      "delete": {
        "summary": "Erase a learner",
        "description": "Blocks the learner at once: every read and every new event for them returns 410. Then removes their events, states, check plans, predictions, outcomes, queued jobs, stored responses and export files, and reports what was removed by class. Only a keyed reference remains, never the learner ID, so a replayed import cannot quietly re-create them. If removal cannot finish in the request, the service finishes it; poll the data request. No body. Requires scope `data:delete`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "External learner ID, URL-encoded.",
              "example": "learner_demo_01"
            },
            "required": true,
            "description": "External learner ID, URL-encoded.",
            "name": "learner_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "responses": {
          "202": {
            "description": "Access is blocked. `status` is `completed` once removal has finished.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DataRequest"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/data-requests/{data_request_id}": {
      "get": {
        "summary": "An export or erasure request",
        "description": "The request as it stands now: status, what an erasure removed, or where an export can be downloaded. It never names the learner. Requires scope `data:export or data:delete`: the one that matches the request kind, otherwise the request is reported as not found.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "The data request ID returned when the export or erasure was requested."
            },
            "required": true,
            "description": "The data request ID returned when the export or erasure was requested.",
            "name": "data_request_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Data request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DataRequest"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/data-requests/{data_request_id}/download": {
      "get": {
        "summary": "Download an export",
        "description": "The export file as JSON Lines, with its SHA-256 in `X-Content-SHA256`. Authorization is checked on every download. After `expires_at`, or once the learner is erased, returns 410. Requires scope `data:export`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "The data request ID returned when the export or erasure was requested."
            },
            "required": true,
            "description": "The data request ID returned when the export or erasure was requested.",
            "name": "data_request_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The export file.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/interventions": {
      "post": {
        "summary": "Record an intervention",
        "description": "What your learning system actually delivered to a learner: the teaching move, the capability it targeted, and the agent, model and policy release that chose it. Only delivered interventions are recorded; a proposal that never reached the learner is not one. Name `experiment` to record exposure in an experiment. Set `contract.template` to open an Outcome Contract in the same request; its receipt is issued by the job drain as evidence arrives and as each window closes. Append-only: the same ID with identical content is a duplicate, and different content returns 409. Requires scope `events:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "schema_version": "1.0",
                "intervention_id": "int_demo_0001",
                "learner_id": "learner_demo_01",
                "capability": "fractions.add_unlike_denominators",
                "class": "worked_example",
                "delivered_at": "2026-09-13T09:00:00Z",
                "source": "tutor_service",
                "agent_id": "math_tutor",
                "prompt_policy_version": "pp_42",
                "contract": {
                  "template": "durable_capability_gain"
                }
              },
              "schema": {
                "$ref": "#/components/schemas/InterventionInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Already recorded with identical content.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeIngestReceipt"
                }
              }
            }
          },
          "201": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeIngestReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/interventions/batch": {
      "post": {
        "summary": "Record several interventions at once",
        "description": "Up to 100 records, atomic: every invalid intervention is reported with its index and nothing is stored unless all are valid. Results come back in request order without your IDs. Requires scope `events:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "interventions": [
                  {
                    "schema_version": "1.0",
                    "intervention_id": "int_demo_0001",
                    "learner_id": "learner_demo_01",
                    "capability": "fractions.add_unlike_denominators",
                    "class": "worked_example",
                    "delivered_at": "2026-09-13T09:00:00Z",
                    "source": "tutor_service",
                    "agent_id": "math_tutor",
                    "prompt_policy_version": "pp_42",
                    "contract": {
                      "template": "durable_capability_gain"
                    }
                  }
                ]
              },
              "schema": {
                "type": "object",
                "properties": {
                  "interventions": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/InterventionInput"
                    },
                    "minItems": 1,
                    "maxItems": 100
                  }
                },
                "required": [
                  "interventions"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Every record was already recorded with identical content.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeIngestReceipt"
                }
              }
            }
          },
          "201": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeIngestReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/evidence": {
      "post": {
        "summary": "Record evidence",
        "description": "What a learner showed: a score on its own scale, whether they had help, and whether the item was new to them. Evidence that falls inside an open or closed contract window re-evaluates that contract; a late arrival revises its receipt as a new version and never edits the old one. Only `unassisted` evidence counts toward a delayed outcome, and `unknown` is never read as unassisted. Requires scope `events:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "schema_version": "1.0",
                "evidence_id": "ev_demo_0001",
                "learner_id": "learner_demo_01",
                "capability": "fractions.add_unlike_denominators",
                "observed_at": "2026-09-13T09:10:00Z",
                "evidence_type": "assessment_item",
                "score": 1,
                "scale": [
                  0,
                  1
                ],
                "assistance": "unassisted",
                "is_unseen_item": true,
                "scorer": {
                  "type": "deterministic"
                },
                "source": "practice_service"
              },
              "schema": {
                "$ref": "#/components/schemas/EvidenceInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Already recorded with identical content.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeIngestReceipt"
                }
              }
            }
          },
          "201": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeIngestReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/evidence/batch": {
      "post": {
        "summary": "Record several pieces of evidence at once",
        "description": "Up to 100 records, atomic: every invalid piece of evidence is reported with its index and nothing is stored unless all are valid. Results come back in request order without your IDs. Requires scope `events:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "evidence": [
                  {
                    "schema_version": "1.0",
                    "evidence_id": "ev_demo_0001",
                    "learner_id": "learner_demo_01",
                    "capability": "fractions.add_unlike_denominators",
                    "observed_at": "2026-09-13T09:10:00Z",
                    "evidence_type": "assessment_item",
                    "score": 1,
                    "scale": [
                      0,
                      1
                    ],
                    "assistance": "unassisted",
                    "is_unseen_item": true,
                    "scorer": {
                      "type": "deterministic"
                    },
                    "source": "practice_service"
                  }
                ]
              },
              "schema": {
                "type": "object",
                "properties": {
                  "evidence": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/EvidenceInput"
                    },
                    "minItems": 1,
                    "maxItems": 100
                  }
                },
                "required": [
                  "evidence"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Every record was already recorded with identical content.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeIngestReceipt"
                }
              }
            }
          },
          "201": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeIngestReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/states": {
      "post": {
        "summary": "Record a state observation",
        "description": "What your own learner-state model estimated for one capability at one time. OutFigure does not require any particular model; a contract can name a state observation as its baseline. Requires scope `events:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "schema_version": "1.0",
                "state_id": "state_demo_0001",
                "learner_id": "learner_demo_01",
                "capability": "fractions.add_unlike_denominators",
                "observed_at": "2026-09-13T08:59:00Z",
                "state_value": 0.42,
                "uncertainty": 0.31,
                "source_adapter": "customer_kt_model",
                "model_version": "kt-2026-08"
              },
              "schema": {
                "$ref": "#/components/schemas/StateInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Already recorded with identical content.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeIngestReceipt"
                }
              }
            }
          },
          "201": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeIngestReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/states/batch": {
      "post": {
        "summary": "Record several state observations at once",
        "description": "Up to 100 records, atomic: every invalid state observation is reported with its index and nothing is stored unless all are valid. Results come back in request order without your IDs. Requires scope `events:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "states": [
                  {
                    "schema_version": "1.0",
                    "state_id": "state_demo_0001",
                    "learner_id": "learner_demo_01",
                    "capability": "fractions.add_unlike_denominators",
                    "observed_at": "2026-09-13T08:59:00Z",
                    "state_value": 0.42,
                    "uncertainty": 0.31,
                    "source_adapter": "customer_kt_model",
                    "model_version": "kt-2026-08"
                  }
                ]
              },
              "schema": {
                "type": "object",
                "properties": {
                  "states": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/StateInput"
                    },
                    "minItems": 1,
                    "maxItems": 100
                  }
                },
                "required": [
                  "states"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Every record was already recorded with identical content.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeIngestReceipt"
                }
              }
            }
          },
          "201": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeIngestReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/outcome-contracts": {
      "post": {
        "summary": "Open an Outcome Contract",
        "description": "States, before any evidence is read, what would count as evidence after one intervention: which roles (immediate, delayed retention, transfer, external), in which windows, with how much help allowed, and what the baseline is. Use a template or give a plan; a delayed or external role starts at least a day after delivery and always requires unassisted evidence. One contract per intervention, fixed once it opens. Requires scope `outcomes:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "intervention_id": "int_demo_0001",
                "template": "retention_7d",
                "baseline": {
                  "state_id": "state_demo_0001"
                },
                "minimum_effect": 0.1
              },
              "schema": {
                "$ref": "#/components/schemas/ContractCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Opened.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeContract"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/outcome-contracts/{contract_id}": {
      "get": {
        "summary": "An Outcome Contract",
        "description": "The contract as it was opened, whether its windows are still open, and its latest receipt. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "44444444-4444-4444-8444-444444444444"
            },
            "required": true,
            "name": "contract_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Outcome Contract",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeContract"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/outcome-receipts": {
      "get": {
        "summary": "Outcome Receipts",
        "description": "The latest receipt of each contract, newest first; `include_superseded=true` returns every version. Each receipt states its measurement depth (MD0 to MD4) and causal strength (CS0 or CS1: one learner's change is never attributed) separately, lists every piece of evidence the linker considered and why it did or did not count, and names the evaluator and linker versions that produced it. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "intervention_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "learner_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "capability",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "provisional",
                "evaluated",
                "insufficient_evidence"
              ]
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "MD0",
                "MD1",
                "MD2",
                "MD3",
                "MD4"
              ]
            },
            "required": false,
            "name": "measurement_depth",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            },
            "required": false,
            "name": "include_superseded",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Outcome Receipts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeReceiptList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/outcome-receipts/{receipt_id}": {
      "get": {
        "summary": "One Outcome Receipt version",
        "description": "A receipt version exactly as it was issued. A receipt is never edited: a later version supersedes it and points at it. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "55555555-5555-4555-8555-555555555555"
            },
            "required": true,
            "name": "receipt_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Outcome Receipt",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeReceipt"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/experiments": {
      "post": {
        "summary": "Declare an experiment",
        "description": "Declares arms, their allocation in basis points, the control arm, the primary outcome and any secondary outcomes, and the smallest effect that would matter, all before any learner is assigned. The experiment starts as a draft. Randomization is per learner; `cluster` returns 422 until a cluster-level analysis exists. Requires scope `experiments:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "experiment_id": "exp_contrast_cases",
                "name": "Contrast cases against worked examples",
                "hypothesis": "Contrast cases improve 7-day retention on unlike denominators.",
                "capability": "fractions.add_unlike_denominators",
                "randomization_unit": "learner",
                "arms": [
                  {
                    "arm": "worked_examples",
                    "allocation": 5000
                  },
                  {
                    "arm": "contrast_cases",
                    "allocation": 5000
                  }
                ],
                "control_arm": "worked_examples",
                "primary_outcome": {
                  "role": "delayed_retention",
                  "metric": "mean_normalized_score"
                },
                "secondary_outcomes": [
                  {
                    "role": "immediate",
                    "metric": "mean_normalized_score"
                  }
                ],
                "minimum_meaningful_effect": 0.05
              },
              "schema": {
                "$ref": "#/components/schemas/ExperimentCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Declared.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Experiment"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/experiments/{experiment_id}": {
      "get": {
        "summary": "An experiment",
        "description": "The declaration, its status, and the hash it was frozen under when it started. The assignment key is never returned. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "exp_contrast_cases"
            },
            "required": true,
            "name": "experiment_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Experiment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Experiment"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/experiments/{experiment_id}/start": {
      "post": {
        "summary": "Start an experiment",
        "description": "Freezes the declaration under a SHA-256 hash and opens assignment. A running experiment cannot be edited. Requires scope `experiments:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "exp_contrast_cases"
            },
            "required": true,
            "name": "experiment_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "responses": {
          "200": {
            "description": "Experiment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Experiment"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/experiments/{experiment_id}/stop": {
      "post": {
        "summary": "Stop an experiment",
        "description": "Closes assignment. Outcomes of learners already exposed keep arriving and are compared once every window has closed. Requires scope `experiments:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "exp_contrast_cases"
            },
            "required": true,
            "name": "experiment_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "responses": {
          "200": {
            "description": "Experiment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Experiment"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/experiments/{experiment_id}/assign": {
      "post": {
        "summary": "Assign a learner",
        "description": "Returns the arm for a learner in a running experiment, chosen by a keyed hash of the learner ID so the same learner always gets the same arm. The assignment is stored before any exposure; an existing assignment is returned unchanged and never moved. Record the delivered intervention with `experiment` to record exposure. Requires scope `experiments:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "exp_contrast_cases"
            },
            "required": true,
            "name": "experiment_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "learner_id": "learner_demo_01"
              },
              "schema": {
                "$ref": "#/components/schemas/AssignRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Already assigned; returned unchanged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssignResult"
                }
              }
            }
          },
          "201": {
            "description": "Assigned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssignResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "410": {
            "description": "The learner was deleted, or the export is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/experiments/{experiment_id}/results": {
      "get": {
        "summary": "Experiment results",
        "description": "Compares arms on the receipts of learners exposed in the arm they were assigned: assignment and exposure counts, a sample-ratio check, missingness by arm, arm means, and each arm's difference from control with a seeded 95% bootstrap interval. The effect is estimated only once every arm has at least 20 learners with an outcome and every exposed window has closed. Causal strength is CS3 while the split holds and CS1 if the ratio check fails; no result explains an individual learner. Computed from stored receipts; reading it changes nothing. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "exp_contrast_cases"
            },
            "required": true,
            "name": "experiment_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Experiment results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExperimentResult"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/experiments/{experiment_id}/release-check": {
      "get": {
        "summary": "Release check",
        "description": "For each version other than the comparison group, whether the experiment's result supports releasing it, by the fixed rule `release-gate-v1`: `ALLOW`, `ALLOW_WITH_CONSTRAINTS`, `DEFER`, `REVIEW` or `DENY`, with reason codes. The first check that applies decides: not started (defer); a safety check triggered for the version (deny); a broken split or uneven missing results or teaching (review); checks still to come, too few learners or a result being updated (defer); then the main result's 95% interval, wholly above zero (allow), wholly below (deny), or across zero, where a lower end no further below zero than the declared `minimum_meaningful_effect` allows with constraints and anything else defers. Every other problem found follows the deciding reason. Other results are notes and never change the decision. Advisory and unvalidated: OutFigure holds back or releases nothing, and the decision is about the groups, never one learner. Reading it changes nothing the results route would not. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "exp_contrast_cases"
            },
            "required": true,
            "name": "experiment_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Release check",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReleaseCheck"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/experiments/{experiment_id}/report": {
      "get": {
        "summary": "Experiment evidence report",
        "description": "The experiment's declaration and its result, set out as a document to share: its label (randomized experiment, observational association or descriptive analysis), method, period, arms, who was compared, the evidence plans behind each outcome, arm means and differences with their intervals and fixed readings of where each interval lies, outcomes that read differently straight after and later, the sample-ratio check, missing outcomes, warnings, limitations and versions, with the same content as a Markdown document in `markdown`. It adds no estimate of its own and is built from the stored result alone, so the same result always gives the same report. A sandbox project's report says it holds test data. Reading it changes nothing. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "exp_contrast_cases"
            },
            "required": true,
            "name": "experiment_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Experiment evidence report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExperimentReport"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/outcome-summary": {
      "get": {
        "summary": "A project's outcome summary",
        "description": "Records received in the last 30 days; contracts open and closed; how many closed contracts were followed by usable direct evidence (MD2 or deeper) and how many by durable evidence (MD3 or MD4); the depth and causal-strength distribution of the latest receipts; immediate and delayed means; and the same figures by intervention class and policy release. Every rate comes with its numerator and denominator. Descriptive: a difference between groups here is an association, and a randomized experiment is what can compare them. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Outcome summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeSummary"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/cohorts": {
      "get": {
        "summary": "Outcomes by cohort",
        "description": "Outcomes straight after and a day or more later, by intervention class (`by=class`, the default), by class and policy release (`by=release`), or by class and the band of the receipt baseline score (`by=baseline`: low below 0.5, middle below 0.8, high above that, none without a baseline). Closed contracts only, with their latest receipt. Each learner counts once per group: their receipts are averaged first, and each estimate is a 95% t interval over learners, clipped to the 0 to 1 scale and marked when clipped; nothing is estimated from fewer than 20 learners. Per group, how many contracts that asked for a later result got one. At most 50 groups, the largest first. An observational association throughout (CS1): nobody assigned these groups, they were not declared in advance, and a randomized experiment is what can compare them. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "$ref": "#/components/schemas/CohortGrouping"
            },
            "required": false,
            "description": "`class`: by intervention class. `release`: by class and policy release. `baseline`: by class and the band of the receipt's baseline score.",
            "name": "by",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Cohort report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CohortReport"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/capabilities": {
      "get": {
        "summary": "A project's capabilities",
        "description": "Every capability the catalog registers or an intervention or piece of evidence names, in code-point order, with whether the catalog registers it and its label, interventions and evidence recorded on it, its open and closed contracts, how many closed contracts were followed by usable direct evidence (MD2 or deeper) and by durable evidence (MD3 or MD4), and when it was last taught and last measured. Every rate comes with its numerator and denominator, defined as in the outcome summary. Descriptive: thin coverage on a capability means no efficacy statement can be made there yet, not that teaching failed. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Capabilities",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CapabilityList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/data-quality": {
      "get": {
        "summary": "Data-quality report",
        "description": "Eleven checks, each with its definition, numerator, denominator, window, thresholds and state: duplicate submissions, late arrival, unregistered capabilities, assisted evidence, missing baselines, contracts with no follow-up, windows not yet mature, practiced items where transfer was required, exposure without assignment, sample-ratio mismatch and differential missingness. A check with nothing to count is `not_applicable`, never a reassuring zero. Requires scope `evaluations:read`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Data-quality report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DataQualityReport"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints": {
      "post": {
        "summary": "Register a webhook endpoint",
        "description": "Registers a URL to receive signed notices for the listed events. The URL must use https and must not be, or name, a loopback, private, link-local or internal address; the address is checked again at every delivery, on what the name resolves to, and redirects are never followed. The response carries the signing secret once; a replay of this request returns `secret: null`. At most 10 active endpoints per project. Requires scope `webhooks:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "url": "https://api.example.com/outfigure/webhooks",
                "event_types": [
                  "outcome.receipt.created",
                  "outcome.receipt.updated"
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/WebhookEndpointCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered. Store the secret now: it is not shown again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSecretIssued"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "List webhook endpoints",
        "description": "The project's endpoints, newest first, without their secrets. Requires scope `webhooks:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook endpoints",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointList"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints/{endpoint_id}/rotate-secret": {
      "post": {
        "summary": "Rotate an endpoint secret",
        "description": "Issues a new signing secret. For the next 24 hours notices carry a signature from each secret, so a receiver can switch without missing one. The new secret is in this response once; a replay returns `secret: null`. Requires scope `webhooks:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "66666666-6666-4666-8666-666666666666"
            },
            "required": true,
            "name": "endpoint_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "responses": {
          "200": {
            "description": "Rotated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSecretIssued"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints/{endpoint_id}/disable": {
      "post": {
        "summary": "Disable an endpoint",
        "description": "Stops new notices to the endpoint; deliveries still pending to it fail as `endpoint_disabled`. Disabling is final: register a new endpoint to resume. Requires scope `webhooks:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "66666666-6666-4666-8666-666666666666"
            },
            "required": true,
            "name": "endpoint_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "responses": {
          "200": {
            "description": "Disabled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-deliveries": {
      "get": {
        "summary": "List webhook deliveries",
        "description": "Deliveries newest first, each with its notice, status, attempts and the last result. `status=failed` is the dead-letter view. Filters: `endpoint_id`, `status`; cursor pagination. Requires scope `webhooks:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "endpoint_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "delivered",
                "failed"
              ]
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook deliveries",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-deliveries/{delivery_id}/replay": {
      "post": {
        "summary": "Replay a delivery",
        "description": "Sends a delivered or failed notice again, under the same delivery ID, with a fresh set of attempts. Receivers deduplicate by that ID. Requires scope `webhooks:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "77777777-7777-4777-8777-777777777777"
            },
            "required": true,
            "name": "delivery_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "responses": {
          "202": {
            "description": "Queued for delivery.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDelivery"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/imports": {
      "post": {
        "summary": "Import a CSV of interventions or evidence",
        "description": "Stores the file and its mapping and returns at once; a job reads 1000 rows at a time, checks each as the record routes do, and records the valid rows through the same service. Rows older than the historical cutoff are accepted here, as the explicit import route. At most 2097152 bytes. Sandbox projects only, until live learner data can be admitted. Import evidence before the interventions it follows. Requires scope `events:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
              "example": "demo-send-0001"
            },
            "required": true,
            "description": "Required for every mutation. Reuse the same key when retrying the same request; a different body with the same key returns 409.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ImportCreateRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Stored; rows are recorded by a job. Poll the import for its progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeImport"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, event_id reused with different content, a conflicting item version, mapping revision or correction, or an outcome already recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Body over 64 KiB, more than 25 events, or an export over its size limit. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "415": {
            "description": "Body is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Semantically invalid input. Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable or commit outcome unknown. Retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "List imports",
        "description": "Imports newest first, with their progress. Requires scope `events:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Imports",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeImportList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing Idempotency-Key, invalid query or cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/imports/{import_id}": {
      "get": {
        "summary": "Read an import",
        "description": "Its status and row counts: recorded, duplicate and invalid. Requires scope `events:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "88888888-8888-4888-8888-888888888888"
            },
            "required": true,
            "name": "import_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The import",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutcomeImport"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/imports/{import_id}/errors": {
      "get": {
        "summary": "Download the problems in an import",
        "description": "Every stored problem as CSV with the columns `line`, `field` and `code`, in line order: never a value from the file. Requires scope `events:write`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "88888888-8888-4888-8888-888888888888"
            },
            "required": true,
            "name": "import_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The problems, as CSV.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the required scope, or the project is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource, or a resource in another project (indistinguishable by design).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this API key, or the sandbox project reached its daily limit of new events. Nothing was processed or stored. Retry after the Retry-After interval with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "notice": {
      "post": {
        "summary": "A notice OutFigure sends to your endpoint",
        "description": "Sent to each registered endpoint for the events it subscribed to. Verify the `OutFigure-Signature` header against the raw body before parsing: `t=<unix seconds>,v1=<hex>`, where the hex is HMAC-SHA256 over `<t>.<body>` with your secret as UTF-8, and refuse a timestamp more than five minutes old. Deduplicate by `OutFigure-Webhook-Id`. Answer 2xx quickly; anything else is retried up to eight times over about two days. Redirects are not followed.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=329743da…"
            },
            "required": true,
            "name": "OutFigure-Signature",
            "in": "header"
          },
          {
            "schema": {
              "type": "string",
              "description": "The delivery ID."
            },
            "required": true,
            "description": "The delivery ID.",
            "name": "OutFigure-Webhook-Id",
            "in": "header"
          },
          {
            "schema": {
              "type": "string",
              "example": "outcome.receipt.created"
            },
            "required": true,
            "name": "OutFigure-Event",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookNotice"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Received. Any 2xx status counts."
          }
        }
      }
    }
  }
}
