K-OTP

발급 수명주기

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

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

발급 옵션과 기본값

필드기본값범위
channelsmssms, alimtalk
templateIdotp_default_kr템플릿 목록
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 필터를 지원합니다. 자세한 내용은 발급 이력 목록을 참고하세요.

채널과 템플릿

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

이 페이지의 내용