{
  "openapi": "3.1.0",
  "info": {
    "title": "NoireBox",
    "description": "\nFlight data recorder for AI agents: **tamper-proof journal** (SHA-256 chain + Ed25519),\n**guardrail** on transcripts, **exportable attestation** verifiable by a third party.\n\nAuth (optional, enabled with `NOIREBOX_CLIENTS=id:secret,...`):\n`POST /api/v1/token` → 1 h JWT → `Authorization: Bearer ...`. Rate limit 60 req/min/client.\n",
    "version": "0.10.0"
  },
  "paths": {
    "/health": {
      "get": {
        "summary": "Health",
        "operationId": "health_health_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "type": "object",
                  "title": "Response Health Health Get"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/token": {
      "post": {
        "summary": "Token",
        "description": "Exchanges client_id/client_secret for a JWT (1 h). 401 if invalid.",
        "operationId": "token_api_v1_token_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenIn"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "type": "object",
                  "title": "Response Token Api V1 Token Post"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/events": {
      "post": {
        "summary": "Append Event",
        "description": "Records an event (prompt, LLM output, evaluation, ...). Returns 201.",
        "operationId": "append_event_api_v1_events_post",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventIn"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "title": "Response Append Event Api V1 Events Post"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "List Events",
        "description": "Paginated list (offset + limit, capped at 1000).\n\n`since_seq` switches to tail mode: everything strictly newer than\nthat seq, oldest first — the cursor for live consumers (the\ndashboard's TRAFFIC view polls it). seq is the primary key, so the\ncursor can neither skip nor re-deliver an event.",
        "operationId": "list_events_api_v1_events_get",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 1000,
              "minimum": 1,
              "default": 100,
              "title": "Limit"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0,
              "title": "Offset"
            }
          },
          {
            "name": "since_seq",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0,
              "title": "Since Seq"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "title": "Response List Events Api V1 Events Get"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/verify": {
      "get": {
        "summary": "Verify",
        "description": "On-the-spot chain verification (internal diagnostic).",
        "operationId": "verify_api_v1_verify_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "type": "object",
                  "title": "Response Verify Api V1 Verify Get"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/activity": {
      "get": {
        "summary": "Activity",
        "description": "Per-day sealed-event counts (UTC) — the dashboard heatmap's source.\n\nOpen by default: an aggregate (day → count) carries no payload, and\nthe dashboard fetches are plain browser calls that never carry an\nAuthorization header. `NOIREBOX_METADATA_AUTH=1` moves it behind the\nbearer token (never /verify — ADR 004).",
        "operationId": "activity_api_v1_activity_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "additionalProperties": true,
                    "type": "object"
                  },
                  "type": "array",
                  "title": "Response Activity Api V1 Activity Get"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/transcripts/scan": {
      "post": {
        "summary": "Scan",
        "description": "Guardrail: detects injections, logs the incident, 201.\n\nTwo engines to choose from: `regex` (built-in heuristics, default) or\n`ml` (trained micro-model — 293 KB, see ml/train.py). The scan AND\nits logging happen in the same request: an incident detected but not\nrecorded would be an audit gap.",
        "operationId": "scan_api_v1_transcripts_scan_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScanIn"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "type": "object",
                  "title": "Response Scan Api V1 Transcripts Scan Post"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/attestation": {
      "get": {
        "summary": "Attestation",
        "description": "Signed attestation of the current state (digest only, no detail).\n\nOpen by default — this is what a third party checks without\ncredentials; `NOIREBOX_METADATA_AUTH=1` moves it behind the token\n(the exported dossier still carries a copy, so the auditor's flow\nnever depends on this route being open).",
        "operationId": "attestation_api_v1_attestation_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "type": "object",
                  "title": "Response Attestation Api V1 Attestation Get"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/attestation.pdf": {
      "get": {
        "summary": "Attestation Pdf Route",
        "description": "Attestation as PDF — the document a DPO files in a case record.\n\nThe source of truth remains the JSON (machine-readable); the PDF is\nthe human version, with the verification procedure printed on it.\n`?lang=fr` serves the French DPO wording; identifiers stay\nlanguage-independent.\n\nThe PDF engine is an optional extra: without `noirebox[pdf]` the\nroute answers 501 with the install hint — never a traceback.",
        "operationId": "attestation_pdf_route_api_v1_attestation_pdf_get",
        "parameters": [
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^(en|fr)$",
              "default": "en",
              "title": "Lang"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/export": {
      "get": {
        "summary": "Export",
        "description": "Full auditable export: events + attestation.\n\nTHIS is the file the third party feeds to verifier/verifier.py.",
        "operationId": "export_api_v1_export_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "type": "object",
                  "title": "Response Export Api V1 Export Get"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/anchors": {
      "post": {
        "summary": "Create Anchor",
        "description": "RFC 3161 anchor: seals the current chain head to one or more TSAs.\n\nEach TSA (separate process with its own key — or a configured external\nservice) signs \"this head_hash existed at date T\". The anchor is\nlogged as an \"anchor\" event: the journal seals its own external\nproof, and a chain regeneration by an insider holding the key becomes\ndetectable (ADR 006). Multi-witness profiles (NOIREBOX_TSA_PROFILES,\nADR 008) put several independent TSAs behind one anchor event — at\nleast one qualified eIDAS TSA for legal weight, plus rotating public\nwitnesses.",
        "operationId": "create_anchor_api_v1_anchors_post",
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "type": "object",
                  "title": "Response Create Anchor Api V1 Anchors Post"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/attestation/verify": {
      "post": {
        "summary": "Verify Attestation Endpoint",
        "description": "Verifies a submitted attestation (the third party holding only the digest).\n\n`att: dict` without a DTO is deliberate — this is foreign data we\ninspect defensively, not an internal contract.",
        "operationId": "verify_attestation_endpoint_api_v1_attestation_verify_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": true,
                "type": "object",
                "title": "Att"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "type": "object",
                  "title": "Response Verify Attestation Endpoint Api V1 Attestation Verify Post"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "EventIn": {
        "properties": {
          "type": {
            "type": "string",
            "maxLength": 64,
            "minLength": 1,
            "title": "Type",
            "examples": [
              "llm_call"
            ]
          },
          "payload": {
            "additionalProperties": true,
            "type": "object",
            "title": "Payload"
          }
        },
        "type": "object",
        "required": [
          "type"
        ],
        "title": "EventIn",
        "description": "Body expected by POST /api/v1/events."
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
          }
        },
        "type": "object",
        "title": "HTTPValidationError"
      },
      "ScanIn": {
        "properties": {
          "meeting_id": {
            "type": "string",
            "maxLength": 128,
            "minLength": 1,
            "title": "Meeting Id"
          },
          "text": {
            "type": "string",
            "maxLength": 200000,
            "minLength": 1,
            "title": "Text"
          },
          "engine": {
            "type": "string",
            "enum": [
              "regex",
              "ml",
              "llm",
              "tiered"
            ],
            "title": "Engine",
            "default": "regex"
          },
          "lang": {
            "type": "string",
            "enum": [
              "fr",
              "en",
              "es"
            ],
            "title": "Lang",
            "description": "consumed by the ml, llm and tiered engines; the regex patterns are bilingual and deliberately ignore it",
            "default": "fr"
          }
        },
        "type": "object",
        "required": [
          "meeting_id",
          "text"
        ],
        "title": "ScanIn",
        "description": "Body expected by POST /api/v1/transcripts/scan."
      },
      "TokenIn": {
        "properties": {
          "client_id": {
            "type": "string",
            "maxLength": 64,
            "minLength": 1,
            "title": "Client Id"
          },
          "client_secret": {
            "type": "string",
            "maxLength": 256,
            "minLength": 1,
            "title": "Client Secret"
          }
        },
        "type": "object",
        "required": [
          "client_id",
          "client_secret"
        ],
        "title": "TokenIn",
        "description": "Body expected by POST /api/v1/token (in-house OAuth2 client-credentials)."
      },
      "ValidationError": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          },
          "input": {
            "title": "Input"
          },
          "ctx": {
            "type": "object",
            "title": "Context"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      }
    }
  }
}
