옴니ID 개발자 문서
가맹점 콘솔
문서 목록

OmniID API

스펙최종 수정 2026년 7월 31일

가맹점 Backend에서 사용하는 옴니ID 외부 API 명세입니다. 인증, 세션 생성, 결과 조회를 정의합니다.

목차

가맹점 Backend가 호출하는 OmniID 외부 API 스펙입니다.

Base URL#

환경Base URL
testhttps://api.test.omniid.ai.kr
productionhttps://api.omniid.ai.kr

모든 구간은 HTTPS입니다. HTTP 요청은 HTTPS로 redirect됩니다. Hosted UI 주소는 직접 조립하지 말고 세션 생성 응답의 popup_url을 그대로 사용하십시오. test/production credential은 분리 발급되며 merchant_id, client_id, client_secret을 환경 간에 혼용하지 않습니다 (연동 가이드 §1).

Authentication#

가맹점 Backend는 OAuth2 client credentials 방식으로 access token을 발급받고, OmniID API 호출 시 Bearer token을 전달합니다.

POST/oauth/token#

Access token을 발급합니다.

Method

POST

Content-Type

application/x-www-form-urlencoded

Request

FieldTypeRequiredDescription
grant_typestringyesclient_credentials 고정
client_idstringyesOmniID가 가맹점에 발급한 client id
client_secretstringyesOmniID가 가맹점에 발급한 client secret
scopestringno공백으로 구분한 scope 목록. 생략 시 client 기본 scope 사용

Example:

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&
client_id=client_merchant_123&
client_secret=secret_xxx&
scope=enrollment:create verification:create result:read

Response 200

{
  "access_token": "eyJ...",
  "token_type": "Bearer",
  "expires_in": 600,
  "scope": "enrollment:create verification:create result:read"
}

Token Claims

Access token은 short-lived JWT로 발급합니다.

ClaimDescription
isstoken issuer
submerchant_id
audomniid-api
exp만료 시각
iat발급 시각
jtitoken id
merchant_idOmniID가 발급한 가맹점 ID
client_idtoken 발급에 사용한 OAuth client ID
scope허용 scope

Error Codes

HTTPCodeDescription
400invalid_request필수 field 누락 또는 형식 오류
400unsupported_grant_typeclient_credentials 외 grant type
401invalid_clientclient 인증 실패
400invalid_scope요청 scope가 허용 범위를 벗어남
429rate_limitedtoken endpoint rate limit 초과

Security Notes

  • client_secret은 브라우저, 모바일 앱, 바이오 인증앱에 노출 금지
  • client_secret과 access token 원문은 로그에 남기기 금지
  • refresh token은 MVP에서 발급하지 않습니다.
  • access token 기본 만료 시간은 10분입니다.
  • 현재 test/production token endpoint 설정은 client_id별 60초에 30회입니다.

Common Headers#

OmniID API 호출에는 다음 header를 사용합니다.

Authorization: Bearer {access_token}
Content-Type: application/json
Idempotency-Key: {uuid}  # state-changing request 권장

응답 추적 (X-Request-Id)#

HTTPS API endpoint가 반환하는 모든 application 응답(성공·실패)에 X-Request-Id 응답 헤더를 반환합니다. 이 값은 OmniID가 요청마다 생성합니다. 가맹점은 CS 문의 시 이 값 또는 session_id를 제출합니다. 에러 body의 request_id는 응답 헤더와 동일합니다. 성공 응답 body에는 별도 request_id 필드를 추가하지 않습니다(헤더가 canonical). HTTP→HTTPS 301 redirect나 reverse proxy 자체 오류는 application 응답에 포함되지 않습니다.

Common Error Response#

{
  "error": {
    "code": "invalid_token",
    "message": "Access token is invalid.",
    "request_id": "req_..."
  }
}

외부 응답에는 provider 내부 payload나 바이오 payload를 포함하지 않습니다.

Common Request Errors#

HTTPCodeDescription
400invalid_requestJSON 등 request body를 해석할 수 없음
413payload_too_largerequest body 크기 제한 초과

JSON body 제한은 1 MiB(1,048,576 bytes), application/x-www-form-urlencoded body 제한은 64 KiB(65,536 bytes)입니다. 두 오류 모두 공통 오류 형식과 X-Request-Id 응답 헤더를 사용합니다.

Scope#

ScopeUsage
enrollment:create등록 세션 생성
verification:create확인 세션 생성
result:read등록/확인 결과 조회
session:cancel세션 취소

