S SMS OTPDeveloper

SMS OTP API v1 · Production

문자 인증을
서비스에 연결하세요.

외부 서비스 서버에서 인증 요청을 만들고, 사용자를 일회용 인증 URL로 이동시킨 뒤, Callback에서 받은 1회용 결과 코드를 서버에서 교환하는 방식입니다.

중요API Key / API Secret은 반드시 서버에서만 사용하세요. 브라우저 JavaScript, HTML, 앱 번들, 공개 저장소에 넣으면 안 됩니다.

Production endpoint

호출 주소

운영 환경의 모든 API 호출은 아래 도메인을 기준으로 합니다.

https://smsotp.imtdot.kr
https://smsotp.imtdot.kr
용도Method전체 URL
인증 요청 생성POSThttps://smsotp.imtdot.kr/api/v1/sessions
사용자 인증GEThttps://smsotp.imtdot.kr/v/{ONE_TIME_TOKEN}
결과 코드 교환POSThttps://smsotp.imtdot.kr/api/v1/results/exchange
관리자 콘솔GEThttps://smsotp.imtdot.kr/console/
개발자 문서GEThttps://smsotp.imtdot.kr/dev/
AI/Markdown 문서GEThttps://smsotp.imtdot.kr/dev/guide.md
1
세션 생성서비스 서버 → SMS OTP API
→
2
사용자 인증verification_url로 이동
→
3
Callbackcode + state 반환
→
4
결과 교환전화번호 검증 결과 수신

Before integration

관리자 콘솔 준비

먼저 관리자 콘솔에서 서비스와 Callback을 등록하고 API Key/Secret을 발급하세요.

관리자 콘솔
https://smsotp.imtdot.kr/console/

서비스 이름: My Service
Client ID: my-service
Callback URL: https://service.example.com/auth/sms/callback

API Key:    sms_live_...
API Secret: sms_secret_...
API SecretSecret은 발급 직후 한 번만 표시됩니다. 서버 환경변수나 Secret 저장소에 즉시 보관하세요.

Step 1

인증 요청 생성

외부 서비스의 백엔드에서 이 API를 호출합니다. 브라우저에서 직접 호출하지 마세요.

POSThttps://smsotp.imtdot.kr/api/v1/sessions

Request Headers

X-API-Key: sms_live_YOUR_KEY
X-API-Secret: sms_secret_YOUR_SECRET
Content-Type: application/json

Request Body

{
  "callback_url": "https://service.example.com/auth/sms/callback",
  "state": "6a9e09bd-17ea-4aa6-b82a-cb1f9ccd6a2e"
}

cURL 요청 예시

curl -X POST "https://smsotp.imtdot.kr/api/v1/sessions" \
  -H "X-API-Key: sms_live_YOUR_KEY" \
  -H "X-API-Secret: sms_secret_YOUR_SECRET" \
  -H "Content-Type: application/json" \
  --data '{
    "callback_url": "https://service.example.com/auth/sms/callback",
    "state": "6a9e09bd-17ea-4aa6-b82a-cb1f9ccd6a2e"
  }'

성공 출력 예시 · HTTP 201

{
  "request_id": "550e8400-e29b-41d4-a716-446655440000",
  "verification_url": "https://smsotp.imtdot.kr/v/5yR3lR4FM0uX6dQKpYxWf8YpM0zFQhX5",
  "expires_in": 600,
  "expires_at": "2026-08-30T01:20:00.000Z",
  "session_reuse_enabled": true
}
응답 필드설명
request_id인증 요청 식별자
verification_url사용자를 이동시킬 10분짜리 일회용 URL
expires_inURL 유효시간(초), 현재 600
expires_atURL 만료 시각
session_reuse_enabled이 API Key가 7일 중앙 세션 재사용을 허용하는지 여부

Node.js / 서버 JavaScript

const SMS_OTP_BASE = 'https://smsotp.imtdot.kr';

