발급 수명주기
issue의 검증 상태, 발송 상태, 검증 결과 코드, 채널과 템플릿
발급(issue) 한 건은 검증 상태, 발송 상태, 정산 상태를 각각 가집니다. 이 문서는 상태가 어떻게 바뀌는지와 조회 방법을 설명합니다.
발급 옵션과 기본값
| 필드 | 기본값 | 범위 |
|---|---|---|
channel | sms | sms, alimtalk |
templateId | otp_default_kr | 템플릿 목록 |
expiresInSec | 180 (3분) | 30–600 |
maxAttempts | 5 | 1–10 |
인증번호는 서버에서 생성하는 6자리 숫자이며, 응답이나 조회 API 어디에서도 반환되지 않습니다. 전화번호도 해시로만 저장되고 어떤 API에서도 반환되지 않습니다.
검증 상태
verificationStatus는 다음 값 중 하나입니다.
| 값 | 의미 |
|---|---|
issued | 발급됨. 아직 검증되지 않았고 만료 전입니다. |
verified | 검증 성공. 다시 검증할 수 없습니다. |
expired | 만료 시각이 지났습니다. |
max_attempts | 검증 실패가 최대 시도 횟수에 도달했습니다. |
replaced | 같은 전화번호·같은 purpose로 새로 발급되어 무효화됐습니다. |
검증 성공
issued ───────────────▶ verified
│ ├── 만료 시각 경과 ──▶ expired
│ ├── 실패가 maxAttempts 도달 ──▶ max_attempts
└──┴── 같은 번호·purpose로 재발급 ──▶ replaced검증 결과
POST /v1/verify는 검증에 실패해도 HTTP 200을 반환합니다. verified가 false이면 reasonCode로 사유를 알 수 있습니다.
reasonCode | 의미 | 권장 처리 |
|---|---|---|
MISMATCH | 코드가 틀렸습니다. 시도 횟수 1회가 차감됩니다. | attemptsRemaining을 보여 주고 다시 입력받습니다. |
MAX_ATTEMPTS | 남은 시도 횟수가 없습니다. | 새 인증번호를 받도록 안내합니다(새 멱등 키로 재발급). |
EXPIRED | 유효 시간이 지났습니다. | 재발급을 안내합니다. |
ALREADY_VERIFIED | 이미 검증에 성공한 issue입니다. | 중복 제출로 보고 기존 인증 결과를 사용합니다. |
REPLACED | 이후 재발급으로 무효화된 issue입니다. | 가장 최근 issueId로 검증합니다. |
NOT_FOUND | 해당 앱에 이 issueId가 없습니다. | issueId 저장·전달 과정을 확인합니다. |
issueId가 비어 있거나 code가 6자리 숫자 문자열이 아니면 400 BAD_REQUEST입니다.
발송 상태
deliveryStatus는 문자 발송 진행 상황을 나타내며, 발송 추적은 비동기로 갱신됩니다.
| 값 | 의미 |
|---|---|
unknown | 아직 추적 기록이 없거나 조회할 수 없습니다. |
queued | 발송 대기열에 들어갔습니다. |
sent | 발송사에 전달됐습니다. |
delivered | 수신 확인됐습니다. |
failed | 발송에 실패했습니다. |
상태 조회
GET /v1/status?issueId=...(sk_ 키, otp:status)로 한 건의 상태를 조회합니다.
curl "https://api.k-otp.dev/v1/status?issueId=0192f3c4-8b7a-7c3e-9a51-2f4d6e8b1a90" \
-H "Authorization: Bearer $KOTP_SECRET_KEY"{
"issueId": "0192f3c4-8b7a-7c3e-9a51-2f4d6e8b1a90",
"messageId": "0192f3c4-8b7b-7a01-8c2d-3e4f5a6b7c8d",
"purpose": "login",
"templateId": "otp_default_kr",
"verificationStatus": "issued",
"deliveryStatus": "delivered",
"overallStatus": "delivered",
"expiresAt": "2026-09-28T03:03:00.000Z",
"attemptsUsed": 0,
"maxAttempts": 5,
"attemptsRemaining": 5,
"createdAt": "2026-09-28T03:00:00.000Z",
"updatedAt": "2026-09-28T03:00:04.000Z"
}화면에 하나의 상태만 보여 주려면 overallStatus를 사용하세요. 계산 규칙은 다음과 같습니다.
verificationStatus가issued가 아니면 그 값을 그대로 사용합니다(verified,expired,max_attempts,replaced).issued이면deliveryStatus로 결정합니다.
deliveryStatus | overallStatus |
|---|---|
delivered | delivered |
failed | delivery_failed |
queued, sent | in_progress |
unknown | pending_lookup |
알아 둘 점:
- 발급 직후에는 기록이 아직 저장 중일 수 있습니다. 발급 후 최대 60초 동안은
404대신overallStatus: "pending_lookup"으로 응답하며, 그 뒤에도 기록이 없으면404 NOT_FOUND입니다. providerOutcomeAmbiguous: true는 발송사 응답이 불명확했다는 뜻입니다. 이런 발송은 자동으로 재시도되지 않습니다(멱등성과 재시도).- 다른 앱의
issueId는 존재하지 않는 것과 똑같이404로 응답합니다.
여러 건을 조회하려면 GET /v1/issues(otp:dashboard:read)를 사용합니다. 최신순 커서 페이지네이션(limit 1–100, 기본 50, 응답의 nextCursor를 다음 요청의 cursor로 전달)과 verificationStatus, createdFrom/createdTo 필터를 지원합니다. 자세한 내용은 발급 이력 목록을 참고하세요.
채널과 템플릿
channel로 SMS(기본값) 또는 카카오 알림톡을 선택합니다. 메시지 본문은 미리 등록된 템플릿만 사용할 수 있으며, 모든 템플릿에는 인증번호 자리인 #{code}가 들어 있습니다. 알림톡은 사전 등록된 템플릿 코드로만 발송됩니다. 알림톡 발송에 실패하면 K-OTP가 같은 인증번호를 SMS로 자동 발송하며 크레딧은 추가로 차감되지 않습니다. 채널과 대체 발송을 참고하세요.
templateId | 본문 |
|---|---|
otp_default_kr (기본) | [K-OTP] 인증번호는 #{code}입니다. 3분 내에 입력해주세요. |
otp_login_kr | [K-OTP] 로그인 인증번호는 #{code}입니다. 3분 내에 입력해주세요. |
otp_signup_kr | [K-OTP] 회원가입 인증번호는 #{code}입니다. 3분 내에 입력해주세요. |
otp_payment_kr | [K-OTP] 결제 인증번호는 #{code}입니다. 3분 내에 입력해주세요. |
사용 가능한 템플릿과 변수는 GET /v1/templates, GET /v1/templates/{templateId}(otp:templates)로 확인하세요. #{code}는 서버가 채우므로 templateVariables에 넣으면 안 됩니다. 새 템플릿은 API가 아니라 별도 문의로 등록합니다.
템플릿 본문의 "3분"은 고정 문구입니다. expiresInSec를 바꾸면 본문과 실제 유효 시간이 달라질 수
있습니다.