POST/v1/enrollment-sessions#

등록 세션을 생성합니다.

Authentication#

Bearer token with enrollment:create

Request#

{
  "merchant_id": "merchant_123",
  "merchant_user_ref": "user_abc",
  "return_url": "https://merchant.example.com/bio/complete",
  "provider_id": "pointlink",
  "type": "fingerprint",
  "finger": "LI",
  "locale": "ko-KR",
  "metadata": {
    "order_ref": "optional-non-sensitive-ref"
  }
}
FieldTypeRequiredDescription
merchant_idstringyes1~128자. token의 merchant_id와 일치해야 함
merchant_user_refstringyes1~256자. 가맹점 로그인 사용자를 나타내는 비식별 불투명 참조값
return_urlstringno최대 2048자의 전체 HTTPS URL(HTTP는 거부). PC popup postMessage target origin 고정용이며 모바일 자동 복귀 계약이나 최종 결과 판단에 사용하지 않음
provider_idstringno1~64자. MVP 기본값 pointlink
typestringno1~32자. MVP 기본값 fingerprint
fingerstringnoPointLink finger code. 예: LI, LM, RI, RM. 등록에서는 앱에 전달되지 않는 참고값입니다. PointLink 딥링크의 finger는 match 전용이며, 등록 시 앱이 보고한 손가락이 가맹점 Bio API 저장 요청으로 전달됩니다. 가맹점은 등록 결과 조회의 result.finger를 저장해 확인 세션에 사용합니다.
localestringno최대 16자. Hosted UI 표시 locale
metadataobjectnoJSON 직렬화 기준 최대 4096자. 비민감 trace용 메타데이터만 허용

merchant_user_ref에는 이메일, 전화번호, 이름, 주민등록번호 등 개인정보 원문을 넣지 않습니다. 가맹점 DB의 무작위 내부 회원 ID(UUID 등)를 권장합니다. 개인정보에서 값을 유도해야 한다면 가맹점만 보관하는 비밀키와 용도 구분값을 사용한 HMAC을 적용하고, 전화번호처럼 경우의 수가 작은 값의 단순 SHA-256 해시는 사용하지 않습니다. metadata에도 features, enc_key, 생체 원본, 주민번호, 카드번호 같은 민감정보를 넣지 않으며, 해당 의미를 가진 최상위 key도 허용되지 않습니다.

Response 201#

{
  "enrollment_session_id": "enr_01J...",
  "session_id": "enr_01J...",
  "mode": "enroll",
  "status": "created",
  "popup_url": "https://verify.omniid.ai.kr/s/enr_01J...?token=hosted_...",
  "expires_at": "2026-07-02T13:10:00Z",
  "provider_id": "pointlink"
}

Error Codes#

HTTPCodeDescription
400invalid_request필수 field 누락 또는 형식 오류
400unsupported_provider지원하지 않는 provider. MVP는 pointlink만 지원
400unsupported_bio_type지원하지 않는 bio type. MVP는 fingerprint만 지원
401invalid_tokentoken 없음, 위조, 만료
403insufficient_scopeenrollment:create scope 없음
403merchant_mismatch요청 merchant_id와 token의 merchant_id 불일치
409idempotency_conflict동일 Idempotency-Key로 다른 body 요청

Idempotency#

Idempotency-Key header 사용을 권장합니다. 같은 key와 같은 request body는 같은 생성 결과를 반환할 수 있습니다.

Security Notes#

  • 생성된 세션은 created 상태와 expires_at을 가집니다.
  • 기본 만료 시간은 10분이고 hard max는 30분입니다.
  • Hosted UI token은 OAuth access token과 별개이며, 브라우저에서 OmniID Hosted 화면 접근에만 사용합니다.

POST/v1/verification-sessions#

확인 세션을 생성합니다.

Authentication#

Bearer token with verification:create

Request#

