K-OTP

크레딧과 결제

선불 크레딧, 잔액 부족 시 402, 잔액·원장 조회

K-OTP는 선불 크레딧 방식입니다. 콘솔에서 앱 지갑에 크레딧을 충전하고, 발송 1건마다 크레딧 1개가 차감됩니다. 크레딧은 화폐가 아닌 수량 단위이며 API 응답의 currency는 항상 CREDIT입니다.

  • 지갑은 앱 단위입니다. 같은 앱의 키는 모두 하나의 지갑을 공유합니다.
  • 충전은 콘솔에서 합니다. 공개 API로는 충전할 수 없습니다.

크레딧 1개 = OTP 발송 1건

OTP 발송 1건은 SMS, 알림톡, 알림톡 실패 후 SMS 자동 대체 발송 중 어느 채널로 전달되든 정확히 1 크레딧입니다. 대체 발송에 크레딧이 추가로 차감되지 않습니다. 채널과 대체 발송을 참고하세요.

크레딧 팩

팩가격크레딧크레딧당 실질 가격
Starter$19.901,500 (기본 1,000 + 보너스 500)약 $0.0133
Pro$199.017,000 (기본 10,000 + 보너스 7,000)약 $0.0117

정가는 크레딧당 $0.0199이며, 보너스 크레딧만큼 실질 가격이 낮아집니다. 구독은 없습니다. 최신 팩은 k-otp.dev/ko/pricing에서 확인하세요.

차감 흐름

  1. POST /v1/issue 시점에 1 크레딧을 예약합니다. 잔액이 부족하면 이 단계에서 거절됩니다.
  2. 발송 처리가 끝나면 비동기로 정산됩니다.
    • 발송에 성공하면 예약분이 차감(debit) 됩니다.
    • 발송이 실패하면 예약분이 해제(release) 되어 차감되지 않습니다.
  3. 발급 응답에는 잔액이나 차감 금액이 포함되지 않습니다. 필요하면 아래 조회 API를 사용하세요.

발급 건별 정산 상태(billingStatus)는 GET /v1/issues/{issueId}에서 확인할 수 있습니다.

billingStatus의미
reserved발급 시 크레딧을 예약함
debit_pending → debited차감 진행 중 → 차감 완료
release_pending → released예약 해제 진행 중 → 해제 완료(차감 없음)

잔액 부족: 402 PAYMENT_REQUIRED

잔액이 부족하면 발급이 402로 거절되고 문자는 발송되지 않습니다. data.code로 사유를 구분합니다.

{
	"defined": true,
	"code": "PAYMENT_REQUIRED",
	"status": 402,
	"message": "PAYMENT_REQUIRED",
	"data": { "code": "INSUFFICIENT_CREDIT" }
}
data.code의미
INSUFFICIENT_CREDIT잔액이 부족합니다.
OVERDRAFT_LIMIT_EXCEEDED앱에 허용된 초과 사용(overdraft) 한도를 넘었습니다.

크레딧을 충전한 뒤 다시 시도하세요. 사용자에게는 "잠시 후 다시 시도" 같은 일반 오류로 보여 주고, 운영 알림으로 잔액 부족을 감지하는 것을 권장합니다.

잔액 조회

GET /v1/balance (sk_ 키, otp:balance)

curl "https://api.k-otp.dev/v1/balance" \
  -H "Authorization: Bearer $KOTP_SECRET_KEY"
{
	"appId": "app_...",
	"balance": 1200,
	"currency": "CREDIT",
	"updatedAt": "2026-09-28T03:00:05.000Z"
}

원장 조회

GET /v1/credit-ledger (sk_ 키, otp:ledger:read)는 지갑의 변동 내역을 최신순으로 반환합니다.

entryType의미amountDelta
credit충전양수
debit발급 차감음수
refund환불양수
  • balanceAfter는 해당 항목 반영 후 잔액입니다. 초과 사용이 허용된 앱은 음수일 수 있습니다.
  • issueId는 발급과 연결된 항목(debit, refund)에만 있습니다.
  • 페이지네이션: limit(1–100, 기본 50), 응답의 nextCursor를 다음 요청의 cursor로 그대로 전달합니다. nextCursor가 없으면 마지막 페이지입니다. 페이지를 넘기는 동안 필터를 바꾸지 마세요.
  • 필터: entryType, createdFrom(포함), createdTo(미포함) — RFC 3339 형식.
const API = "https://api.k-otp.dev/v1";

export async function* listLedger(createdFrom: string) {
	let cursor: string | undefined;
	do {
		const params = new URLSearchParams({ limit: "100", createdFrom });
		if (cursor) params.set("cursor", cursor);
		const res = await fetch(`${API}/credit-ledger?${params}`, {
			headers: { Authorization: `Bearer ${process.env.KOTP_SECRET_KEY}` },
		});
		const body = await res.json();
		if (!res.ok) throw new Error(`${body.code}: ${body.message}`);
		yield* body.items;
		cursor = body.nextCursor;
	} while (cursor);
}

환불

사용하지 않은 크레딧은 구매 후 7일 이내에 청약철회할 수 있습니다. 일부 사용한 팩은 사용한 크레딧을 정가로 계산합니다. 자세한 내용은 환불 정책을 참고하세요.

이 페이지의 내용