const response = await fetch(`${SMS_OTP_BASE}/api/v1/sessions`, {
  method: 'POST',
  headers: {
    'content-type': 'application/json',
    'X-API-Key': process.env.SMS_OTP_API_KEY,
    'X-API-Secret': process.env.SMS_OTP_API_SECRET,
  },
  body: JSON.stringify({
    callback_url: 'https://service.example.com/auth/sms/callback',
    state: crypto.randomUUID(),
  }),
});

const data = await response.json();
if (!response.ok) throw new Error(data.error);

// 사용자를 일회용 인증 URL로 이동
res.redirect(data.verification_url);

Step 2

사용자 인증 페이지

응답에서 받은 URL로 브라우저를 이동시킵니다. 토큰을 직접 만들어서는 안 됩니다.

GET https://smsotp.imtdot.kr/v/ONE_TIME_TOKEN
10분인증 URL 유효시간
5분OTP 유효시간
7일중앙 인증 세션
2분결과 code · 1회용

Central session

7일 인증 세션

API Key 발급 시 기본적으로 세션 재사용이 켜져 있습니다.

기본값 · ON

기존 인증 재사용

같은 브라우저에서 최근 7일 이내 인증했다면 SMS를 다시 보내지 않고 바로 Callback으로 이동합니다.

선택 · OFF

매번 새 SMS OTP

API Key 설정에서 세션 허용을 끄면 기존 세션이 있어도 무시하고 매번 새로운 OTP 인증을 요구합니다.

세션 재사용 시 결과

{
  "verification": {
    "method": "existing_session"
  }
}

Step 3

Callback 처리

인증 성공 후 등록된 Callback URL에 code와 원래의 state가 붙어서 돌아옵니다.

https://service.example.com/auth/sms/callback?code=result_A1B2C3D4&state=6a9e09bd-17ea-4aa6-b82a-cb1f9ccd6a2e
state 검증 필수Callback의 state와 인증 요청을 만들 때 서버에 저장한 state가 정확히 같은지 확인하세요. 다르면 인증을 중단해야 합니다.

Callback 서버 처리 예시

const { code, state } = req.query;

if (!code || !state || state !== req.session.smsOtpState) {
  return res.status(400).send('Invalid callback state');
}

// 여기서 code를 결과 교환 API로 전송

Step 4

결과 코드 교환

Callback의 code 자체를 신뢰하지 말고 서버에서 최종 결과를 교환해야 합니다.

POSThttps://smsotp.imtdot.kr/api/v1/results/exchange

Request Headers

X-API-Key: sms_live_YOUR_KEY
X-API-Secret: sms_secret_YOUR_SECRET
Content-Type: application/json

Request Body

{
  "code": "result_A1B2C3D4"
}

cURL 요청 예시

curl -X POST "https://smsotp.imtdot.kr/api/v1/results/exchange" \
  -H "X-API-Key: sms_live_YOUR_KEY" \
  -H "X-API-Secret: sms_secret_YOUR_SECRET" \
  -H "Content-Type: application/json" \
  --data '{"code":"result_A1B2C3D4"}'

성공 출력 · 이번에 SMS 인증

{
  "verified": true,
  "phone": "01012345678",
  "verification": {
    "method": "sms_otp",
    "authenticated_at": "2026-08-30T00:42:15.000Z",
    "session_expires_at": "2026-09-06T00:42:15.000Z"
  }
}

성공 출력 · 7일 세션 재사용

{
  "verified": true,
  "phone": "01012345678",
  "verification": {
    "method": "existing_session",
    "authenticated_at": "2026-08-30T00:42:15.000Z",
    "session_expires_at": "2026-09-06T00:42:15.000Z"
  }
}

Node.js 결과 교환

