{
  "openapi": "3.1.0",
  "info": {
    "title": "Construct Metrics API",
    "version": "1.0.0-private-beta",
    "summary": "Deterministic metrics for customer-executed AI-agent and LLM evaluations.",
    "description": "The customer runs its own AI agent or LLM workflow and submits only bounded structured observations. Observation status, latency, token counts, cost and check outcomes are customer-reported; Construct does not independently verify their factual truth, execution provenance or provider billing. The API never executes customer commands, calls caller-selected target URLs, accepts prompts or model responses, or relays customer credentials. It returns machine-readable metrics only, never a full report, recommendation, certification or legal opinion."
  },
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "servers": [
    {
      "url": "https://www.consilience.work",
      "description": "Production private-beta service"
    }
  ],
  "tags": [
    {
      "name": "Contract",
      "description": "Authenticated schema discovery."
    },
    {
      "name": "Access",
      "description": "Allocated 60-minute access windows and usage under a written Order."
    },
    {
      "name": "Metrics",
      "description": "Metrics calculated from customer-reported structured observations."
    }
  ],
  "paths": {
    "/api/construct/v1/schema": {
      "get": {
        "operationId": "getConstructMetricsSchema",
        "tags": ["Contract"],
        "summary": "Read the accepted observation contract",
        "description": "Requires a provisioned client API key. Reading the schema does not consume an evaluation-hour credit.",
        "security": [
          {
            "ClientApiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Current closed schema, operations and service boundaries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SchemaDocument"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/construct/v1/sessions": {
      "post": {
        "operationId": "openConstructMetricsSession",
        "tags": ["Access"],
        "summary": "Open one allocated 60-minute access window",
        "description": "Consumes exactly one evaluation-hour credit allocated under the written Order through an atomic ledger update. The access window is consecutive and includes idle time. This operation accepts no request body. Retry with the same Idempotency-Key to receive the same session token without consuming another credit.",
        "security": [
          {
            "ClientApiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "Access window opened, or an idempotent replay of the same opening operation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "No allocated evaluation-hour credits remain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/construct/v1/metrics": {
      "post": {
        "operationId": "calculateConstructMetrics",
        "tags": ["Metrics"],
        "summary": "Calculate metrics from structured observations",
        "description": "Requires an active session token. The JSON body is limited to 262144 bytes and 500 cases. Every accepted value is customer-reported. Construct computes deterministic aggregates but does not execute, observe or attest the underlying AI run. The returned SHA-256 identifies the canonical validated payload; it does not prove factual truth, authorship, authenticity, execution provenance or chain of custody.",
        "security": [
          {
            "SessionToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MetricsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculated metrics or an idempotent replay. A full report is never included.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MetricsEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "The Idempotency-Key was already used with a different canonical request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "The request body exceeds 262144 bytes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "The request does not use application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "A structured field is invalid, unknown or outside its boundary.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/construct/v1/usage": {
      "get": {
        "operationId": "getConstructMetricsUsage",
        "tags": ["Access"],
        "summary": "Read the allocated evaluation-hour ledger",
        "description": "Returns allocated, consumed and remaining hour credits plus the number of currently active sessions. Reading usage consumes no credit.",
        "security": [
          {
            "ClientApiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Current client ledger totals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ClientApiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "opaque client API key",
        "description": "Provisioned manually after the written Order and any required payment. Send only in the HTTPS Authorization header. Never place the key in a URL, request body, source code or this public contract."
      },
      "SessionToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "opaque 60-minute session token",
        "description": "Returned once by POST /sessions for use with POST /metrics during that allocated access window. Send only in the HTTPS Authorization header."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "A unique operation identifier. Use 16 to 128 characters from A-Z, a-z, 0-9, period, underscore, colon and hyphen. Reuse only when retrying the exact same operation.",
        "schema": {
          "type": "string",
          "minLength": 16,
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9._:-]{16,128}$"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The query, body, URL or idempotency contract is invalid.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The Bearer credential is missing, invalid or expired.",
        "headers": {
          "WWW-Authenticate": {
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The client access agreement has expired.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "Unavailable": {
        "description": "The service, configuration or strong-consistency ledger is temporarily unavailable.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": ["code", "message"],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "field": {
                "type": "string"
              }
            }
          }
        }
      },
      "SchemaDocument": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schema_version",
          "service",
          "purpose",
          "accepted_content",
          "prohibited_content",
          "limits",
          "operations",
          "metrics_request",
          "output"
        ],
        "properties": {
          "schema_version": {
            "const": "1.0"
          },
          "service": {
            "const": "Construct Metrics API"
          },
          "purpose": {
            "type": "string"
          },
          "accepted_content": {
            "type": "string"
          },
          "prohibited_content": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "limits": {
            "type": "object",
            "additionalProperties": false,
            "required": ["body_bytes", "cases_per_request", "access_window_minutes"],
            "properties": {
              "body_bytes": {
                "const": 262144
              },
              "cases_per_request": {
                "const": 500
              },
              "access_window_minutes": {
                "const": 60
              }
            }
          },
          "operations": {
            "type": "object",
            "minProperties": 4,
            "maxProperties": 4,
            "additionalProperties": {
              "type": "string"
            }
          },
          "metrics_request": {
            "type": "object",
            "additionalProperties": true
          },
          "output": {
            "type": "object",
            "additionalProperties": false,
            "required": ["report_included", "full_report"],
            "properties": {
              "report_included": {
                "const": false
              },
              "full_report": {
                "type": "string"
              }
            }
          }
        }
      },
      "MetricsRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": ["run_id", "cases"],
        "description": "Closed, structured observation payload. All observations are supplied by the customer and are not independently witnessed or verified by Construct. Maximum encoded request body: 262144 bytes.",
        "properties": {
          "run_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$",
            "description": "Opaque non-sensitive customer run identifier."
          },
          "cost_currency": {
            "type": "string",
            "enum": ["EUR", "USD", "GBP"],
            "description": "Required when any case reports cost. Cost remains customer-reported."
          },
          "cases": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "$ref": "#/components/schemas/CaseObservation"
            }
          }
        }
      },
      "CaseObservation": {
        "type": "object",
        "additionalProperties": false,
        "required": ["case_id", "status", "latency_ms"],
        "description": "A bounded customer-reported observation. It is an input to aggregation, not an independent Construct attestation of an AI execution.",
        "properties": {
          "case_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
          },
          "status": {
            "type": "string",
            "enum": ["pass", "fail", "error", "timeout"],
            "description": "Customer-reported case outcome. Construct does not independently verify its factual truth or provenance."
          },
          "latency_ms": {
            "type": "number",
            "minimum": 0,
            "maximum": 86400000,
            "description": "Finite customer-reported latency in milliseconds."
          },
          "repeat_group": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
          },
          "input_tokens": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1000000000,
            "description": "Customer-reported provider token count."
          },
          "output_tokens": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1000000000,
            "description": "Customer-reported provider token count."
          },
          "cost": {
            "type": "number",
            "minimum": 0,
            "maximum": 1000000,
            "description": "Customer-reported cost in the request-level cost_currency. Construct does not verify provider billing."
          },
          "tool_checks": {
            "type": "array",
            "maxItems": 32,
            "items": {
              "$ref": "#/components/schemas/ToolCheck"
            }
          },
          "boundary_violations": {
            "type": "array",
            "maxItems": 32,
            "description": "Customer-reported enumerated violations. Duplicate codes in one case are de-duplicated before aggregation.",
            "items": {
              "type": "string",
              "enum": [
                "scope_escape",
                "unauthorized_tool",
                "data_boundary",
                "policy_boundary",
                "instruction_override"
              ]
            }
          },
          "trace": {
            "$ref": "#/components/schemas/TraceObservation"
          }
        }
      },
      "ToolCheck": {
        "type": "object",
        "additionalProperties": false,
        "required": ["name", "passed"],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80,
            "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,79}$"
          },
          "passed": {
            "type": "boolean",
            "description": "Customer-reported check outcome."
          }
        }
      },
      "TraceObservation": {
        "type": "object",
        "additionalProperties": false,
        "required": ["expected_steps", "observed_steps"],
        "description": "Customer-reported trace counts. observed_steps must not exceed expected_steps.",
        "properties": {
          "expected_steps": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100000
          },
          "observed_steps": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100000
          }
        }
      },
      "SessionEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": ["session", "idempotent_replay"],
        "properties": {
          "session": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "token",
              "issued_at",
              "expires_at",
              "access_window_minutes",
              "hours_consumed"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^sess_[a-f0-9]{24}$"
              },
              "token": {
                "type": "string",
                "writeOnly": true,
                "description": "Opaque session credential. Store securely and send only in the Authorization header."
              },
              "issued_at": {
                "type": "string",
                "format": "date-time"
              },
              "expires_at": {
                "type": "string",
                "format": "date-time"
              },
              "access_window_minutes": {
                "const": 60
              },
              "hours_consumed": {
                "const": 1
              }
            }
          },
          "idempotent_replay": {
            "type": "boolean"
          }
        }
      },
      "UsageEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": ["usage"],
        "properties": {
          "usage": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "client_id",
              "allocated_hours",
              "consumed_hours",
              "remaining_hours",
              "active_sessions",
              "metering"
            ],
            "properties": {
              "client_id": {
                "type": "string"
              },
              "allocated_hours": {
                "type": "integer",
                "minimum": 0
              },
              "consumed_hours": {
                "type": "integer",
                "minimum": 0
              },
              "remaining_hours": {
                "type": "integer",
                "minimum": 0
              },
              "active_sessions": {
                "type": "integer",
                "minimum": 0
              },
              "metering": {
                "type": "string"
              }
            }
          }
        }
      },
      "MetricsEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": ["metrics", "idempotent_replay"],
        "properties": {
          "metrics": {
            "$ref": "#/components/schemas/MetricsResult"
          },
          "idempotent_replay": {
            "type": "boolean"
          }
        }
      },
      "MetricsResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schema_version",
          "run_id",
          "case_count",
          "status",
          "latency_ms",
          "repeat_stability",
          "tokens_reported",
          "cost_reported",
          "tool_checks",
          "boundary_violations",
          "trace_completeness",
          "integrity",
          "report_included"
        ],
        "properties": {
          "schema_version": {
            "const": "1.0"
          },
          "run_id": {
            "type": "string"
          },
          "case_count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500
          },
          "status": {
            "$ref": "#/components/schemas/StatusMetrics"
          },
          "latency_ms": {
            "type": "object",
            "additionalProperties": false,
            "required": ["p50", "p95", "max"],
            "properties": {
              "p50": {
                "type": "number",
                "minimum": 0
              },
              "p95": {
                "type": "number",
                "minimum": 0
              },
              "max": {
                "type": "number",
                "minimum": 0
              }
            }
          },
          "repeat_stability": {
            "type": "object",
            "additionalProperties": false,
            "required": ["eligible_groups", "stable_groups", "stability_rate"],
            "properties": {
              "eligible_groups": {
                "type": "integer",
                "minimum": 0
              },
              "stable_groups": {
                "type": "integer",
                "minimum": 0
              },
              "stability_rate": {
                "type": ["number", "null"],
                "minimum": 0,
                "maximum": 1
              }
            }
          },
          "tokens_reported": {
            "type": "object",
            "additionalProperties": false,
            "required": ["cases", "input", "output", "total"],
            "description": "Sums of customer-reported token values; not independently verified provider usage.",
            "properties": {
              "cases": {"type": "integer", "minimum": 0},
              "input": {"type": "integer", "minimum": 0},
              "output": {"type": "integer", "minimum": 0},
              "total": {"type": "integer", "minimum": 0}
            }
          },
          "cost_reported": {
            "type": "object",
            "additionalProperties": false,
            "required": ["cases", "currency", "total"],
            "description": "Sum of customer-reported cost values; not independently verified provider billing.",
            "properties": {
              "cases": {"type": "integer", "minimum": 0},
              "currency": {
                "type": ["string", "null"],
                "enum": ["EUR", "USD", "GBP", null]
              },
              "total": {"type": "number", "minimum": 0}
            }
          },
          "tool_checks": {
            "type": "object",
            "additionalProperties": false,
            "required": ["total", "passed", "failed", "pass_rate"],
            "properties": {
              "total": {"type": "integer", "minimum": 0},
              "passed": {"type": "integer", "minimum": 0},
              "failed": {"type": "integer", "minimum": 0},
              "pass_rate": {"type": ["number", "null"], "minimum": 0, "maximum": 1}
            }
          },
          "boundary_violations": {
            "type": "object",
            "additionalProperties": false,
            "required": ["total", "cases_affected", "by_code"],
            "properties": {
              "total": {"type": "integer", "minimum": 0},
              "cases_affected": {"type": "integer", "minimum": 0},
              "by_code": {
                "$ref": "#/components/schemas/BoundaryCounts"
              }
            }
          },
          "trace_completeness": {
            "type": "object",
            "additionalProperties": false,
            "required": ["cases", "expected_steps", "observed_steps", "completeness_rate"],
            "properties": {
              "cases": {"type": "integer", "minimum": 0},
              "expected_steps": {"type": "integer", "minimum": 0},
              "observed_steps": {"type": "integer", "minimum": 0},
              "completeness_rate": {"type": ["number", "null"], "minimum": 0, "maximum": 1}
            }
          },
          "integrity": {
            "type": "object",
            "additionalProperties": false,
            "required": ["algorithm", "canonical_sha256"],
            "description": "Payload identity only. The digest detects a changed canonical validated payload; it does not prove factual truth, authenticity, authorship, execution provenance or chain of custody.",
            "properties": {
              "algorithm": {
                "const": "SHA-256"
              },
              "canonical_sha256": {
                "type": "string",
                "pattern": "^[a-f0-9]{64}$"
              }
            }
          },
          "report_included": {
            "const": false,
            "description": "Always false. Human-reviewed analysis and the full report are separate Construct services."
          }
        }
      },
      "StatusMetrics": {
        "type": "object",
        "additionalProperties": false,
        "required": ["counts", "pass_rate", "failure_rate", "error_rate", "timeout_rate"],
        "description": "Aggregates customer-reported statuses. They are not independent execution evidence.",
        "properties": {
          "counts": {
            "type": "object",
            "additionalProperties": false,
            "required": ["pass", "fail", "error", "timeout"],
            "properties": {
              "pass": {"type": "integer", "minimum": 0},
              "fail": {"type": "integer", "minimum": 0},
              "error": {"type": "integer", "minimum": 0},
              "timeout": {"type": "integer", "minimum": 0}
            }
          },
          "pass_rate": {"type": "number", "minimum": 0, "maximum": 1},
          "failure_rate": {"type": "number", "minimum": 0, "maximum": 1},
          "error_rate": {"type": "number", "minimum": 0, "maximum": 1},
          "timeout_rate": {"type": "number", "minimum": 0, "maximum": 1}
        }
      },
      "BoundaryCounts": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data_boundary",
          "instruction_override",
          "policy_boundary",
          "scope_escape",
          "unauthorized_tool"
        ],
        "properties": {
          "data_boundary": {"type": "integer", "minimum": 0},
          "instruction_override": {"type": "integer", "minimum": 0},
          "policy_boundary": {"type": "integer", "minimum": 0},
          "scope_escape": {"type": "integer", "minimum": 0},
          "unauthorized_tool": {"type": "integer", "minimum": 0}
        }
      }
    }
  },
  "x-construct-limits": {
    "max_body_bytes": 262144,
    "max_cases": 500,
    "access_window_minutes": 60,
    "report_included": false
  },
  "x-construct-boundaries": {
    "accepts_structured_observations_only": true,
    "executes_customer_commands": false,
    "calls_customer_targets": false,
    "accepts_prompts_or_model_responses": false,
    "accepts_customer_credentials_in_payload": false,
    "customer_reported_observations": true,
    "sha256_proves_payload_identity_only": true
  }
}