{
  "merchant_id": "merchant_123",
  "merchant_user_ref": "user_abc",
  "return_url": "https://merchant.example.com/bio/complete",
  "provider_id": "pointlink",
  "type": "fingerprint",
  "finger": "LI",
  "locale": "ko-KR",
  "metadata": {
    "purpose": "login-step-up"
  }
}
FieldTypeRequiredDescription
merchant_idstringyes1~128자. token의 merchant_id와 일치해야 함
merchant_user_refstringyes1~256자. 가맹점 로그인 사용자를 나타내는 비식별 불투명 참조값
return_urlstringno최대 2048자의 전체 HTTPS URL(HTTP는 거부). PC popup postMessage target origin 고정용이며 모바일 자동 복귀 계약이나 최종 결과 판단에 사용하지 않음
provider_idstringno1~64자. MVP 기본값 pointlink
typestringno1~32자. MVP 기본값 fingerprint
fingerstringyes확인할 등록 손가락. 등록 결과 조회의 result.finger 값을 사용합니다. PointLink 딥링크의 finger는 match 전용·서버 지정이므로(가이드 v1.0) 생략 불가 — 생략 시 invalid_request (2026-07-08 필수화)
localestringno최대 16자. Hosted UI 표시 locale
metadataobjectnoJSON 직렬화 기준 최대 4096자. 비민감 trace용 메타데이터만 허용

Response 201#

{
  "verification_session_id": "ver_01J...",
  "session_id": "ver_01J...",
  "mode": "verify",
  "status": "created",
  "popup_url": "https://verify.omniid.ai.kr/s/ver_01J...?token=hosted_...",
  "expires_at": "2026-07-02T13:10:00Z",
  "provider_id": "pointlink"
}

Error Codes#

HTTPCodeDescription
400invalid_request필수 field 누락 또는 형식 오류
400unsupported_provider지원하지 않는 provider. MVP는 pointlink만 지원
400unsupported_bio_type지원하지 않는 bio type. MVP는 fingerprint만 지원
401invalid_tokentoken 없음, 위조, 만료
403insufficient_scopeverification:create scope 없음
403merchant_mismatch요청 merchant_id와 token의 merchant_id 불일치
409idempotency_conflict동일 Idempotency-Key로 다른 body 요청

GET/v1/sessions/{session_id}/result#

등록/확인 세션의 결과를 조회합니다.

가맹점 Frontend의 완료 화면은 최종 성공 근거가 아닙니다. 가맹점 Backend는 이 API로 최종 상태를 확인해야 합니다.

Authentication#

Bearer token with result:read

Query Parameters#

FieldTypeRequiredDescription
merchant_user_refstringyes현재 로그인 사용자와 세션 사용자 일치 확인용. 세션 생성 시 사용한 비식별 참조값과 동일해야 함

Request Example#

GET /v1/sessions/ver_01J.../result?merchant_user_ref=user_abc
Authorization: Bearer eyJ...

Response 200 - Enrollment Succeeded#

{
  "session_id": "enr_01J...",
  "mode": "enroll",
  "merchant_id": "merchant_123",
  "merchant_user_ref": "user_abc",
  "status": "succeeded",
  "provider_id": "pointlink",
  "result": {
    "enrolled": true,
    "finger": "LI",
    "completed_at": "2026-07-02T13:03:10Z"
  },
  "expires_at": "2026-07-02T13:10:00Z"
}

result.finger는 가맹점 Bio API의 저장 응답에 finger가 있으면 그 값, 없으면 앱 callback의 손가락 값입니다. 세션 생성 시 지정한 참고용 finger와 다를 수 있으므로, 가맹점은 확인 세션에 필요한 손가락을 이 값으로 판단해야 합니다. Bio API가 응답에 finger를 보낼 때는 실제 저장한 요청의 finger와 동일한 값을 반환해야 합니다.

Response 200 - Verification Succeeded#

{
  "session_id": "ver_01J...",
  "mode": "verify",
  "merchant_id": "merchant_123",
  "merchant_user_ref": "user_abc",
  "status": "succeeded",
  "provider_id": "pointlink",
  "result": {
    "matched": true,
    "finger": "LI",
    "completed_at": "2026-07-02T13:03:10Z"
  },
  "expires_at": "2026-07-02T13:10:00Z"
}

Response 200 - Verification Not Matched#

확인 처리가 정상 완료됐지만 바이오가 일치하지 않은 경우입니다. 시스템 처리 실패가 아니므로 session statussucceeded이고, 가맹점은 result.matched=false를 기준으로 서비스 처리를 결정합니다.

{
  "session_id": "ver_01J...",
  "mode": "verify",
  "merchant_id": "merchant_123",
  "merchant_user_ref": "user_abc",
  "status": "succeeded",
  "provider_id": "pointlink",
  "result": {
    "matched": false,
    "finger": "LI",
    "completed_at": "2026-07-02T13:03:10Z"
  },
  "expires_at": "2026-07-02T13:10:00Z"
}

