✅ 승인 (2026-05-06)
자동님 한 줄 회신 verbatim: "오키 좋다. 지금 승인하고 문서화하면 얘네가 보면될거같아"
- v3 설계 (24h 큐 + TM 1차 + 자동님 2차 + V12 + T11 + Phase P3) 전체 confirmed
- 대시보드 에이전트(Codex/병렬 Claude)들이 §12 룰 + §10 API 명세 + §09 마이그레이션 따라 P3 진입 가능
- in-repo 작업 entry docs/keeper-out/anti-abuse/06-p3-implementation-brief.md 신설
- 미해결 1건 (T+20~24h 정책)은 P3 구현 진행 중 자동님 결정 — 기본값 (c) 하이브리드 추천
★ v2 → v3 핵심 변경 (자동님 2026-05-05 23:30 결정)
- 자동 즉시 발송 폐기 → 24시간 내 휴먼 승인 큐 — 해커 침투 시 잔액 0 만들기 차단의 근본 대책
- TM 콜센터(1차) + 자동님 admin(2차) fallback — TM이 영업 routine 콜로 ①③룰 자연 검증 후 발송 승인
- 새 검증 차원 V12 — 휴먼 승인 추가 (V1~V11 기존 차원과 직교)
- 새 위협 T11 — 해커 침투 추가 (잔액 0 만들기 / API key leak / sim swap)
- 새 Phase P3 — 휴먼 승인 시스템 통합 (P1.5/P2 위에 별도 layer)
- 대시보드 에이전트 작업 가능 디테일 — 마이그레이션·API 명세·UI·룰·커밋 단위까지 누락 zero
01.한 줄 요약 (v3)
1만원 N페이는 자동 즉시 발송 X → 24시간 내 휴먼 승인 후 발송으로 바뀐다.
영업원 출장 종료 시점에 진단은 pending_review 상태로 큐 진입,
TM(콜센터)가 사장님께 routine follow-up 콜을 거치며 통화 결과를 admin에 입력하면
그 시점에 [💸 발송 확인] 버튼 활성화되고 클릭 → Giftishow 발송.
TM이 6시간 안에 처리 못한 건은 자동님 admin pending 큐로 escalate되어 일괄 검토.
공격 발생 시 자동님이 [ALL HOLD] 1클릭으로 모든 발송 일시 정지.
이 방식은 키퍼메이트의 ①룰(연락 가능)·③룰(도입 의향)을 콜로 자연 검증하면서,
해커 침투·잔액 노출 시 인간이 끼어들 시간을 확보한다.
02.왜 휴먼 승인이 필요한가 — 해커 위협 모델 갱신
v1·v2까지의 위협 모델은 어뷰징 위주(사례비 헌터, 봇, 영업원 본인 어뷰징). 어뷰징은 단가 높고 빈도 낮아 자동 게이트로 충분.
⚠ T11 — 해커 침투 (v3 신규)
특성: 어뷰징과 다른 클래스. 단가는 작지만(건당 1만원) 빈도가 폭발적(분당 N건). API key leak·관리자 세션 탈취·SIM swap 시:
- 잔액 100만원이 1분 만에 0이 됨 — 100건 발송이면 끝
- 모두 공격자 통제 폰으로 빠짐 (피해 회수 사실상 불가)
- 알아챘을 때는 이미 늦음 — Giftishow 환불 불가
★ 자동 게이트의 한계
- 자동 게이트는 "알려진 위협 룰"만 막음 — 해커는 모든 룰을 통과하는 새 페턴을 만들 수 있음 (예: 정상 매장 정보 가지고 본인 폰으로만 빼돌리기)
- 비정상 패턴 탐지 cron은 "사후" — 야간 1회 cron이면 이미 12h 동안 잔액 빼간 후
- 휴먼은 "느린 승인" 자체가 방어 — 분당 N건 폭증을 사람이 본다면 즉시 멈출 수 있음.
"이거 왜 한 시간에 50건이지?" 한 줄이 자동 룰보다 빠르고 정확
위협 모델 v3 — Actor × Scenario 갱신
| ID | Actor | 시나리오 | v1·v2 차단 | v3 차단 |
| T1 | 외부 사례비 헌터 | 폰 N개로 무한 제출 | V3·V4 | + V12 휴먼 승인 |
| T2 | 외부 봇/스크립트 | API 자동 POST | V3·V5 | + V12 휴먼 승인 |
| T3 | 영업원 본인 어뷰징 | 본인/지인 폰 5대 | V3·V7 | + V12 (TM이 모르는 폰 콜 시 발견) |
| T4·T5 | 실수/사장 어뷰징 | 중복 | V4 | + V12 |
| T6·T7 | 매장 미방문/거짓 데이터 | V2·V6 | V2·V6 | + V12 (TM 콜 안 받음) |
| T9·T10 | 잔액 cross-bleed/사일런트 실패 | V5 | V5 + 승인 직전 재확인 | 강화 |
| T11 ★ 신규 | 해커 침투 | API leak로 잔액 빼내기 | ❌ 막을 수 없음 | ✅ V12 휴먼 승인 + ALL HOLD |
03.24h 큐 — 정확한 timeline + 정책
T0 (영업원 출장 종료 + 사장님 진단 완료)
큐 진입
모든 게이트(V1 OTP, V2 GPS, V3 영업원 코드, V4 unique, V5 잔액, V8 place 실재성) 통과.
leads INSERT, payouts.naver_pay_10k status='pending_review', funnel_event 'review.queued'.
사장님께 안내 SMS 발송 (다음 §05 참조).
T+0 ~ T+6h (TM 콜센터 routine)
1차 승인 — TM follow-up 콜
TM이 admin /keeper-out/admin/pending 큐 카드 보고 사장님께 콜.
통화 outcome 입력 → outcome 'confirmed' 시 [💸 발송 확인] 버튼 활성화 → Giftishow 호출.
T+6h ~ T+20h (TM 미처리 건 escalate)
자동님 admin fallback 큐
6h 안에 TM이 outcome 입력 못한 건은 자동님 admin pending 큐로 자동 이동.
자동님이 일괄 검토 + 일괄 [✓ 승인] 또는 [⨯ 거부].
T+20h ~ T+24h (24h 임박)
최종 처리
자동님 텔레그램 alarm — "X건 24h 임박, 미승인". 자동님이 마지막 처리.
20~24h 미승인 건 정책 (자동님 결정 대기):
(a) 자동 dispatch fallback — 다른 게이트 모두 통과 + 패턴 정상 시 자동 발송
(b) 사장님께 "추가 확인 필요" 안내 + 영업원 재방문 요청
T+24h+ (사후 검수 dual-layer 시작)
영업원 30k 인센 회수 윈도우
v2 §07의 dual-layer. GET /v1/account/admin/users?phone={leads.phone} →
②룰(가입) + ③룰(subscribes) 통과 시 payouts.lead_30k status='confirmed'.
미통과 시 영업원 invalid률 누적 + 7일/30일 후 final invalidate.
큐 cap (해커 차단)
| cap 종류 | 임계값 | 위반 시 |
| 분당 발송 | ≥ 10건 | 자동 ALL HOLD + 자동님 텔레그램 즉시 alarm |
| 시간당 승인 | ≥ 30건 | 승인 일시정지 30분 + admin alarm |
| 일별 승인 | ≥ 200건 | 당일 승인 차단 (영업원 7명 × 25/일 = 175 base + buffer) |
| 잔액 임계 | < ₩50,000 | 승인 차단 + 자동님 충전 요청 |
| 같은 IP 1h | ≥ 5건 | 해당 IP 진단 신규 진입 차단 |
| 같은 agent 24h | invalid률 > 30% | agent.active=false 자동 + admin alarm |
04.휴먼 승인 흐름 — 누가 / 어떻게 / 어디서
1차 승인자 — TM 콜센터 (이서경 등 발레리온 직원)
왜 TM이 1차인가 — 마찰 0인 이유 3개
- 키퍼메이트 ①룰(연락 가능) + ③룰(도입 의향)을 콜이 자동 검증 — 어차피 진단 후 follow-up 콜은 영업 routine. 새 단계 추가 없이 기존 동작에 승인 액션을 묶음
- TM은 영업 인센과 분리 — agent_2 콜센터 인센은 설치 완료 시 install_50k에서 15k. 1만원 발송과 무관 → 콜 outcome 위조 인센티브 없음. 영업원 자기승인 모순(T3) 차단
- 사장님 신뢰 ↑ — "본사 직접 확인 콜 후 N페이 입금"이 즉시 입금보다 신뢰감 큼. 진단 직후 안내 SMS에 "본사 콜 후 24h 내" 명시하면 사장님 기대치 정렬
2차 승인자 — 자동님 admin fallback (TM 6h 미처리 건)
TM이 처리 못한 건은 자동님 admin /keeper-out/admin/fallback 큐로 escalate. 자동님은 일괄 [✓ 모두 승인] 또는 카드별 검토. 자동님 부담 절감 — TM 정상 운영 시 fallback 큐는 거의 비어있어야 함.
거부 사유 enum (TM·admin 공통)
| outcome 값 | 의미 | 다음 액션 |
confirmed | 사장님 본인 + 진단 인지 + 도입 의향 | [💸 발송 확인] 버튼 활성화 |
no_answer | 3회 콜 무응답 | 4시간 후 재시도 큐 / 자동님 escalate |
not_owner | 본인 매장 아님 / 진단 모름 | 거부 + 영업원 alarm + V7 fraud_signal 'wrong_owner' |
rejected | 관심 없음 명시 + 발송 거부 | 거부 + payouts invalid + 영업원 invalid률 누적 |
fraud_suspect | 의심스러운 응답 패턴 | 거부 + V7 fraud_signal 'tm_suspect' + 자동님 alarm |
wrong_phone | 틀린 폰번호 / 전화 불통 | 거부 + 영업원 데이터 입력 오류 alarm |
05.UX — 사장님 / TM / 자동님 / ALL HOLD
5-1. 사장님 진단 종료 화면
사장님 — /diagnose 종료 직후
✅ 진단 완료!
매장 보안 진단에 협조해 주셔서 감사합니다.
📞 본사 콜센터에서 곧 연락드릴 예정이에요
"진단 잘 받으셨어요?" 짧은 확인 통화입니다
💸 1만원 N페이 (입력하신 010-XXXX-XXXX번호)
본사 확인 콜 종료 후 24시간 내 발송됩니다
🔒 보안을 위해 자동 즉시 발송이 아닌 본사 확인 후
발송하는 정책으로 운영하고 있어요
[확인]
5-2. 사장님 안내 SMS (T0 직후 발송)
사장님 폰 SMS — balerion.cloud SMS proxy
[키퍼] 진단 협조 감사합니다.
본사 콜센터 확인 후 24시간 내
1만원 네이버페이를 입력하신 폰으로
보내드릴 예정이에요.
문의: 1670-5772
[수신거부 080-XXX-XXXX]
5-3. TM 화면 — admin /keeper-out/admin/pending
TM (이서경 등 발레리온 콜센터)
📋 1만원 발송 대기 큐 — 24h 내 처리 (45건)
─────────────────────────────────────────
☐ 이정희 사장님 · 아지트 (구리시)
📞 010-3111-1165 · 영업원 K9bR2(원희연)
진단 시작 1h 23m 전 · 카메라 5대 ₩395k/월
pain: 출동지연·앱불편
[📞 콜 시작] [📋 사장님 진단 미리보기]
─────────────────────────────────────────
☐ 송진학 사장님 · 돈락 (대전)
📞 010-6427-8756 · 영업원 m2K9b(조세종)
진단 5h 12m 전 · ⚠ 5h 경과 (1h 후 escalate)
[📞 콜 시작] [📋 미리보기]
─────────────────────────────────────────
[💸 일괄 발송 확인 (1건 선택됨)]
[ALL HOLD ⏸] (관리자만)
5-4. TM 콜 outcome 입력 모달
TM — 통화 종료 후
📞 통화 결과 입력 — 이정희 사장님
콜 진행 시간: 2분 14초
통화 결과:
⦿ confirmed — 사장님 본인 + 진단 인지 + 도입 의향
○ no_answer — 3회 무응답
○ not_owner — 본인 매장 아님
○ rejected — 관심 없음
○ fraud_suspect — 의심
○ wrong_phone — 틀린 번호
메모 (50자):
[ "통화 완료, 견적 안내 받기 OK" __________ ]
[💸 1만원 발송 확인] [취소]
5-5. 자동님 admin fallback 화면
자동님 (또는 PM) — /keeper-out/admin/fallback
📋 자동님 fallback 큐 — TM 6h 미처리 (12건)
─────────────────────────────────────────
☑ 김수지 사장님 · 스튜디오수지 (김해)
📞 010-3018-0254 · 영업원 K9bR2(원희연)
T+8h 12m 경과 · TM 무응답 3회
─────────────────────────────────────────
☑ 채정연 사장님 · 스마일 (대구)
📞 010-6864-1296 · 영업원 m2K9b(조세종)
T+10h 03m 경과 · TM 무응답 3회
─────────────────────────────────────────
☐ 강홍원 사장님 · 빅파이브 먹골역점 (서울)
📞 010-5000-8858 · ⚠ V7 패턴 alarm: 같은 IP 3건
─────────────────────────────────────────
[✓ 선택 2건 일괄 승인] [⨯ 선택 거부]
[ALL HOLD ⏸ — 발송 일시 정지]
5-6. ALL HOLD — 비상 정지 메커니즘
⏸ ALL HOLD 정확한 동작
- 자동님 또는 PM 권한자만 호출 가능 (admin 권한 체크)
- 모든
pending_review + 신규 게이트 통과 진단의 발송 차단 (큐 진입은 막지 않음)
- 해제까지
fraud_signal('all_hold') 기록 + 자동님 텔레그램 alarm
- 해제 시 큐 그대로 살아있음 → 패턴 검증 후 일괄 처리
- cap 위반(분당 ≥10) 시 자동 ALL HOLD 발동
06.검증 차원 V1~V12 (V12 휴먼 승인 신설)
| V | 이름 | 막는 위협 | 티어 |
| V1 | 본인성 — SMS OTP | T1·T2·T3·T5 | MUST |
| V2 | 현장성 #1 — GPS × place ≤200m | T2·T4·T6 | MUST |
| V3 | 영업원 이름+5자리 코드 (지원금 받기 클릭 후) | T3·T6·T7 | MUST |
| V4 | 유일성 — naver_place_id NOT NULL + phone unique | T5·T8 | MUST |
| V5 | 예산 — 잔액 preflight + cap (승인 직전 재확인) | T2·T9·T10 | MUST |
| V6 | 사진 EXIF 시간 검증 | T6·T7 | SHOULD |
| V7 | 패턴 이상치 탐지 cron + admin alarm | T3·T7 누적·T11 | SHOULD |
| V8 | 매장 실재성 외부 검증 | T6·T8 | SHOULD |
| V9 | 디바이스/IP 핑거프린팅 (기록만) | T2·T11 | NICE |
| V10 | CAPTCHA / Turnstile | T2 | OUT (인바운드만) |
| V11 | 사업자번호 NICE/홈택스 | T6 | OUT |
| V12 ★ v3 신규 | 휴먼 승인 (TM 1차 + 자동님 2차) | T11 + 모든 위협 일괄 차단 | MUST |
★ V12가 다른 차원과 직교한 이유
- V1~V11은 알려진 패턴을 룰로 막음 — 새 패턴은 통과
- V12는 "왜 갑자기 50건이지?"의 사람 직관 — 룰 없이 막음
- V1~V11이 fail-open(룰 미정의 시 통과)인 반면 V12는 fail-closed(승인 없으면 차단)
- V12 단독으로는 모든 위협 막지만 평상시 운영 비용(TM 인력)이 듦 → V1~V11과 함께 박아 TM 부담 최소화
07.3-layer 통합 스키마 (v2 유지)
┌─────────────────────────────────────────────────────────────────────────────┐
│ L1 — keeper-out 자체 DB Supabase hvcrbdpjxfszhwrtcygq │
│ keeper_outbound_leads │
│ pain_points · photo_urls · agent_1/2/3 FK · place_id · owner_id │
│ payout_status = pending_review/pending_dispatch/sent/invalid ★ v3 │
│ tm_outcome · tm_called_at · approved_by · approved_at ★ v3 신규 │
│ │ │
│ ├──→ agents · payouts · funnel_events │
│ ├──→ visit_logs · places · owners (P2) │
│ └──→ otp_sessions · fraud_signals · approval_queue (P3) │
└──────────────┬──────────────────────────────────────────────────────────────┘
│ phone match (사후 검수 ②③룰)
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ L2 — 키퍼 admin api-account.keeperplatform.com 권한 ceo@bizdirect │
│ User OWNER (4136명) → mobile · subscribes · shopName │
│ Order (42건) → status (접수→일시확정→완료) │
└──────────────┬──────────────────────────────────────────────────────────────┘
│ ?ref={code} URL attribution
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ L3 — Referio referio.puzl.co.kr 자동님 코드 86Af9 │
│ Partner · 보상 1.5만+2만 / 4티어 / 어뷰징 정책 차용 │
└─────────────────────────────────────────────────────────────────────────────┘
08.병렬 세션 진척 (P1 박힘)
e8f22edfeat(keeper-out/admin): lead detail view P1 (8섹션)
+654 줄 — LeadDetailClient.tsx + page.tsx + PipelineRow + AppHeader
dc4d9a0feat(keeper-out/admin): lead GET endpoint with PostgREST embed
+47 줄 — lead/[id]/route.ts (GET) + types.ts (LeadDetail)
dbba9d1feat(keeper-out): /diagnose pain_points·pain_note 백엔드
+18 줄 — migration + types.ts + diagnose/route.ts
P1.5/P2 대기, P3(휴먼 승인)가 이번 v3에서 신규.
09.P3 마이그레이션 — 누락 zero
-- supabase/migrations/2026MMDD_keeper_outbound_anti_abuse_v3.sql
-- v3: 휴먼 승인 큐 + 어뷰징 가드 풀 박기
-- ============================================================
-- 1. agents 테이블 — 영업원 코드 + Referio 매핑 + invalid률
-- ============================================================
alter table public.keeper_outbound_agents
add column if not exists code text, -- ★ 5자리 영숫자 mixed case (예: K9bR2)
add column if not exists referio_partner_id text, -- 86Af9 매핑
add column if not exists keeper_partner_id int, -- 향후 키퍼 partners 통합
add column if not exists kakao_user_id text, -- 향후 SSO
add column if not exists invalid_rate_30d numeric default 0,
add column if not exists day_cap_count int default 0,
add column if not exists week_cap_count int default 0,
add column if not exists last_active_at timestamptz;
create unique index if not exists uniq_agent_code
on public.keeper_outbound_agents(code) where code is not null;
-- ============================================================
-- 2. leads 테이블 — V4 강화 + 휴먼 승인 컬럼 + 핑거프린팅
-- ============================================================
alter table public.keeper_outbound_leads
-- V4 유일성
alter column naver_place_id set not null, -- 자동완성 통과 강제
-- 휴먼 승인 트래킹
add column if not exists tm_called_at timestamptz,
add column if not exists tm_outcome text check (tm_outcome in
('confirmed','no_answer','not_owner','rejected','fraud_suspect','wrong_phone')),
add column if not exists tm_note text,
add column if not exists approved_by uuid references auth.users(id),
add column if not exists approved_at timestamptz,
add column if not exists approval_tier int, -- 1=TM, 2=admin fallback, 3=auto-fallback
-- 핑거프린팅 (V9)
add column if not exists ip_address inet,
add column if not exists user_agent text,
-- L2 매칭 캐시 (사후 검수)
add column if not exists external_user_id int,
add column if not exists external_subscribed_at timestamptz,
add column if not exists external_checked_at timestamptz;
-- phone unique lifetime (V4)
create unique index if not exists uniq_leads_phone
on public.keeper_outbound_leads(phone);
-- ============================================================
-- 3. payouts — payout_status 확장
-- ============================================================
alter table public.keeper_outbound_payouts
drop constraint if exists keeper_outbound_payouts_status_check,
add constraint keeper_outbound_payouts_status_check check (status in
('pending','pending_review','pending_dispatch','sent','failed','invalid','on_hold'));
-- ============================================================
-- 4. OTP 세션 (V1)
-- ============================================================
create table if not exists public.keeper_outbound_otp_sessions (
id uuid primary key default gen_random_uuid(),
phone text not null,
code char(6) not null,
expires_at timestamptz not null,
attempts int default 0,
verified_at timestamptz,
ip_address inet,
user_agent text,
created_at timestamptz default now()
);
create index idx_otp_phone_active on public.keeper_outbound_otp_sessions(phone)
where verified_at is null;
-- ============================================================
-- 5. 어뷰징 신호 (V7)
-- ============================================================
create table if not exists public.keeper_outbound_fraud_signals (
id uuid primary key default gen_random_uuid(),
lead_id uuid references public.keeper_outbound_leads(id) on delete cascade,
agent_id uuid references public.keeper_outbound_agents(id),
rule text not null, -- self_referral / unknown_responses /
-- cluster / wrong_owner / tm_suspect / all_hold
severity text check (severity in ('low','med','high')),
payload jsonb,
resolved_at timestamptz,
resolved_by uuid references auth.users(id),
resolution_note text,
flagged_at timestamptz default now()
);
create index idx_fraud_lead on public.keeper_outbound_fraud_signals(lead_id);
create index idx_fraud_unresolved
on public.keeper_outbound_fraud_signals(severity, flagged_at desc)
where resolved_at is null;
-- ============================================================
-- 6. 시스템 캡 (해커 차단 + 운영 안전)
-- ============================================================
create table if not exists public.keeper_outbound_system_state (
key text primary key, -- 'all_hold' / 'minute_count' / 'hour_count' /
-- 'day_count' / 'last_balance_check_at'
value jsonb not null,
updated_at timestamptz default now()
);
-- ============================================================
-- 7. RLS
-- ============================================================
alter table public.keeper_outbound_otp_sessions enable row level security;
alter table public.keeper_outbound_fraud_signals enable row level security;
alter table public.keeper_outbound_system_state enable row level security;
create policy "service_all_otp" on public.keeper_outbound_otp_sessions
for all to service_role using (true) with check (true);
create policy "service_all_fraud" on public.keeper_outbound_fraud_signals
for all to service_role using (true) with check (true);
create policy "service_all_system" on public.keeper_outbound_system_state
for all to service_role using (true) with check (true);
10.P3 API endpoint 명세
| method | path | 역할 | auth |
| POST | /api/keeper-out/diagnose/request-otp | V3·V4·V5·V8·V2 통과 시 OTP 발송 | 익명 (RLS) |
| POST | /api/keeper-out/diagnose/verify | V1·V6 통과 시 leads INSERT + 큐 진입 | 익명 |
| GET | /api/keeper-out/admin/pending | TM 큐 조회 (24h 내 미처리) | admin |
| POST | /api/keeper-out/admin/leads/[id]/tm-call | TM 통화 outcome 입력 | admin |
| POST | /api/keeper-out/admin/leads/[id]/dispatch | 승인 후 Giftishow 발송 (잔액 재확인) | admin |
| POST | /api/keeper-out/admin/leads/[id]/reject | 거부 (사유 enum) | admin |
| GET | /api/keeper-out/admin/fallback | 자동님 fallback 큐 (TM 6h 미처리) | admin (PM+) |
| POST | /api/keeper-out/admin/dispatch/all-hold | ALL HOLD on/off | admin (PM+) |
| GET | /api/keeper-out/admin/leads/[id]/external | L2 admin User phone match (사후 검수) | admin |
| POST | /api/keeper-out/cron/recheck-completion | 24h cron — 외부 매칭 + payouts 확정 | service |
| POST | /api/keeper-out/cron/escalate-fallback | 6h cron — TM 미처리 → fallback 큐 | service |
| POST | /api/keeper-out/cron/auto-dispatch | 20~24h 미승인 처리 (정책 a 채택 시) | service |
request-otp 명세
// POST /api/keeper-out/diagnose/request-otp
// Body
{
payload: DiagnoseSubmitPayload, // 진단 데이터 전체 (clinet 임시 보관)
agent_name: string, // V3 영업원 이름
agent_code: string, // V3 5자리 영숫자
}
// 게이트 순서 (모두 통과 시 OTP 발송)
1. V3: agents.code + agents.name 매칭 + active=true + day_cap·week_cap 미달
2. V4: payload.naver_place_id NOT NULL + DB 미존재
3. V4: payload.phone DB 미존재 (lifetime unique)
4. V5: system_state.minute_count·hour_count·day_count 미달 + system_state.all_hold !== true
5. V5: Giftishow checkBalance() ≥ ₩50,000
6. V8: place 실재성 (Naver Place API)
7. V2: GPS × place 거리 ≤ 200m
8. await sendOtp(phone) // balerion SMS proxy
9. INSERT otp_sessions (5분 만료, attempts=0)
// Response
{ ok: true, otp_session_id: uuid } // 클라이언트가 verify로 다시 보냄
verify 명세
// POST /api/keeper-out/diagnose/verify
{
otp_session_id: uuid,
otp_code: string, // 6자리
payload: DiagnoseSubmitPayload, // request-otp와 동일
agent_code: string,
}
// 게이트
1. V1: otp_sessions row 매칭 + expires_at > now() + attempts < 3
2. V6: photo EXIF 검증 (soft, agent confirm 옵션)
3. INSERT leads (agent_id resolved, payout_status='pending_review', ip_address, user_agent)
4. INSERT payouts × 3 (naver_pay_10k status='pending_review', lead_30k 2건 status='pending')
5. funnel_event 'review.queued'
6. balerion SMS — 사장님 안내 SMS 발송
// Response
{ ok: true, lead_id: uuid, expected_dispatch_within: '24h' }
tm-call + dispatch 명세
// POST /api/keeper-out/admin/leads/[id]/tm-call
{
outcome: 'confirmed' | 'no_answer' | 'not_owner' | ...,
note: string,
}
// → leads.tm_outcome + tm_called_at + tm_note 갱신
// → outcome === 'fraud_suspect' 시 fraud_signal INSERT + admin alarm
// POST /api/keeper-out/admin/leads/[id]/dispatch
{ approval_tier: 1 | 2 | 3 } // 1=TM, 2=admin, 3=auto-fallback
// 게이트
1. payout_status === 'pending_review'
2. system_state.all_hold !== true
3. cap 재확인 (분당/시간당/일별)
4. Giftishow checkBalance() 재확인
5. await sendNaverPayCoupon(...)
6. payouts.naver_pay_10k status='sent' + leads.approved_by + approved_at + approval_tier
7. funnel_event 'payout.dispatched'
11.P3 admin UI — 신규 페이지 + 섹션
신규 페이지 2건
| 경로 | 역할 | 담당자 |
| app/keeper-out/admin/pending/page.tsx | TM 큐 (24h 내 미처리) | TM (이서경 등) |
| app/keeper-out/admin/fallback/page.tsx | 자동님 fallback 큐 (TM 6h 미처리) | 자동님·PM |
LeadDetailClient (이미 박힌 P1)에 추가할 섹션 4개
| # | 섹션 | 표시 데이터 | Phase |
| 9 | 방문 로그 | visit_logs | P2 |
| 10 | 점주 다른 매장 | other_leads | P2 |
| 11 | 매장 마스터 | place | P2 |
| 13 | 메모 폼 | visits POST | P2 |
| 14 | ★ 검증 결과 | otp_session(언제·통과여부) / agent_attribution / fraud_signals[] | P3 |
| 15 | ★ 키퍼 admin 매칭 | external_user (subscribes 비교) → ②③룰 통과 표시 | P3 |
| 16 | ★ 승인 이력 (TM/admin) | tm_outcome / tm_note / approved_by / approval_tier | P3 |
| 17 | ★ 발송 액션 | [💸 발송 확인] / [⨯ 거부] / [📞 콜 outcome 입력] | P3 |
신규 컴포넌트 5건
- app/diagnose/components/AgentGateModal.tsx — 사장님 "지원금 받기" 클릭 시 영업원 이름+코드 입력 (V3)
- app/diagnose/components/OtpVerifyModal.tsx — V1 OTP 6자리 입력 (5분 카운트다운)
- app/keeper-out/admin/pending/PendingQueue.tsx — TM 큐 카드 리스트
- app/keeper-out/admin/fallback/FallbackQueue.tsx — 자동님 fallback 큐
- app/keeper-out/admin/components/AllHoldButton.tsx — ALL HOLD 토글 (PM+ 권한)
12.대시보드 에이전트 작업 룰 (필독)
다른 Claude/Codex 세션이 P3 진입 시 따라야 할 룰. 위반 시 PM 검증 fail.
✗ 절대 금지 (위반 시 fail)
#009090 / #00b4b4 / 청록 토큰을 /keeper-out/admin/*에 사용 금지 (admin은 키퍼 오렌지 #ff5c00)
app/diagnose/* 격리 영역 절대 수정 금지 (이 보고서의 P3 신규 컴포넌트는 예외 — diagnose에 모달 추가 가능)
- "한화" / "키퍼" / "하나비전" 단어를 사용자 face 텍스트에 박지 말 것 (이용진 5/4 MVP 룰 — 텍스트=캐시온)
--no-verify / --force push / pre-commit 우회 금지
- main 직접 push 금지 (staging만)
- Giftishow 발송을 fire-and-forget으로 박지 말 것 — v3는 명시적 dispatch endpoint만 사용
- 자동님 동의 없이 다른 외부 API 추가 금지 (NICE/PASS/CAPTCHA)
- LeadDetailClient.tsx 615줄 통째 rewrite 금지 — 섹션 단위 추가만
✓ 따를 것
- 색깔: admin 키퍼 오렌지 토큰 (
#ff5c00 primary, #cc4900 hover, #fff6f1 light bg)
- PostgREST embed 패턴 차용 (이미 박힌
LEAD_DETAIL_SELECT 배열에 push)
URLSearchParams 사용 (raw template string 대신, XSS 방어)
- Next.js 15 App Router params Promise —
await params 필수
- 커밋 단위 분리 (migration 1개, route 1개, UI 섹션별 1개씩)
- tsc + eslint clean 후 commit (PM 검증 통과 기준)
service_role key는 lib/keeper-out/supabase.ts가 자동 사용 — 직접 박지 말 것
- untracked 13개(CLAUDE.md, catchrank, landing, context, data, scripts, docs) 모두 보존 — 룰 따라 그대로 둠
커밋 단위 (P3 — 권장 8 commits)
feat(keeper-out): migration v3 — agent code, leads approval columns, otp/fraud/system_state tables
feat(keeper-out): /diagnose request-otp + verify endpoints + AgentGateModal + OtpVerifyModal
feat(keeper-out/admin): pending queue page + TM call API
feat(keeper-out/admin): dispatch + reject API + balance preflight + cap check
feat(keeper-out/admin): fallback queue page (TM 6h escalate)
feat(keeper-out/admin): ALL HOLD toggle + system_state CRUD
feat(keeper-out/admin): LeadDetailClient sections 14·15·16·17 (검증/외부매칭/승인/액션)
feat(keeper-out): cron — recheck-completion + escalate-fallback (24h dual-layer)
13.Phase 분할 — P1·P1.5·P2·P3·P4
P1
★ 박힘
완료 (오늘 22:50) — pain_points 백엔드 + GET endpoint + 8섹션 (Hero/고객/보안/Pain/사진/영업자/지급/타임라인). 3 commits, PM 검증 6/6 PASS.
P1.5
대기
admin layout 분리 — 마케팅 헤더 격리. 1 commit, ~15분.
P2
대기
4섹션 추가 (visit_logs / 점주 다른 매장 / 매장 마스터 / 메모 폼). 3 commits, ~90분.
P3
★ v3 신설
휴먼 승인 시스템 통합 — V1 OTP / V3 agent gate / V5 잔액 preflight + cap / V12 TM·자동님 승인 / dual-layer 사후 검수. /diagnose 진입점 + admin pending/fallback 페이지 신설 + LeadDetail 섹션 14~17 추가. 8 commits, ~2일.
P4
운영
텔레그램 alarm 통합 + V7 야간 cron 패턴 룰 + invalid률 누적 자동 active=false + 키퍼메이트 ?ref={code} URL 흐름 wire-up. 운영 도구 layer.
14.측정 KPI
| KPI | 목표 | 측정 방법 |
| OTP 5분 내 입력 성공률 | ≥ 95% | otp_sessions verified_at IS NOT NULL / 전체 |
| TM 6h 내 처리율 | ≥ 80% | tm_called_at IS NOT NULL within 6h / 전체 |
| 자동님 fallback 접수율 | ≤ 20% | approval_tier=2 / 전체 dispatched |
| 24h 내 dispatch율 | ≥ 95% | approved_at - created_at < 24h / 전체 |
| cap 발동 빈도 | 주 ≤ 1회 | system_state minute/hour 위반 alarm |
| fraud_signals 미해결 | ≤ 3건 | resolved_at IS NULL count |
| Giftishow 발송 실패율 | ≤ 1% | payouts status='failed' / 전체 dispatch |
| L2 phone match 성공률 (사후) | ≥ 70% | external_user_id IS NOT NULL within 24h |
| 영업원 invalid률 평균 | ≤ 10% | agents.invalid_rate_30d 평균 |
15.결정 6건 누적
| Q | 결정 | 출처 |
| agent.code 형식 | 5자리 영숫자 mixed case | v1 |
| ref URL 패턴 | ?ref={code} | v1 |
| UX — 영업원 코드 시점 | "지원금 받기" 클릭 후 모달 | v1 (자동님 5/5 정정) |
| 코드 수정 보류 | 계획 끝난 후 추후 지시 | v1 |
| v2 통합 보고서 | 대부분 수용 + 병렬 세션 합본 | v2 |
| ★ v3 24h 큐 + 휴먼 승인 | TM 1차 + 자동님 2차 fallback 채택 | v3 (이번 결정) |
16.미해결 정책 결정 1건
★ T+20~24h 미승인 건 정책 — 결정 필요
| 옵션 | 설명 | 장단점 |
| (a) 자동 dispatch fallback |
다른 게이트 모두 통과 + 패턴 정상 시 자동 발송 |
+ 사장님 기대 24h 충족 − 휴먼 검증 우회 |
| (b) "추가 확인 필요" 안내 |
사장님께 "추가 확인 필요해요" SMS + 영업원 재방문 요청 |
+ 100% 휴먼 검증 보장 − 사장님 기대 미달 / 영업 흐름 부담 |
| (c) 하이브리드 ★ 추천 |
패턴 정상 + cap 미달 + V7 hit 0건이면 자동 dispatch, 그 외엔 (b) |
안전한 케이스만 자동 / 의심스러운 건은 사람 확인 |
비결정 5건 (v1~v2 이월)
- OTP 발송 채널 — balerion SMS proxy / KISA / NICE / NHN
- 영업원 우회로 — 관리자 PIN 백도어 / 코드 없는 진단 거부
- review-on과 잔액 격리 — Giftishow 키 별도 / 모니터링만 분리
- agent.code 발급 운영 — 본사 admin 수동 / 자율가입 후 승인 / Referio 매핑
05-policy.md 어뷰징 정책 명문화 별도 문서 — 만들까?
17.참조 — 보고서 체인 + in-repo 문서
보고서 체인 (시간순)
in-repo 문서 (4건)
- docs/keeper-out/anti-abuse/01-current-state.md
- docs/keeper-out/anti-abuse/02-verification-requirements.md
- docs/keeper-out/anti-abuse/03-re-candidates.md
- docs/keeper-out/anti-abuse/04-keeper-platform-re.md
주요 식별자
| Supabase 프로젝트 | hvcrbdpjxfszhwrtcygq |
| 키퍼 admin 권한 | ceo@bizdirect.co.kr (group=13 "키퍼 아웃바운드") |
| Referio 자동님 코드 | 86Af9 (Authorized) |
| balerion SMS proxy | balerion.cloud:4000 (BALERION_API_KEY) |
| Giftishow callback | 01034333385 (자동님 본인) |
| 현재 production | www.keeper-direct.ceo (sha a1da85d → P1 미배포) |