한국형 개인정보 필터

# 한국형 개인정보 필터 API 한국어 텍스트에서 개인정보(PII)를 탐지하고 마스킹하는 API. - **모델 ID**: `pii` - **Base URL**: `https://api.corepin.ai` ## 1. 엔드포인트 | Method | Path | 인증 | 설명 | |---|---|:-:|---| | GET | `/v1/pii/version` | – | 모델 버전·라벨 수 (`label_count: 19`) | | GET | `/v1/pii/categories` | – | 19 카테고리 카탈로그 (라벨·한글명·설명) | | POST | `/v1/pii/detect` | ✓ | 탐지 — `detected_spans` 배열만 반환 | | POST | `/v1/pii/redact` | ✓ | 탐지 + 마스킹된 텍스트 반환 | | POST | `/v1/pii/batch` | ✓ | 1 ~ 100 텍스트 일괄 처리 | | GET | `/v1/pii/history` | ✓ | 검출 이력 (본문 미저장, 라벨 카운트만) | | GET | `/v1/pii/audit` | ✓ | 감사 로그 (대시보드 설정 활성화 후 적재) | 레거시 alias `/v1/{detect,redact,batch}`도 같은 동작이에요. 새 통합은 `/v1/pii/*`를 사용해주세요. ## 2. POST `/v1/pii/detect` **Request**: ```json { "text": "탐지할 텍스트 (1 ~ 32,768자)", "categories": ["kr_rrn", "private_phone"], "return_text": true, "apply_policy": false } ``` | 필드 | 타입 | 기본 | 설명 | |---|---|---|---| | `text` | string | (필수) | 1 ~ 32,768자 | | `categories` | string[] | `null` | 화이트리스트. 미지정 시 19 카테고리 모두 탐지. | | `return_text` | bool | `true` | 응답에 `text` 필드 echo 여부. | | `apply_policy` | bool | `false` | 조직·앱·프로젝트 정책 적용해서 차단 카테고리 spans 제외. | ## 3. POST `/v1/pii/redact` `/v1/pii/detect`의 모든 필드 + ```json { "output_mode": "redacted", "placeholder_template": "[{label}]" } ``` | 필드 | 값 | 동작 | |---|---|---| | `output_mode` | `redacted` (기본) | `placeholder_template` 적용 (예: `<KR_RRN>`) | | `output_mode` | `partial_mask` | 마지막 4자 외 `*` (예: `**********4567`) | | `output_mode` | `typed` | 마스킹 없이 spans만 반환 | `placeholder_template` 변수: `{label}`, `{start}`, `{end}`, `{original}`. ## 4. POST `/v1/pii/batch` ```json { "texts": ["...", "...", "..."], "categories": null, "output_mode": "redacted", "placeholder_template": null, "return_text": true, "apply_policy": false } ``` - 1 ~ 100 텍스트. - 각 항목당 호출 1건 차감 (분당·월 한도 모두). ## 5. 응답 ```json { "text": "안녕하세요, 김민수입니다. 010-1234-5678로 연락주세요.", "detected_spans": [ {"label": "private_person", "start": 7, "end": 10, "text": "김민수", "placeholder": "<PRIVATE_PERSON>"}, {"label": "private_phone", "start": 16, "end": 29, "text": "010-1234-5678", "placeholder": "<PRIVATE_PHONE>"} ], "span_count": 2, "by_label": {"private_person": 1, "private_phone": 1}, "redacted_text": "안녕하세요, <PRIVATE_PERSON>입니다. <PRIVATE_PHONE>로 연락주세요.", "meta": { "model_id": "pii", "model_version": "pii-2026.05-v1.3", "processing_time_ms": 38.7, "request_id": "4c74bd30c53e497ca8a71f05aead6012", "quota_remaining": 99998 } } ``` | 필드 | 타입 | 설명 | |---|---|---| | `text` | string \| null | `return_text=false`면 null. | | `detected_spans` | Span[] | 탐지된 PII 위치 배열. | | `detected_spans[].label` | string | 19 카테고리 중 하나. | | `detected_spans[].start` / `end` | int | Python 문자열 인덱스 (UTF-16 단위 아님), `end` exclusive. | | `detected_spans[].text` | string | 원문 substring. | | `detected_spans[].placeholder` | string | 권장 마스킹 토큰. | | `span_count` | int | 탐지된 span 개수. | | `by_label` | object | 카테고리별 카운트. | | `redacted_text` | string \| null | `output_mode` 적용된 본문 (`detect`는 null). | | `meta.*` | – | 공통 응답 메타 (모델 버전, 처리 시간, request_id, quota). | | `meta.policy_applied` | bool | 요청에 `apply_policy=true`를 보냈는지. | | `meta.filtered_out` | int | 정책으로 제외된 span 수 (`apply_policy=true`일 때만). | ## 6. 19 카테고리 한글명은 `GET /v1/pii/categories`의 `ko` 필드와 같아요. `pipa_sensitive` 열은 같은 응답의 `pipa_sensitive` 값이에요. | 라벨 | 한글명 | `pipa_sensitive` | |---|---|:-:| | `private_person` | 이름 | | | `private_address` | 주소 | | | `private_email` | 이메일 | | | `private_phone` | 전화번호 | | | `private_url` | URL | | | `private_date` | 날짜 (생년월일 등) | | | `account_number` | 계좌·카드번호 | | | `secret` | 비밀정보 (API 키·토큰·비밀번호) | | | `kr_rrn` | 주민등록번호 | ✓ | | `kr_foreigner_id` | 외국인등록번호 | ✓ | | `kr_passport` | 여권번호 | ✓ | | `kr_driver_license` | 운전면허번호 | ✓ | | `kr_biz_no` | 사업자등록번호 | | | `kr_corp_no` | 법인등록번호 | | | `kr_health_insurance` | 건강보험증번호 | ✓ | | `kr_vehicle_plate` | 차량번호 | | | `private_ip` | IP 주소 | | | `kr_company` | 회사명·기관명 | | | `kr_money` | 금액 | | `pipa_sensitive: true`는 개인정보보호법상 별도 보호 대상이에요 — §24-2 고유식별정보 4종(주민등록번호·외국인등록번호·여권번호·운전면허번호)과 건강보험증번호. 마스킹 후에도 별도 처리·암호화 의무가 생길 수 있어요. `GET /v1/pii/categories`에서 카테고리별 한글 설명까지 받을 수 있어요. (v1.1 에서 `kr_company`·`kr_money`가 추가되며 17 → 19 카테고리) ## 7. GET `/v1/pii/history` · `/v1/pii/audit` **`/v1/pii/history`** — 본문 미저장 검출 이력. 라벨 카운트·길이만 반환. 쿼리: `from_ts` / `to_ts` (unix sec) · `limit` (1 ~ 500) · `offset`. **`/v1/pii/audit`** — 본문 저장 감사 로그. 대시보드에서 PII 감사 모드를 `metadata` 또는 `full`로 켠 이후의 요청만 적재. `level=full`일 때만 `input_text`가 채워져요. 쿼리: `from_ts` / `to_ts` · `blocked_only` · `limit` · `offset`. ## 8. 단가 - 무료: 분당 60회 · 월 1,000회 (카드 등록 불필요) - 유료: **5원 / 호출** (배치는 `texts` 개수만큼 차감) - 전체 비교는 [`/pricing`](/pricing) · 청구 방식은 [`/docs/billing`](/docs/billing) ## 9. 오류 응답 공통 오류 envelope·인증·rate limit 오류는 [빠른 시작 §6](/docs/quickstart) 참고. ## 10. 코드 예제 ### Python ```python from kpf_client import KPFClient client = KPFClient(api_key="sk_live_…") result = client.redact("홍길동 010-1234-5678") print(result.redacted_text) # "<PRIVATE_PERSON> <PRIVATE_PHONE>" ``` ### JavaScript ```ts const r = await fetch(`${BASE}/v1/pii/redact`, { method: "POST", headers: { "Authorization": `Bearer ${process.env.COREPIN_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ text, output_mode: "redacted" }), }); const { redacted_text, detected_spans } = await r.json(); ```