K-OTP

발급 수명주기

issue의 검증 상태, 발송 상태, 검증 결과 코드, 채널과 템플릿

발급(issue) 한 건은 검증 상태, 발송 상태, 정산 상태를 각각 가집니다. 이 문서는 상태가 어떻게 바뀌는지와 조회 방법을 설명합니다.

발급 옵션과 기본값

필드기본값범위
templateIdotp_default_kr템플릿 목록
channelalimtalkalimtalk만 (채널)
smsFallbacktruetrue, false (API 1.8.0, 알림톡에만 적용)
expiresInSec180 (3분)30–600
maxAttempts51–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로 결정합니다.
deliveryStatusoverallStatus
delivereddelivered
faileddelivery_failed
queued, sentin_progress
unknownpending_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 필터를 지원합니다. 자세한 내용은 발급 이력 목록을 참고하세요.

채널과 템플릿

인증번호는 카카오 알림톡으로 발송되며, SMS 대체 발송은 기본으로 켜져 있고 요청마다 smsFallback: false로 끌 수 있습니다. 승인된 알림톡 템플릿이 있는 템플릿만 발급할 수 있으며, SMS 직접 발송은 없습니다(channel: "sms"는 400 BAD_REQUEST로 거절됩니다). 어느 경우든 발송 1건에 1 크레딧입니다. 채널과 SMS 대체 발송을 참고하세요.

메시지 본문은 미리 등록된 템플릿만 사용할 수 있으며, 모든 템플릿에는 인증번호 자리인 #{code}가 들어 있습니다. 알림톡은 사전 등록된 템플릿 코드로만 발송됩니다.

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를 바꾸면 본문과 실제 유효 시간이 달라질 수 있습니다.

이 페이지의 내용