const response = await fetch('https://smsotp.imtdot.kr/api/v1/results/exchange', {
  method: 'POST',
  headers: {
    'content-type': 'application/json',
    'X-API-Key': process.env.SMS_OTP_API_KEY,
    'X-API-Secret': process.env.SMS_OTP_API_SECRET,
  },
  body: JSON.stringify({ code }),
});

const result = await response.json();

if (!response.ok || result.verified !== true) {
  throw new Error(result.error || 'SMS verification failed');
}

console.log(result.phone);

Full example

Express 전체 흐름

인증 시작부터 Callback 및 결과 교환까지 한 번에 보는 예시입니다.

Express / Node.js

import crypto from 'node:crypto';

const SMS_OTP_BASE = 'https://smsotp.imtdot.kr';
const CALLBACK_URL = 'https://service.example.com/auth/sms/callback';

app.get('/verify-phone', async (req, res) => {
  const state = crypto.randomUUID();
  req.session.smsOtpState = state;

  const r = await fetch(`${SMS_OTP_BASE}/api/v1/sessions`, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      'X-API-Key': process.env.SMS_OTP_API_KEY,
      'X-API-Secret': process.env.SMS_OTP_API_SECRET,
    },
    body: JSON.stringify({ callback_url: CALLBACK_URL, state }),
  });

  const data = await r.json();
  if (!r.ok) return res.status(502).json(data);
  return res.redirect(data.verification_url);
});

app.get('/auth/sms/callback', async (req, res) => {
  const { code, state } = req.query;

  if (!code || !state || state !== req.session.smsOtpState) {
    return res.status(400).send('Invalid callback state');
  }

  delete req.session.smsOtpState;

  const r = await fetch(`${SMS_OTP_BASE}/api/v1/results/exchange`, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      'X-API-Key': process.env.SMS_OTP_API_KEY,
      'X-API-Secret': process.env.SMS_OTP_API_SECRET,
    },
    body: JSON.stringify({ code }),
  });

  const result = await r.json();
  if (!r.ok || result.verified !== true) {
    return res.status(400).json(result);
  }

  req.session.verifiedPhone = result.phone;
  req.session.phoneVerified = true;
  return res.redirect('/verification-complete');
});

Errors

오류 응답

API 오류는 JSON으로 반환됩니다. HTTP 상태 코드와 error 값을 함께 확인하세요.

API Key / Secret 오류 · 401

{
  "error": "INVALID_API_CREDENTIALS"
}

등록되지 않은 Callback · 403

{
  "error": "INVALID_REDIRECT_URI"
}

Callback 누락 · 400

{
  "error": "CALLBACK_URL_REQUIRED"
}

결과 code 오류/재사용

{
  "error": "INVALID_OR_USED_CODE"
}
HTTP의미
200정상 처리
201인증 요청 생성 성공
400요청 데이터 또는 결과 code 오류
401API Key/Secret 인증 실패
403허용되지 않은 Callback 또는 정책 차단
410인증 URL 만료 또는 이미 처리됨
500서버 내부 오류
503필수 서버 설정 누락

Security

연동 체크리스트

✓ API Key/Secret은 서버에서만 사용
✓ Callback은 HTTPS 사용
✓ Callback URL은 콘솔 등록값과 정확히 일치
✓ state는 매 요청마다 랜덤 생성
✓ state는 서버 세션/DB에 저장
✓ Callback에서 state를 반드시 비교
✓ code 자체만 보고 인증 성공 처리 금지
✓ /api/v1/results/exchange의 verified === true 확인
✓ 결과 code는 2분/1회용으로 취급

For AI agents

AI 에이전트용 문서

HTML을 파싱하기보다 canonical Markdown 문서를 직접 읽는 것을 권장합니다. Markdown에는 전체 호출 주소, 요청/출력 예시, 전체 흐름, 오류와 보안 규칙이 포함되어 있습니다.

https://smsotp.imtdot.kr/dev/guide.md

AI 문서 인덱스: https://smsotp.imtdot.kr/llms.txt