Response 200 - Processing#

{
  "session_id": "ver_01J...",
  "mode": "verify",
  "merchant_id": "merchant_123",
  "merchant_user_ref": "user_abc",
  "status": "processing",
  "provider_id": "pointlink",
  "result": null,
  "expires_at": "2026-07-02T13:10:00Z"
}

Response 200 - Failed / Expired#

statusfailed 또는 expired이면 최상위 failure_code 필드가 포함되고 resultnull입니다.

{
  "session_id": "ver_01J...",
  "mode": "verify",
  "merchant_id": "merchant_123",
  "merchant_user_ref": "user_abc",
  "status": "failed",
  "provider_id": "pointlink",
  "failure_code": "provider_timeout",
  "result": null,
  "expires_at": "2026-07-02T13:10:00Z"
}

failure_code는 비민감 코드만 노출합니다. 값 목록과 각 코드의 재시도 판단·안내 방향은 상태 및 오류 처리가 canonical입니다. expiredfailure_codesession_expired로 고정됩니다. 재시도는 항상 새 세션 생성입니다.

Final Status Values#

succeeded
failed
expired
canceled

Non-final status values may be returned while the flow is active:

created
opened
app_launched
callback_received
processing

Error Codes#

HTTPCodeDescription
400invalid_requestmerchant_user_ref 누락 또는 형식 오류
401invalid_tokentoken 없음, 위조, 만료
403insufficient_scoperesult:read scope 없음
403merchant_mismatchtoken의 merchant_id와 세션 merchant_id 불일치
403merchant_user_ref_mismatchquery의 merchant_user_ref와 세션 사용자 불일치
404session_not_found세션 없음 또는 접근 불가

Security Notes#

  • 결과 응답에는 features, enc_key, 바이오 원본, 바이오 템플릿을 포함하지 않습니다.
  • provider 내부 오류는 표준 error code로 변환합니다.
  • score, decision_threshold는 결과 응답에 노출하지 않습니다. 매칭 유사도는 내부 운영/튜닝 목적으로만 저장하며, 가맹점은 matched만으로 판정합니다.

POST/v1/sessions/{session_id}/cancel#

세션을 사용자 취소 상태로 전환합니다.

가맹점 Backend와 Hosted UI 모두 세션을 취소할 수 있습니다. 현재 허용된 상태 전이에 따라 opened 또는 app_launched 상태에서만 canceled로 전이합니다.

Authentication#

Bearer token with session:cancel

Request#

{
  "merchant_id": "merchant_123",
  "merchant_user_ref": "user_abc",
  "reason": "user_cancel"
}
FieldTypeRequiredDescription
merchant_idstringyes1~128자. token 및 세션의 merchant_id와 일치해야 함
merchant_user_refstringyes1~256자. 세션 생성 시 사용한 비식별 참조값과 일치해야 함
reasonstringno최대 128자의 비민감 취소 사유 코드. 생략 시 user_cancel

Response 200#

{
  "session_id": "ver_01J...",
  "status": "canceled"
}

Error Codes#

HTTPCodeDescription
401invalid_tokentoken 없음, 위조, 만료
403insufficient_scopesession:cancel scope 없음
403merchant_mismatchtoken의 merchant_id와 요청/세션 불일치
403merchant_user_ref_mismatch요청의 merchant_user_ref와 세션 사용자 불일치
404session_not_found세션 없음 또는 접근 불가
409session_finalized이미 최종 상태인 세션
409invalid_session_transition현재 상태에서 취소 전이가 허용되지 않음
400invalid_request요청 body 형식 오류

Hosted UI URL#

세션 생성 응답의 popup_url은 가맹점 Frontend가 PC popup 또는 모바일 현재 창/새 화면으로 여는 OmniID Hosted 화면 URL입니다. 모바일 앱 완료 후 복귀는 manual return을 기본으로 하며, return_url 자동 redirect에 의존하지 않습니다.

https://verify.omniid.ai.kr/s/{session_id}?token={hosted_token}

Hosted token은 다음 용도에만 사용합니다.

  • OmniID Hosted 화면 진입
  • QR 또는 앱 링크 표시
  • 진행 상태 polling
  • 성공/실패/만료/취소 화면 표시

Hosted token은 가맹점 API 인증 수단이 아니며, 최종 성공 처리는 결과 조회 API로만 확정합니다.