연락처 인증
크리에이터 이메일 및 전화번호 인증 API
연락처 인증
크리에이터의 이메일과 전화번호 인증 상태를 조회하고, 6자리 인증번호를 발송/검증합니다.
신규 크리에이터는 온보딩 완료 전에 연락처 인증을 완료해야 합니다. 전화 국가번호(telDialCode)가 +82인 경우 이메일과 전화번호를 모두 인증하고, +82가 아니면 이메일만 인증합니다. 기존 크리에이터는 대시보드 최초 진입 시 status 응답의 verificationRequired 값을 보고 프론트에서 인증 플로우를 노출합니다.
이메일 인증번호는 Node 메일 API를 통해 발송됩니다. 이메일 bounce는 비동기로 반영되므로 발송 직후에는 deliveryStatus가 PENDING일 수 있습니다.
공통
| 항목 | 값 |
|---|---|
| Base URL | /ai/influence |
| 인증 | 필요 |
| 인증 방식 | JWT Bearer Token |
| 인증번호 | 6자리 숫자 |
| 만료 시간 | 3분 |
| 요청 제한 | 10분 3회 |
상태 조회
GET /ai/influence/contact-verification/status
Authorization: Bearer {access_token}응답
{
"status": 200,
"code": null,
"message": "연락처 인증 상태 조회 성공",
"data": {
"verificationRequired": true,
"onboardingCompleted": true,
"emailVerified": false,
"telVerified": false,
"email": "creator@example.com",
"tel": "01012345678",
"telDialCode": "+82",
"maskedEmail": "c****@example.com",
"maskedTel": "010****5678",
"lastEmailDeliveryStatus": "BOUNCED",
"lastEmailFailureReason": "mailbox unavailable"
}
}Prop
Type
이메일 인증번호 발송
POST /ai/influence/contact-verification/email/send
Authorization: Bearer {access_token}
Content-Type: application/json
{
"contact": "creator@example.com",
"purpose": "ONBOARDING"
}contact를 비우면 현재 계정에 저장된 이메일로 발송합니다.
응답
{
"status": 200,
"code": null,
"message": "이메일 인증번호를 발송했습니다.",
"data": {
"channel": "EMAIL",
"maskedContact": "c****@example.com",
"expiresInSeconds": 180,
"attemptId": "8a83d5cc-b8e2-4ed4-9c45-a5f4ab1fb2a0",
"deliveryStatus": "PENDING"
}
}Prop
Type
Node 메일 연동 참고
연락처 인증 이메일도 기존 Node 메일 API를 공용으로 사용합니다. Java에서 Node로 보낼 때 기존 경로는 유지하고, purpose와 attemptId로 용도를 구분합니다.
POST {node.api.base-url}/contract/send-otp-email
Content-Type: application/json{
"toEmail": "creator@example.com",
"otpCode": "123456",
"purpose": "CREATOR_CONTACT_VERIFICATION",
"attemptId": "8a83d5cc-b8e2-4ed4-9c45-a5f4ab1fb2a0"
}Node에서 메일 벤더 webhook 등을 통해 bounce 또는 발송 실패를 확인하면 아래 Java API로 다시 알려줍니다.
POST /ai/internal/contact-verification/email-delivery
Content-Type: application/json{
"attemptId": "8a83d5cc-b8e2-4ed4-9c45-a5f4ab1fb2a0",
"messageId": "provider-message-id",
"status": "BOUNCED",
"reason": "mailbox unavailable"
}현재 연동 기준으로 별도 secret header는 사용하지 않습니다. Java 설정에 internal.contact-verification.secret 값이 있으면 X-Internal-Secret 헤더가 필요합니다. Java는 BOUNCE, BOUNCED, FAIL, FAILED, DELIVERY_FAILED를 실패 상태로 처리하고, 실패가 들어오면 해당 이메일 인증번호를 무효화합니다.
이메일 인증번호 검증
POST /ai/influence/contact-verification/email/verify
Authorization: Bearer {access_token}
Content-Type: application/json
{
"contact": "creator@example.com",
"code": "123456",
"purpose": "ONBOARDING"
}검증 성공 시 해당 이메일이 계정 이메일로 저장되고 이메일 인증 상태가 완료 처리됩니다.
문자 인증번호 발송
POST /ai/influence/contact-verification/sms/send
Authorization: Bearer {access_token}
Content-Type: application/json
{
"contact": "01012345678",
"telDialCode": "+82",
"purpose": "ONBOARDING"
}contact를 비우면 현재 계정에 저장된 전화번호로 발송합니다. SMS 인증은 telDialCode가 +82인 경우에만 지원합니다. +82가 아닌 전화번호는 SMS 인증번호를 발송하지 않고, 요청한 전화번호와 telDialCode를 계정에 저장한 뒤 CONTACT_VERIFICATION_SMS_UNSUPPORTED를 반환합니다. 프론트는 이 응답의 data.saved=true를 전화번호 저장 완료로 보고 이메일 인증만 진행합니다.
문자 인증번호 검증
POST /ai/influence/contact-verification/sms/verify
Authorization: Bearer {access_token}
Content-Type: application/json
{
"contact": "01012345678",
"telDialCode": "+82",
"code": "123456",
"purpose": "ONBOARDING"
}검증 성공 시 해당 전화번호와 telDialCode가 계정에 저장되고 전화번호 인증 상태가 완료 처리됩니다.
에러 응답
모든 에러는 HTTP 200으로 내려오고, 실제 상태는 body의 status/code를 확인합니다.
인증 필요
{
"status": 403,
"code": "CONTACT_VERIFICATION_REQUIRED",
"message": "이메일 인증이 필요합니다.",
"data": {
"channel": "EMAIL",
"maskedContact": "c****@example.com"
}
}인증번호 불일치
{
"status": 400,
"code": "CONTACT_VERIFICATION_INVALID",
"message": "인증번호가 올바르지 않습니다.",
"data": null
}인증번호 만료
{
"status": 400,
"code": "CONTACT_VERIFICATION_EXPIRED",
"message": "인증번호가 만료되었습니다. 다시 요청해주세요.",
"data": null
}이메일 형식 오류
{
"status": 400,
"code": "CONTACT_VERIFICATION_INVALID_EMAIL_FORMAT",
"message": "유효한 이메일 주소를 입력해주세요.",
"data": null
}이메일 bounce
{
"status": 400,
"code": "CONTACT_VERIFICATION_EMAIL_BOUNCED",
"message": "이메일 수신이 실패했습니다. 다른 이메일로 다시 인증해주세요.",
"data": {
"channel": "EMAIL",
"maskedContact": "c****@example.com"
}
}SMS 미지원 국가번호
{
"status": 400,
"code": "CONTACT_VERIFICATION_SMS_UNSUPPORTED",
"message": "해외 전화번호는 SMS 인증 대상이 아닙니다. 이메일 인증을 진행해주세요.",
"data": {
"channel": "SMS",
"maskedContact": "901****5678",
"telDialCode": "+81",
"saved": true
}
}요청 제한
{
"status": 429,
"code": "CONTACT_VERIFICATION_RATE_LIMIT",
"message": "인증번호 요청이 너무 많습니다. 잠시 후 다시 시도해주세요.",
"data": null
}프론트 적용 순서
- 대시보드 진입 시
GET /contact-verification/status호출 verificationRequired=true이면 인증 모달 노출- 이메일 인증번호 발송 및 검증
telDialCode=+82이고telVerified=false인 경우만 문자 인증번호 발송 및 검증- 상태 재조회 후
verificationRequired=false이면 대시보드 진입 허용
이메일 bounce 처리
attemptId는 프론트가 저장하거나 재전송할 필요가 없습니다.- 이메일 bounce는 비동기로 반영됩니다.
- 최소 구현은
email/verify응답에서CONTACT_VERIFICATION_EMAIL_BOUNCED를 처리하면 됩니다. - 더 나은 UX가 필요하면 이메일 발송 후 10
15초 동안만3초 간격으로 짧게 polling합니다.status를 2 lastEmailDeliveryStatus가BOUNCED또는FAILED이면 인증번호 입력 UI 대신 다른 이메일 입력을 유도합니다.
권장 문구:
인증 메일을 받을 수 없는 이메일입니다. 다른 이메일 주소로 다시 인증해주세요.
마이페이지 변경
마이페이지에서 이메일을 변경할 때는 새 이메일로 인증번호를 발송하고, 검증 성공 후 저장 API를 호출해야 합니다. 전화번호를 변경할 때는 원칙적으로 tel과 telDialCode를 함께 보냅니다. 다만 sms/verify 또는 해외번호 sms/send에서 이미 저장된 전화번호를 그대로 다시 저장하는 경우에는 telDialCode를 생략해도 서버가 기존 저장값을 재사용합니다. 전화번호가 바뀌었는데 telDialCode가 없으면 기존 저장된 국가번호를 사용하고, 기존 국가번호도 없으면 CONTACT_VERIFICATION_TARGET_REQUIRED가 반환됩니다. telDialCode=+82이면 SMS 인증이 필요하고, +82가 아니면 SMS 인증을 요구하지 않습니다. 온보딩 완료 요청도 배송지 연락처와 함께 telDialCode를 보내야 합니다. 인증되지 않은 필수 연락처로 저장을 시도하면 CONTACT_VERIFICATION_REQUIRED가 반환됩니다.