Authly 개발자 문서
가맹점 콘솔
문서 목록

Authly API

스펙최종 수정 2026년 9월 18일

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

목차

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

Base URL#

환경Base URL
testhttps://api.test.authly.ai.kr
productionhttps://api.authly.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을 발급받고, Authly API 호출 시 Bearer token을 전달합니다.

POST/oauth/token#

Access token을 발급합니다.

Method

POST

Content-Type

application/x-www-form-urlencoded

Request

FieldTypeRequiredDescription
grant_typestringyesclient_credentials 고정
client_idstringyesAuthly가 가맹점에 발급한 client id
client_secretstringyesAuthly가 가맹점에 발급한 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
audauthly-api
exp만료 시각
iat발급 시각
jtitoken id
merchant_idAuthly가 발급한 가맹점 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#

Authly 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 응답 헤더를 반환합니다. 이 값은 Authly가 요청마다 생성합니다. 가맹점은 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",
  "type": "fingerprint",
  "payload": {
    "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 고정용이며 모바일 자동 복귀 계약이나 최종 결과 판단에 사용하지 않음
typestringno1~32자. MVP 기본값 fingerprint. 인증 방식을 구분하는 값이며, 향후 다른 방식이 추가되면 새 type 값과 그에 맞는 payload 구조가 추가됩니다
payloadobjectnotype에 따라 구조가 달라지는 데이터. type: fingerprint의 구조는 아래와 같습니다
payload.fingerstringnofinger code. 예: LI, LM, RI, RM. 등록에서는 앱에 전달되지 않는 참고값입니다. 인증앱 딥링크의 finger는 match 전용이며, 등록 시 앱이 보고한 손가락이 가맹점 Bio API 저장 요청으로 전달됩니다. 가맹점은 등록 결과 조회의 result.payload.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.authly.ai.kr/s/enr_01J...?token=hosted_...",
  "expires_at": "2026-07-02T13:10:00Z"
}

Error Codes#

HTTPCodeDescription
400invalid_request필수 field 누락 또는 형식 오류
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를 현재 보존 기간인 24시간 안에 다시 보내면 같은 생성 결과를 반환합니다. 같은 key로 다른 body를 보내면 409 idempotency_conflict를 반환합니다.

Security Notes#

  • 생성된 세션은 created 상태와 expires_at을 가집니다.
  • 기본 만료 시간은 10분이고 hard max는 30분입니다.
  • Hosted UI token은 OAuth access token과 별개이며, 브라우저에서 Authly 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",
  "type": "fingerprint",
  "payload": {
    "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 고정용이며 모바일 자동 복귀 계약이나 최종 결과 판단에 사용하지 않음
typestringno1~32자. MVP 기본값 fingerprint
payloadobjectyestype에 따라 구조가 달라지는 데이터. type: fingerprint의 구조는 아래와 같습니다
payload.fingerstringyes인증할 등록 손가락. 등록 결과 조회의 result.payload.finger 값을 사용합니다. 인증앱 딥링크의 finger는 match 전용·서버 지정이므로 생략할 수 없으며, 생략 시 invalid_request를 반환합니다.
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.authly.ai.kr/s/ver_01J...?token=hosted_...",
  "expires_at": "2026-07-02T13:10:00Z"
}

session_id가 등록·인증 세션에 공통으로 사용하는 정본 ID입니다. enrollment_session_idverification_session_id는 세션 종류를 드러내는 호환 필드이며 같은 응답의 session_id와 동일합니다. 결과 조회와 Frontend 연동에는 session_id를 사용합니다.

Error Codes#

HTTPCodeDescription
400invalid_request필수 field 누락 또는 형식 오류
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",
  "result": {
    "enrolled": true,
    "payload": { "finger": "LI" },
    "completed_at": "2026-07-02T13:03:10Z"
  },
  "expires_at": "2026-07-02T13:10:00Z"
}

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

Response 200 - Verification Succeeded#

{
  "session_id": "ver_01J...",
  "mode": "verify",
  "merchant_id": "merchant_123",
  "merchant_user_ref": "user_abc",
  "status": "succeeded",
  "result": {
    "matched": true,
    "payload": { "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",
  "result": {
    "matched": false,
    "payload": { "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",
  "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",
  "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 또는 모바일 현재 창/새 화면으로 여는 Authly Hosted 화면 URL입니다. 모바일 앱 완료 후 복귀는 manual return을 기본으로 하며, return_url 자동 redirect에 의존하지 않습니다.

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

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

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

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