{
 "openapi": "3.0.3",
 "info": {
  "title": "OAK Relay service",
  "version": "0.1.0",
  "description": "RF-to-C2 messaging bridge \u2014 documented interfaces for import, export, exchange and integration (Challenge 17 EO-3 / EO-5 / DO-3)."
 },
 "components": {
  "securitySchemes": {
   "ApiKeyAuth": {
    "type": "apiKey",
    "in": "header",
    "name": "X-API-Key"
   },
   "BearerAuth": {
    "type": "http",
    "scheme": "bearer"
   }
  }
 },
 "security": [
  {
   "ApiKeyAuth": []
  },
  {
   "BearerAuth": []
  }
 ],
 "paths": {
  "/health": {
   "get": {
    "summary": "{status, product, version, catalogue, formats, message_types, counters, audit_intact}",
    "description": "{status, product, version, catalogue, formats, message_types, counters, audit_intact}",
    "responses": {
     "200": {
      "description": "{status, product, version, catalogue, formats, message_types, counters, audit_intact}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/metrics": {
   "get": {
    "summary": "Prometheus text exposition (counters + gauges)",
    "description": "Prometheus text exposition (counters + gauges) \u2014 scrape target for Splunk / Dynatrace / Azure Monitor / System Center collectors",
    "responses": {
     "200": {
      "description": "Prometheus text exposition (counters + gauges) \u2014 scrape target for Splunk / Dynatrace / Azure Monitor / System Center collectors"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/openapi.json": {
   "get": {
    "summary": "OpenAPI 3.0 description of every endpoint in this table (machine-readable ICD)",
    "description": "OpenAPI 3.0 description of every endpoint in this table (machine-readable ICD)",
    "responses": {
     "200": {
      "description": "OpenAPI 3.0 description of every endpoint in this table (machine-readable ICD)"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/catalogue": {
   "get": {
    "summary": "live message catalogue JSON (the GFI seam: a ratified baseline is loaded here)",
    "description": "live message catalogue JSON (the GFI seam: a ratified baseline is loaded here)",
    "responses": {
     "200": {
      "description": "live message catalogue JSON (the GFI seam: a ratified baseline is loaded here)"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/formats": {
   "get": {
    "summary": "{formats: [...]}",
    "description": "{formats: [...]}",
    "responses": {
     "200": {
      "description": "{formats: [...]}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/ingest": {
   "post": {
    "summary": "{message, errors} ",
    "description": "{message, errors}  \u2014 payload is text, or base64 for `packed`",
    "responses": {
     "200": {
      "description": "{message, errors}  \u2014 payload is text, or base64 for `packed`"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{format, payload}"
       }
      }
     }
    }
   }
  },
  "/export": {
   "post": {
    "summary": "{payload, bytes}",
    "description": "{payload, bytes}",
    "responses": {
     "200": {
      "description": "{payload, bytes}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{format, message}"
       }
      }
     }
    }
   }
  },
  "/bridge": {
   "post": {
    "summary": "{payload, bytes}",
    "description": "{payload, bytes}",
    "responses": {
     "200": {
      "description": "{payload, bytes}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{in_format, out_format, payload}"
       }
      }
     }
    }
   }
  },
  "/extract": {
   "post": {
    "summary": "assured extraction: {msg_type, message, confidence, abstain, missing, evidence, language, rationale, requires_confirmati",
    "description": "assured extraction: {msg_type, message, confidence, abstain, missing, evidence, language, rationale, requires_confirmation}",
    "responses": {
     "200": {
      "description": "assured extraction: {msg_type, message, confidence, abstain, missing, evidence, language, rationale, requires_confirmation}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{text, originator?, serial?}"
       }
      }
     }
    }
   }
  },
  "/stix": {
   "post": {
    "summary": "{bundle, bytes}",
    "description": "{bundle, bytes} \u2014 STIX 2.1 bundle (incident + indicators + TLP marking) for a CYBERINC message",
    "responses": {
     "200": {
      "description": "{bundle, bytes} \u2014 STIX 2.1 bundle (incident + indicators + TLP marking) for a CYBERINC message"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{message}"
       }
      }
     }
    }
   }
  },
  "/dis": {
   "post": {
    "summary": "{pdus_b64}",
    "description": "{pdus_b64} \u2014 DIS 7 Entity State PDUs (opendis, optional)",
    "responses": {
     "200": {
      "description": "{pdus_b64} \u2014 DIS 7 Entity State PDUs (opendis, optional)"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{message}"
       }
      }
     }
    }
   }
  },
  "/voice": {
   "post": {
    "summary": "{transcript, normalized, asr, extraction}",
    "description": "{transcript, normalized, asr, extraction} \u2014 offline speech-to-text (Vosk EN/FR, optional) \u2192 spoken-form normaliser \u2192 assured draft",
    "responses": {
     "200": {
      "description": "{transcript, normalized, asr, extraction} \u2014 offline speech-to-text (Vosk EN/FR, optional) \u2192 spoken-form normaliser \u2192 assured draft"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{wav_b64, lang, originator?}"
       }
      }
     }
    }
   }
  },
  "/cot": {
   "post": {
    "summary": "{events, schema_valid, tak_proto_b64}",
    "description": "{events, schema_valid, tak_proto_b64} \u2014 CoT 2.0 events validated against the MITRE public-release schema; TAK Protocol protobuf when takproto is installed",
    "responses": {
     "200": {
      "description": "{events, schema_valid, tak_proto_b64} \u2014 CoT 2.0 events validated against the MITRE public-release schema; TAK Protocol protobuf when takproto is installed"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{message}"
       }
      }
     }
    }
   }
  },
  "/picture": {
   "post": {
    "summary": "{tracks, alerts, taskings, fused}",
    "description": "{tracks, alerts, taskings, fused} \u2014 the COP / wargame feed; also updates the fused picture; CRITICAL alerts open a ServiceNow-shaped incident",
    "responses": {
     "200": {
      "description": "{tracks, alerts, taskings, fused} \u2014 the COP / wargame feed; also updates the fused picture; CRITICAL alerts open a ServiceNow-shaped incident"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{message}"
       }
      }
     }
    }
   },
   "get": {
    "summary": "the fused picture: {tracks (pid, ids, reports, sources, first/last DTG, age_min, stale, matched_by), alerts (count), tas",
    "description": "the fused picture: {tracks (pid, ids, reports, sources, first/last DTG, age_min, stale, matched_by), alerts (count), taskings, stats}",
    "responses": {
     "200": {
      "description": "the fused picture: {tracks (pid, ids, reports, sources, first/last DTG, age_min, stale, matched_by), alerts (count), taskings, stats}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "parameters": [
     {
      "name": "now",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ]
   }
  },
  "/picture/clear": {
   "post": {
    "summary": "{ok}",
    "description": "{ok}",
    "responses": {
     "200": {
      "description": "{ok}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/bearer/send": {
   "post": {
    "summary": "{frames, attempts, retries, delivered, airtime_s, bytes, bearer, ber}",
    "description": "{frames, attempts, retries, delivered, airtime_s, bytes, bearer, ber}",
    "responses": {
     "200": {
      "description": "{frames, attempts, retries, delivered, airtime_s, bytes, bearer, ber}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{message | payload_b64, bearer, ber | (ebn0_db, js_db), max_retries, seed, to_inbox?}"
       }
      }
     }
    }
   }
  },
  "/link/send": {
   "post": {
    "summary": "transport stats + `receipt` from the receiving station (originator, serial, DTG, status, station)",
    "description": "transport stats + `receipt` from the receiving station (originator, serial, DTG, status, station) \u2014 real UDP link, MTU frames, selective repeat",
    "responses": {
     "200": {
      "description": "transport stats + `receipt` from the receiving station (originator, serial, DTG, status, station) \u2014 real UDP link, MTU frames, selective repeat"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{message, peer:\"host:port\", bearer, ber|js_db+ebn0_db, max_retries, seed}"
       }
      }
     }
    }
   }
  },
  "/link/status": {
   "get": {
    "summary": "{station, link: {addr, peers, pending, tx_frames, tx_dropped, rx_frames, rx_messages, receipts}}",
    "description": "{station, link: {addr, peers, pending, tx_frames, tx_dropped, rx_frames, rx_messages, receipts}}",
    "responses": {
     "200": {
      "description": "{station, link: {addr, peers, pending, tx_frames, tx_dropped, rx_frames, rx_messages, receipts}}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/link/outbox": {
   "get": {
    "summary": "{messages: [{seq, peer, bearer, delivered, acknowledged, receipt, ...}]}",
    "description": "{messages: [{seq, peer, bearer, delivered, acknowledged, receipt, ...}]}",
    "responses": {
     "200": {
      "description": "{messages: [{seq, peer, bearer, delivered, acknowledged, receipt, ...}]}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/link/retry": {
   "post": {
    "summary": "{results, pending}",
    "description": "{results, pending} \u2014 next contact window for held payloads",
    "responses": {
     "200": {
      "description": "{results, pending} \u2014 next contact window for held payloads"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{bearer, ber, max_retries}"
       }
      }
     }
    }
   }
  },
  "/link/start": {
   "post": {
    "summary": "start the UDP link listener explicitly (otherwise started on first /link/send)",
    "description": "start the UDP link listener explicitly (otherwise started on first /link/send)",
    "responses": {
     "200": {
      "description": "start the UDP link listener explicitly (otherwise started on first /link/send)"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{host, port}"
       }
      }
     }
    }
   }
  },
  "/voice/script": {
   "get": {
    "summary": "{lines: [{id, lang, type, text, spoken}], conditions}",
    "description": "{lines: [{id, lang, type, text, spoken}], conditions} \u2014 the read-aloud script for volunteers",
    "responses": {
     "200": {
      "description": "{lines: [{id, lang, type, text, spoken}], conditions} \u2014 the read-aloud script for volunteers"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/voice/corpus": {
   "post": {
    "summary": "save a recording + sidecar metadata into the voice corpus (the console's Record button uses this)",
    "description": "save a recording + sidecar metadata into the voice corpus (the console's Record button uses this)",
    "responses": {
     "200": {
      "description": "save a recording + sidecar metadata into the voice corpus (the console's Record button uses this)"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{wav_b64, line_id, speaker, lang?, condition?, verdict?}"
       }
      }
     }
    }
   },
   "get": {
    "summary": "{recordings: [...], real, synthetic}",
    "description": "{recordings: [...], real, synthetic}",
    "responses": {
     "200": {
      "description": "{recordings: [...], real, synthetic}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/voice/eval": {
   "get": {
    "summary": "run the corpus evaluation (WER, type accuracy, coverage, abstention per language / speaker / condition; full=1 adds the ",
    "description": "run the corpus evaluation (WER, type accuracy, coverage, abstention per language / speaker / condition; full=1 adds the noise + radio-band conditions)",
    "responses": {
     "200": {
      "description": "run the corpus evaluation (WER, type accuracy, coverage, abstention per language / speaker / condition; full=1 adds the noise + radio-band conditions)"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "parameters": [
     {
      "name": "full",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ]
   }
  },
  "/tak/connect": {
   "post": {
    "summary": "connect to a TAK server (TAK streaming protocol: TCP 8087, or TLS 8089 with a client certificate); sends the identity ev",
    "description": "connect to a TAK server (TAK streaming protocol: TCP 8087, or TLS 8089 with a client certificate); sends the identity event; inbound CoT from other clients goes into the fused picture",
    "responses": {
     "200": {
      "description": "connect to a TAK server (TAK streaming protocol: TCP 8087, or TLS 8089 with a client certificate); sends the identity event; inbound CoT from other clients goes into the fused picture"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{host, port, callsign?, tls?, certfile?, keyfile?, cafile?}"
       }
      }
     }
    }
   }
  },
  "/tak/send": {
   "post": {
    "summary": "{events, sent}",
    "description": "{events, sent} \u2014 the message's tracks as CoT events streamed to the connected TAK server",
    "responses": {
     "200": {
      "description": "{events, sent} \u2014 the message's tracks as CoT events streamed to the connected TAK server"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{message, stale_s?}"
       }
      }
     }
    }
   }
  },
  "/tak/status": {
   "get": {
    "summary": "{connected, client: {callsign, uid, sent, received, tracks, pongs}, received_tracks}",
    "description": "{connected, client: {callsign, uid, sent, received, tracks, pongs}, received_tracks}",
    "responses": {
     "200": {
      "description": "{connected, client: {callsign, uid, sent, received, tracks, pongs}, received_tracks}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/tak/disconnect": {
   "post": {
    "summary": "{ok}",
    "description": "{ok}",
    "responses": {
     "200": {
      "description": "{ok}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/tak/server": {
   "post": {
    "summary": "start a live TAK server for a demo or test: taky (MIT, PyPI) if installed, else the stdlib CoT relay; returns {server, a",
    "description": "start a live TAK server for a demo or test: taky (MIT, PyPI) if installed, else the stdlib CoT relay; returns {server, addr}",
    "responses": {
     "200": {
      "description": "start a live TAK server for a demo or test: taky (MIT, PyPI) if installed, else the stdlib CoT relay; returns {server, addr}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{host?, port?, prefer_taky?}"
       }
      }
     }
    }
   }
  },
  "/modem/encode": {
   "post": {
    "summary": "the message as a 2-FSK waveform file (base64): WAV for a sound card / radio audio port, interleaved int16 or float32 I/Q",
    "description": "the message as a 2-FSK waveform file (base64): WAV for a sound card / radio audio port, interleaved int16 or float32 I/Q for an SDR or the Proteus ARB; {samples, seconds, bytes_payload}",
    "responses": {
     "200": {
      "description": "the message as a 2-FSK waveform file (base64): WAV for a sound card / radio audio port, interleaved int16 or float32 I/Q for an SDR or the Proteus ARB; {samples, seconds, bytes_payload}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{message | payload_b64, fs?, baud?, fec?, format: wav | iq16 | cf32, deviation?}"
       }
      }
     }
    }
   }
  },
  "/modem/decode": {
   "post": {
    "summary": "demodulate a recording (WAV or I/Q): {frames, messages, decoded: [{bytes, start, snr_db, fec_fixed}]}; optional lab impa",
    "description": "demodulate a recording (WAV or I/Q): {frames, messages, decoded: [{bytes, start, snr_db, fec_fixed}]}; optional lab impairment before decoding",
    "responses": {
     "200": {
      "description": "demodulate a recording (WAV or I/Q): {frames, messages, decoded: [{bytes, start, snr_db, fec_fixed}]}; optional lab impairment before decoding"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{file_b64, format, fs?, baud?, fec?, deviation?, to_inbox?, ebn0_db? js_db? jammer?}"
       }
      }
     }
    }
   }
  },
  "/link/routes": {
   "get": {
    "summary": "{station, routes: {STATION: \"host:port\", \"*\": default next hop}, hops}",
    "description": "{station, routes: {STATION: \"host:port\", \"*\": default next hop}, hops}",
    "responses": {
     "200": {
      "description": "{station, routes: {STATION: \"host:port\", \"*\": default next hop}, hops}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   },
   "post": {
    "summary": "set relay next hops (multi-hop store-and-forward: a message addressed to another station is forwarded, duplicates suppre",
    "description": "set relay next hops (multi-hop store-and-forward: a message addressed to another station is forwarded, duplicates suppressed, hop limit enforced, end-to-end receipt returned along the path)",
    "responses": {
     "200": {
      "description": "set relay next hops (multi-hop store-and-forward: a message addressed to another station is forwarded, duplicates suppressed, hop limit enforced, end-to-end receipt returned along the path)"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{routes: {STATION: \"host:port\"}, remove: [...], hops?}"
       }
      }
     }
    }
   }
  },
  "/inbox": {
   "get": {
    "summary": "{messages: [{message, received_t, stats, picture, fused, priority}]}",
    "description": "{messages: [{message, received_t, stats, picture, fused, priority}]} \u2014 messages delivered over a bearer or the link; `sort=priority` ranks by live score",
    "responses": {
     "200": {
      "description": "{messages: [{message, received_t, stats, picture, fused, priority}]} \u2014 messages delivered over a bearer or the link; `sort=priority` ranks by live score"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "parameters": [
     {
      "name": "sort",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "now",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ]
   }
  },
  "/inbox/clear": {
   "post": {
    "summary": "{ok}",
    "description": "{ok}",
    "responses": {
     "200": {
      "description": "{ok}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/prioritize": {
   "post": {
    "summary": "{ranked: [{rank, score, precedence_w, content_w, staleness, age_min, time_critical, factors[]}]}",
    "description": "{ranked: [{rank, score, precedence_w, content_w, staleness, age_min, time_critical, factors[]}]} \u2014 precedence \u00d7 content \u00d7 staleness, explained",
    "responses": {
     "200": {
      "description": "{ranked: [{rank, score, precedence_w, content_w, staleness, age_min, time_critical, factors[]}]} \u2014 precedence \u00d7 content \u00d7 staleness, explained"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{messages, now?, half_life_min?}"
       }
      }
     }
    }
   }
  },
  "/chat/start": {
   "post": {
    "summary": "start the line-oriented chat listener (TCP; one message per line, `nick: text`); returns its address",
    "description": "start the line-oriented chat listener (TCP; one message per line, `nick: text`); returns its address",
    "responses": {
     "200": {
      "description": "start the line-oriented chat listener (TCP; one message per line, `nick: text`); returns its address"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{host?, port?, room?}"
       }
      }
     }
    }
   }
  },
  "/chat/line": {
   "post": {
    "summary": "HTTP gateway for chat lines (XMPP / Teams bots) \u2192 assured draft: {msg_type, confidence, abstain, missing, draft, require",
    "description": "HTTP gateway for chat lines (XMPP / Teams bots) \u2192 assured draft: {msg_type, confidence, abstain, missing, draft, requires_confirmation}",
    "responses": {
     "200": {
      "description": "HTTP gateway for chat lines (XMPP / Teams bots) \u2192 assured draft: {msg_type, confidence, abstain, missing, draft, requires_confirmation}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{nick?, text, source?}"
       }
      }
     }
    }
   }
  },
  "/chat/drafts": {
   "get": {
    "summary": "{drafts: [...]}",
    "description": "{drafts: [...]} \u2014 non-abstaining drafts awaiting operator confirmation",
    "responses": {
     "200": {
      "description": "{drafts: [...]} \u2014 non-abstaining drafts awaiting operator confirmation"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/chat/status": {
   "get": {
    "summary": "{addr, room, connections, lines, drafts, abstained, xmpp}",
    "description": "{addr, room, connections, lines, drafts, abstained, xmpp}",
    "responses": {
     "200": {
      "description": "{addr, room, connections, lines, drafts, abstained, xmpp}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/integrations/configure": {
   "post": {
    "summary": "outbound push targets (also via env OAK_RELAY_SPLUNK_HEC_URL / _TOKEN, OAK_RELAY_SERVICENOW_URL / _TOKEN); returns statu",
    "description": "outbound push targets (also via env OAK_RELAY_SPLUNK_HEC_URL / _TOKEN, OAK_RELAY_SERVICENOW_URL / _TOKEN); returns status",
    "responses": {
     "200": {
      "description": "outbound push targets (also via env OAK_RELAY_SPLUNK_HEC_URL / _TOKEN, OAK_RELAY_SERVICENOW_URL / _TOKEN); returns status"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{splunk_hec_url, splunk_hec_token, servicenow_url, servicenow_token | servicenow_user+password, auto_push}"
       }
      }
     }
    }
   }
  },
  "/integrations/push": {
   "post": {
    "summary": "deliver pending events to Splunk HEC (`/services/collector/event`, `Authorization: Splunk <token>`, NDJSON) and incident",
    "description": "deliver pending events to Splunk HEC (`/services/collector/event`, `Authorization: Splunk <token>`, NDJSON) and incidents to the ServiceNow Table API (`/api/now/table/incident`); refused items stay pending",
    "responses": {
     "200": {
      "description": "deliver pending events to Splunk HEC (`/services/collector/event`, `Authorization: Splunk <token>`, NDJSON) and incidents to the ServiceNow Table API (`/api/now/table/incident`); refused items stay pending"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/integrations/status": {
   "get": {
    "summary": "{configured, auto_push, targets, pushed, failed, pending_events, pending_incidents, tickets [{local, sys_id, remote}], l",
    "description": "{configured, auto_push, targets, pushed, failed, pending_events, pending_incidents, tickets [{local, sys_id, remote}], last}",
    "responses": {
     "200": {
      "description": "{configured, auto_push, targets, pushed, failed, pending_events, pending_incidents, tickets [{local, sys_id, remote}], last}"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/integrations/splunk-hec": {
   "get": {
    "summary": "{events, next}",
    "description": "{events, next} \u2014 Splunk HTTP-Event-Collector-shaped batch (pull)",
    "responses": {
     "200": {
      "description": "{events, next} \u2014 Splunk HTTP-Event-Collector-shaped batch (pull)"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "parameters": [
     {
      "name": "since",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ]
   }
  },
  "/integrations/servicenow/incidents": {
   "get": {
    "summary": "{result: [incident records]}",
    "description": "{result: [incident records]} \u2014 ServiceNow-incident-shaped (pull)",
    "responses": {
     "200": {
      "description": "{result: [incident records]} \u2014 ServiceNow-incident-shaped (pull)"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/validate": {
   "post": {
    "summary": "{errors, msg_type}",
    "description": "{errors, msg_type} \u2014 catalogue validation without export",
    "responses": {
     "200": {
      "description": "{errors, msg_type} \u2014 catalogue validation without export"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{message}"
       }
      }
     }
    }
   }
  },
  "/validate/niem": {
   "post": {
    "summary": "{available, valid, errors}",
    "description": "{available, valid, errors} \u2014 validation against the OASIS NIEM 6.0 schemas (needs lxml + model checkout)",
    "responses": {
     "200": {
      "description": "{available, valid, errors} \u2014 validation against the OASIS NIEM 6.0 schemas (needs lxml + model checkout)"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "{message | payload}"
       }
      }
     }
    }
   }
  },
  "/samples": {
   "get": {
    "summary": "{messages: [...]}",
    "description": "{messages: [...]} \u2014 the nine representational sample messages",
    "responses": {
     "200": {
      "description": "{messages: [...]} \u2014 the nine representational sample messages"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/audit": {
   "get": {
    "summary": "{intact, count, head}",
    "description": "{intact, count, head} \u2014 tamper-evident hash chain",
    "responses": {
     "200": {
      "description": "{intact, count, head} \u2014 tamper-evident hash chain"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  },
  "/": {
   "get": {
    "summary": "the bilingual (EN/FR) operator console (also /console), served by the same process",
    "description": "the bilingual (EN/FR) operator console (also /console), served by the same process",
    "responses": {
     "200": {
      "description": "the bilingual (EN/FR) operator console (also /console), served by the same process"
     },
     "400": {
      "description": "bad request"
     },
     "401": {
      "description": "missing or wrong API key"
     }
    }
   }
  }
 }
}