{"components":{"schemas":{"BattleRhythmCreateRequest":{"additionalProperties":false,"properties":{"event_time":{"description":"Military time. Supports HHMM or HH:MM input.","type":"string"},"title":{"type":"string"}},"required":["title","event_time"],"type":"object"},"BattleRhythmItem":{"additionalProperties":false,"properties":{"created_at":{"format":"date-time","type":["string","null"]},"event_time":{"pattern":"^([01][0-9]|2[0-3])[0-5][0-9]$","type":"string"},"id":{"type":"integer"},"sort_order":{"type":"integer"},"title":{"type":"string"},"updated_at":{"format":"date-time","type":["string","null"]}},"required":["id","title","event_time","sort_order","created_at","updated_at"],"type":"object"},"BattleRhythmUpdateRequest":{"additionalProperties":false,"properties":{"event_time":{"description":"Military time. Supports HHMM or HH:MM input.","type":"string"},"title":{"type":"string"}},"type":"object"},"ChangeRequestCreateRequest":{"additionalProperties":false,"properties":{"status":{"type":"string"},"subject":{"type":"string"}},"required":["subject"],"type":"object"},"ChangeRequestItem":{"additionalProperties":false,"properties":{"created_at":{"format":"date-time","type":["string","null"]},"id":{"type":"integer"},"status":{"enum":["backlog","in_progress","completed"],"type":"string"},"status_changed_at":{"format":"date-time","type":["string","null"]},"subject":{"type":"string"},"updated_at":{"format":"date-time","type":["string","null"]}},"required":["id","subject","status","status_changed_at","created_at","updated_at"],"type":"object"},"ChangeRequestUpdateRequest":{"additionalProperties":false,"properties":{"status":{"type":"string"},"subject":{"type":"string"}},"type":"object"},"ErrorResponse":{"additionalProperties":false,"properties":{"error":{"type":"string"}},"required":["error"],"type":"object"},"HealthResponse":{"additionalProperties":false,"properties":{"status":{"type":"string"}},"required":["status"],"type":"object"},"IncidentTrackerEntry":{"additionalProperties":false,"description":"One Incident Tracker row as returned by the API.","properties":{"created_at":{"format":"date-time","type":["string","null"]},"details":{"description":"Free-text context; null if empty","type":["string","null"]},"id":{"description":"Primary key for DELETE /api/incident-tracker-entries/{entry_id}","type":"integer"},"incident_name":{"description":"Short title or name of the incident","type":"string"},"recorded_at":{"description":"Incident time (stored as UTC; serialized with isoformat)","format":"date-time","type":"string"},"status":{"description":"Workflow status (same vocabulary as RFIs / change requests)","enum":["backlog","in_progress","completed"],"type":"string"},"team_assigned":{"description":"Owning or responding team","type":"string"},"updated_at":{"format":"date-time","type":["string","null"]}},"required":["id","recorded_at","incident_name","status","team_assigned","details","created_at","updated_at"],"type":"object"},"IncidentTrackerEntryCreateRequest":{"additionalProperties":false,"description":"Body for POST /api/incident-tracker-entries","properties":{"details":{"description":"Optional narrative or notes","type":"string"},"incident_name":{"description":"Required; incident title","type":"string"},"recorded_at":{"description":"Optional; defaults to current server time. ISO-8601; Z or offsets converted to UTC; naive strings treated as America/New_York wall time.","format":"date-time","type":"string"},"status":{"description":"Optional; defaults to backlog. Canonical: backlog, in_progress, completed. Aliases: in progress, inprogress, complete, completed.","type":"string"},"team_assigned":{"description":"Required; team name or code","type":"string"}},"required":["incident_name","team_assigned"],"type":"object"},"IncidentTrackerEntryUpdateRequest":{"additionalProperties":false,"description":"Body for PATCH or PUT /api/incident-tracker-entries/{entry_id}. All properties optional.","properties":{"details":{"description":"Notes; null clears stored details","type":["string","null"]},"incident_name":{"description":"If present, must be non-empty after trim","type":"string"},"recorded_at":{"description":"If present, must be non-empty. Parsed like create: ISO-8601 with optional offset; naive values as America/New_York.","format":"date-time","type":"string"},"status":{"description":"backlog | in_progress | completed (and aliases as on create)","type":"string"},"team_assigned":{"description":"If present, must be non-empty after trim","type":"string"}},"type":"object"},"Project":{"additionalProperties":false,"properties":{"created_at":{"format":"date-time","type":["string","null"]},"description":{"type":["string","null"]},"detail":{"type":["string","null"]},"id":{"type":"integer"},"name":{"type":"string"},"owner":{"type":["string","null"]},"status":{"type":"string"},"updated_at":{"format":"date-time","type":["string","null"]}},"required":["id","name","status","owner","description","detail","created_at","updated_at"],"type":"object"},"ProjectCreateRequest":{"additionalProperties":false,"properties":{"description":{"type":"string"},"detail":{"type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"status":{"type":"string"}},"required":["name"],"type":"object"},"ProjectUpdateRequest":{"additionalProperties":false,"properties":{"description":{"type":"string"},"detail":{"type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"status":{"type":"string"}},"type":"object"},"RFICreateRequest":{"additionalProperties":false,"properties":{"status":{"description":"backlog | in_progress | completed (or in progress)","type":"string"},"subject":{"type":"string"}},"required":["subject"],"type":"object"},"RFIItem":{"additionalProperties":false,"properties":{"created_at":{"format":"date-time","type":["string","null"]},"id":{"type":"integer"},"status":{"enum":["backlog","in_progress","completed"],"type":"string"},"status_changed_at":{"format":"date-time","type":["string","null"]},"subject":{"type":"string"},"updated_at":{"format":"date-time","type":["string","null"]}},"required":["id","subject","status","status_changed_at","created_at","updated_at"],"type":"object"},"RFIUpdateRequest":{"additionalProperties":false,"properties":{"status":{"description":"backlog | in_progress | completed","type":"string"},"subject":{"type":"string"}},"type":"object"}}},"info":{"description":"API for managing key terrain project status, battle rhythm events, RFIs, change requests, CCIRs, the Incident Tracker, and related dashboard data. Incident rows are exposed at /api/incident-tracker-entries.","title":"Incident Response COP API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/api/battle-rhythm":{"get":{"operationId":"listBattleRhythmItems","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BattleRhythmItem"},"type":"array"}}},"description":"Battle rhythm event list"}},"summary":"List battle rhythm events (military-time sorted)"},"post":{"operationId":"createBattleRhythmItem","requestBody":{"content":{"application/json":{"example":{"event_time":"1430","title":"Commander Update"},"schema":{"$ref":"#/components/schemas/BattleRhythmCreateRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BattleRhythmItem"}}},"description":"Event created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Invalid request"}},"summary":"Create battle rhythm event"}},"/api/battle-rhythm/{item_id}":{"delete":{"operationId":"deleteBattleRhythmItem","responses":{"204":{"description":"Deleted"},"404":{"description":"Event not found"}},"summary":"Delete battle rhythm event"},"get":{"operationId":"getBattleRhythmItem","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BattleRhythmItem"}}},"description":"Event record"},"404":{"description":"Event not found"}},"summary":"Get battle rhythm event by id"},"parameters":[{"in":"path","name":"item_id","required":true,"schema":{"type":"integer"}}],"patch":{"operationId":"updateBattleRhythmItem","requestBody":{"content":{"application/json":{"example":{"event_time":"1600","title":"Commander Update"},"schema":{"$ref":"#/components/schemas/BattleRhythmUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BattleRhythmItem"}}},"description":"Updated event"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Invalid request"},"404":{"description":"Event not found"}},"summary":"Update battle rhythm event"},"put":{"operationId":"replaceBattleRhythmItem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BattleRhythmUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BattleRhythmItem"}}},"description":"Updated event"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Invalid request"},"404":{"description":"Event not found"}},"summary":"Replace/update battle rhythm event"}},"/api/change-requests":{"get":{"operationId":"listChangeRequests","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ChangeRequestItem"},"type":"array"}}},"description":"Change request list"}},"summary":"List change request items"},"post":{"operationId":"createChangeRequest","requestBody":{"content":{"application/json":{"example":{"status":"backlog","subject":"Open port 443 on segment DMZ-2"},"schema":{"$ref":"#/components/schemas/ChangeRequestCreateRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeRequestItem"}}},"description":"Created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Invalid request"}},"summary":"Create change request"}},"/api/change-requests/{cr_id}":{"delete":{"operationId":"deleteChangeRequest","responses":{"204":{"description":"Deleted"},"404":{"description":"Not found"}},"summary":"Delete change request"},"get":{"operationId":"getChangeRequest","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeRequestItem"}}},"description":"Change request record"},"404":{"description":"Not found"}},"summary":"Get change request by id"},"parameters":[{"in":"path","name":"cr_id","required":true,"schema":{"type":"integer"}}],"patch":{"operationId":"updateChangeRequest","requestBody":{"content":{"application/json":{"example":{"status":"in_progress"},"schema":{"$ref":"#/components/schemas/ChangeRequestUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeRequestItem"}}},"description":"Updated"},"404":{"description":"Not found"}},"summary":"Update change request"},"put":{"operationId":"replaceChangeRequest","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeRequestUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeRequestItem"}}},"description":"Updated"},"404":{"description":"Not found"}},"summary":"Replace/update change request"}},"/api/health":{"get":{"operationId":"healthCheck","responses":{"200":{"content":{"application/json":{"example":{"status":"ok"},"schema":{"$ref":"#/components/schemas/HealthResponse"}}},"description":"Service is healthy"}},"summary":"Health check"}},"/api/incident-tracker-entries":{"get":{"description":"Returns all incidents sorted by status (in_progress, backlog, completed), then by recorded_at descending within each status group.","operationId":"listIncidentTrackerEntries","responses":{"200":{"content":{"application/json":{"example":[{"created_at":"2026-05-08T18:31:00","details":"Containment in progress; MFA reset queued.","id":1,"incident_name":"Phishing \u2014 exec mailbox","recorded_at":"2026-05-08T18:30:00","status":"in_progress","team_assigned":"IRT-Alpha","updated_at":"2026-05-08T18:31:00"}],"schema":{"items":{"$ref":"#/components/schemas/IncidentTrackerEntry"},"type":"array"}}},"description":"Incident rows in display order"}},"summary":"List incident tracker entries","tags":["Incidents"]},"post":{"description":"Requires incident_name and team_assigned. Optional recorded_at (ISO-8601; timezone-aware values converted to UTC; naive values interpreted as US/Eastern). Optional status defaults to backlog; accepts backlog, in_progress, completed, and common aliases (e.g. in progress, complete).","operationId":"createIncidentTrackerEntry","requestBody":{"content":{"application/json":{"example":{"details":"Isolated host; EDR sweep running.","incident_name":"Ransomware precursors on DC-Auth","recorded_at":"2026-05-08T18:30:00-04:00","status":"in_progress","team_assigned":"IRT-Bravo"},"schema":{"$ref":"#/components/schemas/IncidentTrackerEntryCreateRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"example":{"created_at":"2026-05-08T22:31:00","details":"Isolated host; EDR sweep running.","id":42,"incident_name":"Ransomware precursors on DC-Auth","recorded_at":"2026-05-08T22:30:00","status":"in_progress","team_assigned":"IRT-Bravo","updated_at":"2026-05-08T22:31:00"},"schema":{"$ref":"#/components/schemas/IncidentTrackerEntry"}}},"description":"Created incident row"},"400":{"content":{"application/json":{"example":{"error":"incident_name and team_assigned are required"},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing required fields"}},"summary":"Create incident tracker entry","tags":["Incidents"]}},"/api/incident-tracker-entries/{entry_id}":{"delete":{"description":"Permanently removes one incident row by id.","operationId":"deleteIncidentTrackerEntry","responses":{"204":{"description":"Deleted"},"404":{"description":"Not found"},"500":{"content":{"application/json":{"example":{"error":"failed to delete entry"},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Server error"}},"summary":"Delete incident tracker entry","tags":["Incidents"]},"parameters":[{"description":"Incident tracker row id","in":"path","name":"entry_id","required":true,"schema":{"type":"integer"}}],"patch":{"description":"JSON body may include any subset of fields. Omitted keys are left unchanged. At least one field should be present; an empty object is a no-op.","operationId":"patchIncidentTrackerEntry","requestBody":{"content":{"application/json":{"example":{"details":"Containment verified.","status":"completed"},"schema":{"$ref":"#/components/schemas/IncidentTrackerEntryUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IncidentTrackerEntry"}}},"description":"Updated incident row"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Invalid field value"},"404":{"description":"Not found"},"500":{"content":{"application/json":{"example":{"error":"failed to update entry"},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Server error"}},"summary":"Update incident tracker entry (partial)","tags":["Incidents"]},"put":{"description":"Same request body and behavior as PATCH for this resource.","operationId":"putIncidentTrackerEntry","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IncidentTrackerEntryUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IncidentTrackerEntry"}}},"description":"Updated incident row"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Invalid field value"},"404":{"description":"Not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Server error"}},"summary":"Update incident tracker entry (same semantics as PATCH)","tags":["Incidents"]}},"/api/openapi.json":{"get":{"operationId":"getOpenApiSpec","responses":{"200":{"content":{"application/json":{"example":{"info":{"title":"Incident Response COP API","version":"1.0.0"},"openapi":"3.1.0"},"schema":{"type":"object"}}},"description":"OpenAPI specification"}},"summary":"Machine-readable OpenAPI 3.1 contract"}},"/api/projects":{"get":{"operationId":"listProjects","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Project"},"type":"array"}}},"description":"Project list"}},"summary":"List all projects"},"post":{"operationId":"createProject","requestBody":{"content":{"application/json":{"example":{"description":"Milestone 2 deployment prep.","name":"Customer Portal","owner":"Team A","status":"in_progress"},"schema":{"$ref":"#/components/schemas/ProjectCreateRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}},"description":"Project created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Invalid request"}},"summary":"Create project"}},"/api/projects/{project_id}":{"delete":{"operationId":"deleteProject","responses":{"204":{"description":"Deleted"},"404":{"description":"Project not found"}},"summary":"Delete project"},"get":{"operationId":"getProject","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}},"description":"Project record"},"404":{"description":"Project not found"}},"summary":"Get project by id"},"parameters":[{"in":"path","name":"project_id","required":true,"schema":{"type":"integer"}}],"patch":{"operationId":"updateProject","requestBody":{"content":{"application/json":{"example":{"description":"Restored operations.","status":"green"},"schema":{"$ref":"#/components/schemas/ProjectUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}},"description":"Updated project"},"404":{"description":"Project not found"}},"summary":"Update project fields"},"put":{"operationId":"replaceProject","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}},"description":"Updated project"},"404":{"description":"Project not found"}},"summary":"Replace/update project fields"}},"/api/rfis":{"get":{"operationId":"listRFIs","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/RFIItem"},"type":"array"}}},"description":"RFI list"}},"summary":"List RFI items"},"post":{"operationId":"createRFI","requestBody":{"content":{"application/json":{"example":{"status":"backlog","subject":"Firewall log retention policy"},"schema":{"$ref":"#/components/schemas/RFICreateRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RFIItem"}}},"description":"RFI created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Invalid request"}},"summary":"Create RFI item"}},"/api/rfis/{rfi_id}":{"delete":{"operationId":"deleteRFI","responses":{"204":{"description":"Deleted"},"404":{"description":"RFI not found"}},"summary":"Delete RFI"},"get":{"operationId":"getRFI","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RFIItem"}}},"description":"RFI record"},"404":{"description":"RFI not found"}},"summary":"Get RFI by id"},"parameters":[{"in":"path","name":"rfi_id","required":true,"schema":{"type":"integer"}}],"patch":{"operationId":"updateRFI","requestBody":{"content":{"application/json":{"example":{"status":"in_progress"},"schema":{"$ref":"#/components/schemas/RFIUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RFIItem"}}},"description":"Updated RFI"},"400":{"description":"Invalid request"},"404":{"description":"RFI not found"}},"summary":"Update RFI (status change updates status_changed_at)"},"put":{"operationId":"replaceRFI","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RFIUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RFIItem"}}},"description":"Updated RFI"},"404":{"description":"RFI not found"}},"summary":"Replace/update RFI"}}},"servers":[{"description":"Current deployment","url":"/"}],"tags":[{"description":"Incident Tracker rows on the dashboard. Use GET/POST/DELETE on /api/incident-tracker-entries. Responses use incident_name, status, team_assigned, and details (stored in the action_tracker_entries table). List order is in_progress, then backlog, then completed, then by recorded_at descending. Updates: JSON PATCH or PUT on /api/incident-tracker-entries/{entry_id}, or HTML POST /incident-tracker/{entry_id}/edit.","name":"Incidents"}]}
