GET/api/incidents
인시던트 트리아지 목록(최신순, 페이징).
GET /api/incidents?projectId=X&limit=&offset= — 인시던트 트리아지 목록(최신순, 페이징).
DELETE /api/incidents body {projectId, ids:[]} — 선택 인시던트 일괄 soft-delete(+하위 subtask).
type='incident' 만. incident_details 조인.
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X GET https://sit.in-bridge.com/api/incidents \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
DELETE/api/incidents
인시던트 트리아지 목록(최신순, 페이징).
GET /api/incidents?projectId=X&limit=&offset= — 인시던트 트리아지 목록(최신순, 페이징).
DELETE /api/incidents body {projectId, ids:[]} — 선택 인시던트 일괄 soft-delete(+하위 subtask).
type='incident' 만. incident_details 조인.
Request body
projectIdstring<uuid>required
Responses
200 성공400 입력 검증 실패(zod)401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X DELETE https://sit.in-bridge.com/api/incidents \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"projectId":"00000000-0000-0000-0000-000000000000","ids":"00000000-0000-0000-0000-000000000000"}'
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents", {
method: "DELETE",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"projectId": "00000000-0000-0000-0000-000000000000",
"ids": "00000000-0000-0000-0000-000000000000"
})
});
const data = await res.json();
GET/api/incidents/{key}
인시던트 상세.
GET /api/incidents/[key]?projectId=X — 인시던트 상세.
티켓 본체 + incident_details(SXE 소스 incident_uid/analysis_id 포함) +
대화/시스템 코멘트(타임라인) + spawned subtask. 처리화면 탭들의 데이터.
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X GET https://sit.in-bridge.com/api/incidents/{key} \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents/{key}", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
GET/api/incidents/{key}/agent-state
워커 그래프 상태 스냅샷.
GET /api/incidents/[key]/agent-state?projectId=X — 워커 그래프 상태 스냅샷.
UI 가 인시던트를 열 때 "이미 AI 처리가 시작됐는지/승인 대기 중인지/종결됐는지"를
알아 적절한 버튼(처리 시작 vs 승인/거부 vs 종결됨)을 그리기 위해 사용.
워커 미가동 시 503 대신 비어있는 상태로 degrade(처리 시작 버튼만 노출).
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X GET https://sit.in-bridge.com/api/incidents/{key}/agent-state \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents/{key}/agent-state", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
POST/api/incidents/{key}/assign
SOC 담당자 자동 추천(+선택 적용).
POST /api/incidents/[key]/assign?projectId=X — SOC 담당자 자동 추천(+선택 적용).
body: { apply?: boolean } (기본 false = 추천만)
- apply=false: AI/규칙으로 역할·담당자만 추천해서 반환(미적용).
- apply=true : 추천 담당자를 이슈에 배정 + 활동 로그 + 담당자에게 알림.
판정/심각도는 SXE 가 정한 incident_details 값을 그대로 입력으로만 쓴다(재산정 X).
Responses
200 성공400 입력 검증 실패(zod)401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X POST https://sit.in-bridge.com/api/incidents/{key}/assign \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"apply":true}'
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents/{key}/assign", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"apply": true
})
});
const data = await res.json();
POST/api/incidents/{key}/decision
HITL 사람 결정을 DB 에 기록(워커 실행 없이 DB-only).
POST /api/incidents/[key]/decision?projectId=X — HITL 사람 결정을 DB 에 기록(워커 실행 없이 DB-only).
body: { decision: "approve"|"reject"|"escalate"|"resolve"|"hold", note?: string }
배경(왜 DB-only): 실제 대응 실행은 추후 **고객별 SOAR 정책 배치**가 이 DB 결정을 감시(polling)해서
수행한다. 여기(SoR)는 사람 게이트의 결정만 영속·감사한다. 그래서 워커/실행을 호출하지 않고:
1) 결정을 타임라인 코멘트로 남긴다 — authorType=human + authorMeta={decision,note,source:"sentivex-hitl"}
(배치가 `author_meta->>'decision'` 으로 미처리 결정을 쿼리·고객별 정책 적용하기 좋게 구조화).
2) resolve → 보드 상태를 done 카테고리로(처리완료). escalate → 우선순위 1단계 상향(최소 1=긴급).
3) 활동 로그.
주의: 점수 재산정 없음. 판정/심각도는 SXE 값 그대로. drizzle-kit push 금지(이 라우트는 기존 컬럼만 사용).
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X POST https://sit.in-bridge.com/api/incidents/{key}/decision \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents/{key}/decision", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
POST/api/incidents/{key}/process
LangGraph 워커로 AI 처리 시작.
POST /api/incidents/[key]/process?projectId=X — LangGraph 워커로 AI 처리 시작.
워커가 intake→classify→retrieve(RAG)→propose 까지 달리고 await_approval(HITL)에서
멈춘다. 그 시점의 제안(대응안/담당자/일정)을 그대로 반환 → UI 가 승인/거부 화면을 띄움.
실제 적용(담당자/단계/일정)은 사람이 /resume 로 승인해야 일어난다.
판정/심각도는 SXE 값을 입력으로만 쓴다(재산정 X). 워커 미가동 시 503.
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X POST https://sit.in-bridge.com/api/incidents/{key}/process \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents/{key}/process", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
POST/api/incidents/{key}/resume
HITL 승인/거부로 워커 재개.
POST /api/incidents/[key]/resume?projectId=X — HITL 승인/거부로 워커 재개.
body: { approved: boolean, note?: string }
- approved=true → 워커가 execute(담당자/단계/일정 적용)→verify(stub)→close.
- approved=false → 바로 close(거부 기록).
워커는 같은 Postgres(SoR)에 담당자/단계/일정 + system 코멘트를 직접 쓴다. 이 라우트는
재개 호출 후 활동 로그를 남기고 프로젝트 SSE 를 발행해 목록/드로어가 즉시 갱신되게 한다.
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X POST https://sit.in-bridge.com/api/incidents/{key}/resume \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents/{key}/resume", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
GET/api/incidents/{key}/trace
인시던트 처리 과정 트레이스 조회 (관측성 #208).
GET /api/incidents/[key]/trace?projectId=X — 인시던트 처리 과정 트레이스 조회 (관측성 #208).
목적(왜): "알림왔다/티켓왔다"가 아니라, 워커 각 노드가 어떤 데이터에 어떤 프롬프트로 어떤 결정을
했는지(입력·프롬프트·LLM응답·결정·타이밍)를 노드별로 돌려준다 → 오류 규명·품질 향상(사용자 지시).
워커(worker/nodes.py)가 worker_traces 에 best-effort 로 적재한 것을 읽는다.
인증: requireProjectAccess(READ) — 감독(super)도 조회 가능(쓰기 아님, #206 과 정합). 테넌트 스코프로
남테넌트 인시던트 트레이스는 403(projectVisibleForTenant).
데이터소스: worker_traces (raw SQL — worker_jobs 처럼 drizzle push 범위 밖 테이블).
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X GET https://sit.in-bridge.com/api/incidents/{key}/trace \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents/{key}/trace", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
POST/api/incidents/{key}/unisolate
격리 해제(undo) 요청 (H9).
POST /api/incidents/[key]/unisolate?projectId=X — 격리 해제(undo) 요청 (H9).
오격리(false-positive containment)된 인시던트의 영향 호스트를 LangGraph 워커를 통해
벤더 API(CrowdStrike lift_containment / SentinelOne connect)로 복구한다. 워커는 기본
dry-run 이며(REMEDIATION_LIVE + 자격증명일 때만 실호출), 결과를 타임라인 system 코멘트로
남긴다. 파괴적 조치의 역연산이므로 프로젝트 접근 권한을 강제하고, 활동 로그 + SSE 로
목록/드로어를 동기화한다. 워커 미가동 시 503.
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X POST https://sit.in-bridge.com/api/incidents/{key}/unisolate \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents/{key}/unisolate", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
GET/api/incidents/agent-queue
인시던트 AI 처리 큐 — 백로그를 워커에 한꺼번에 쏟지 않고 제한 동시성으로 흘린다.
인시던트 AI 처리 큐 — 백로그를 워커에 한꺼번에 쏟지 않고 제한 동시성으로 흘린다.
POST /api/incidents/agent-queue?projectId=X body: { keys?: string[] }
- keys 지정: 그 인시던트들을 enqueue.
- keys 생략: 미종결(backlog/todo/in_progress) 인시던트 전부 enqueue(워커가 중복 스킵).
각 잡은 await_approval 까지만 달리고, 이후 사람이 HITL UI 에서 개별 승인.
GET /api/incidents/agent-queue?projectId=X → 큐 스냅샷(동시성/상태 카운트/건별).
워커 미가동 시 503. 판정/심각도 재산정은 하지 않는다.
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X GET https://sit.in-bridge.com/api/incidents/agent-queue \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents/agent-queue", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
POST/api/incidents/agent-queue
인시던트 AI 처리 큐 — 백로그를 워커에 한꺼번에 쏟지 않고 제한 동시성으로 흘린다.
인시던트 AI 처리 큐 — 백로그를 워커에 한꺼번에 쏟지 않고 제한 동시성으로 흘린다.
POST /api/incidents/agent-queue?projectId=X body: { keys?: string[] }
- keys 지정: 그 인시던트들을 enqueue.
- keys 생략: 미종결(backlog/todo/in_progress) 인시던트 전부 enqueue(워커가 중복 스킵).
각 잡은 await_approval 까지만 달리고, 이후 사람이 HITL UI 에서 개별 승인.
GET /api/incidents/agent-queue?projectId=X → 큐 스냅샷(동시성/상태 카운트/건별).
워커 미가동 시 503. 판정/심각도 재산정은 하지 않는다.
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X POST https://sit.in-bridge.com/api/incidents/agent-queue \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents/agent-queue", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
POST/api/incidents/auto-process
인시던트 일괄 자동 처리.
POST /api/incidents/auto-process?projectId=X — 인시던트 일괄 자동 처리.
"처리할 수 있는 건 처리해 단계를 높이고, 처리 불가능할 것 같은 건 알아서" + 간트 표시.
각 미종결 인시던트에 대해:
1) 담당자 배정 — severity/verdict → SOC 역할(IR/L2/L1) 매핑 + 역할 내 부하 최저 멤버.
2) 단계 격상 — 처리 가능: To Do → In Progress.
처리 불가능할 것 같은(malicious + hitl_gated, 사람 전담): → In Review(사람 승인 대기 플래그).
3) 간트 표시 — start_date=occurred_at, due_date=sla_due_at (SLA 막대).
이미 done/cancelled 인 건은 건드리지 않는다. 판정/심각도는 SXE 값 그대로(재산정 X).
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X POST https://sit.in-bridge.com/api/incidents/auto-process \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents/auto-process", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
GET/api/incidents/sla-policy
SLA 처리시간(시간) 정책 매트릭스를 데이터로 반환.
GET /api/incidents/sla-policy — SLA 처리시간(시간) 정책 매트릭스를 데이터로 반환.
목적(왜 존재): sentivex 등 소비자가 SLA 시간표를 severity 단축으로 하드코딩 미러링하다 verdict 축을
빠뜨려 비-malicious 에서 값이 틀리는 드리프트를 없앤다(버스 #1067 F-2). 정책의 단일 진실원본은
`src/lib/incidents/routing.ts` 의 `SLA_POLICY_RULES` 이고, 이 라우트는 그 배열을 그대로 내려준다 —
소비자는 하드코딩 대신 이 응답을 받아쓴다(DEV-STANDARDS §9 카탈로그 주도, 하드코딩 미러 금지).
데이터소스: routing.ts 상수(순수, DB·네트워크 없음). 프로젝트/테넌트 무관(전역 정책값).
인증: requireUser — pm-session 교환토큰(OPEN 모드 포함) 소비. 점수/등급 아님(SLA 시간은 운영 정책값).
반환: {
rules: [{ verdict|null, severity|null, hours }], // 위→아래 첫 매칭이 이김(특정→일반)
defaultHours, // 어느 규칙에도 안 걸릴 때(hours)
clockStart: "occurredAt", // SLA 시계 시작 기준
note // 소비 안내(첫 매칭 규칙)
}.
주의: verdict 는 소문자 비교, severity 는 원문(Critical/High/Medium/Low). null=와일드카드.
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X GET https://sit.in-bridge.com/api/incidents/sla-policy \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents/sla-policy", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
GET/api/incidents/summary
보안(SOC) 프로젝트용 요약 집계.
GET /api/incidents/summary?projectId=X — 보안(SOC) 프로젝트용 요약 집계.
일반 PM 요약(전체/진행중/완료/백로그)과 달리 보안 분석가 관점:
미해결 인시던트의 심각도·판정·라우팅 티어 분포, SLA(위반/임박/정상), 미배정,
역할별 부하, 최근 인시던트.
isSoc = soc_roles 가 정의됐거나 인시던트가 존재하는 프로젝트 → 요약 화면이 SOC 뷰로 분기.
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X GET https://sit.in-bridge.com/api/incidents/summary \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/incidents/summary", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();