FaceMatch REST API

API 문서

별도 SDK 없이 표준 HTTP 한 번 호출로 기존 앱·키오스크·출입장치에 얼굴인증을 붙입니다. 모든 요청은 multipart/form-data로 이미지를 전송하며, 회사코드 헤더 한 줄로 회사별 데이터가 완전히 분리됩니다.

Base URL
https://facematch.team1985.com
Content-Type
multipart/form-data
Authentication

인증 · 회사코드

모든 엔드포인트는 발급받은 회사코드(License Key)를 요구합니다. X-License-Key 헤더로 전달하는 것을 권장하며, 폼 필드 license_key로도 받습니다.

요청 헤더
X-License-Key: <회사코드>
회사코드는 관리자 콘솔에서 회사 등록 시 자동 발급됩니다. 같은 회사코드로 등록·인식된 데이터끼리만 매칭되며, 다른 회사의 데이터에는 접근할 수 없습니다.
Errors

에러 응답

실패 시 status 필드가 "error" 또는 "warning"으로 내려갑니다. 회사코드 관련 오류는 HTTP 상태코드로도 구분됩니다.

코드status의미
200success정상 처리 (인식 성공 여부는 recognized 필드로 구분)
200warning얼굴 미감지·전경 얼굴 없음 등 — 요청은 정상이나 처리할 얼굴이 없음
400error이미지를 읽을 수 없음 (손상·미지원 형식)
401error회사코드(license_key) 누락
403error유효하지 않은 회사코드 · 라이선스 유효기간 만료
Concepts

식별키 · 부가정보

FaceMatch는 평문 개인정보를 보관하지 않는 PII-free 구조입니다. 회원은 파트너가 정한 키로 식별하고, 이름·연락처 등은 암호화된 부가정보로만 저장됩니다.

개념설명
unique_key파트너 시스템의 회원 식별자(실질 유일키). 회원번호·ID 등 아무 값이나 사용. 매칭 성공 시 이 값이 그대로 반환되므로 자체 DB 조회 키로 씁니다.
group_key회사 하위의 그룹 구분값(지점·반 등, 선택). 등록 시 지정하면 탐지 시 group_key로 해당 그룹 소속만 후보로 좁힐 수 있습니다. 재등록(갱신) 판정 범위 = (회사, group_key, unique_key).
extra자유 형식 부가정보 JSON(이름·연락처 등). AES-256-GCM 으로 암호화 저장되고 매칭 성공 시 복호화되어 그대로 반환됩니다 — 자체 서버가 없는 고객사도 매칭 결과만으로 회원정보를 회수할 수 있습니다.

POST 얼굴등록 · 멀티임베딩

얼굴 이미지 1~N장에서 512차원 특징을 추출해 저장합니다. 정면·좌우·상하 등 여러 방향을 함께 등록하면 인식률이 크게 올라갑니다(권장 5장: 정면+네 모서리). 같은 (회사, group_key, unique_key)가 이미 있으면 갱신(update)됩니다.

POST/api/register

요청 파라미터 · multipart/form-data

필드타입설명
files 필수file[]얼굴 이미지 1~N장 (jpg/png). 같은 이름 files 필드를 반복 전송
unique_key 필수string파트너 회원 식별키. 중복 시 기존 등록을 갱신
poses 선택string이미지 순서와 1:1인 콤마구분 방향 라벨. 예: frontal,left_up,right_up,left_down,right_down
group_key 선택string그룹 구분값(지점·반 등). 탐지 시 같은 값으로 후보를 좁힐 수 있음
extra 선택JSON string부가정보 JSON 객체. 암호화 저장 후 매칭 시 복호화 반환. 예: {"name":"홍길동","grade":"A"}
license_key 선택string회사코드 (헤더 X-License-Key 미사용 시)
요청 예시 · 5방향 등록
curl -X POST https://facematch.team1985.com/api/register \
  -H "X-License-Key: <회사코드>" \
  -F files=@front.jpg -F files=@lu.jpg -F files=@ru.jpg \
  -F files=@ld.jpg -F files=@rd.jpg \
  -F poses=frontal,left_up,right_up,left_down,right_down \
  -F unique_key=member-1024 \
  -F group_key=A \
  -F extra='{"name":"홍길동","mobile":"01012345678"}'
응답 200
{
  "status": "success",
  "action": "inserted",   // 또는 "updated"
  "face_id": 12,
  "unique_key": "member-1024",
  "group_key": "A",
  "received": 5,   // 받은 이미지 수
  "accepted": 5,   // 품질게이트 통과·저장 수
  "poses": ["frontal","left_up","right_up","left_down","right_down"],
  "face": {
    "bbox": [142,88,320,300],
    "det_score": 0.99
  }
}
모든 프레임에서 얼굴을 감지하지 못하면 { "status": "warning", "message": "얼굴을 감지하지 못했습니다." } 가 반환됩니다. 일부만 실패하면 통과한 장수만 accepted에 반영됩니다.

POST 얼굴탐지 · 출석체크 1:N

이미지 1장에서 가장 가까운 전경 얼굴 1명만 골라 등록자와 1:N 매칭합니다. 여러 명이 줄 서 있어도 본인 1명만 인식해 출입·출석에 적합합니다.

POST/api/detect

요청 파라미터 · multipart/form-data

필드타입설명
file 필수file인식할 이미지
group_key 선택string지정 시 해당 그룹에 등록된 얼굴만 후보로 매칭
threshold 선택float매칭 임계값. 미지정 시 회사 설정 → 전역 기본(0.35) 순으로 적용
margin 선택float1·2위 유사도 차이 최소값(오인식 방지). 미지정 시 회사 설정 → 전역 기본(0.05)
min_face_ratio 선택float전경 얼굴로 인정할 최소 크기 비율 (기본 0.10)
license_key 선택string회사코드 (헤더 미사용 시)
요청 예시
curl -X POST https://facematch.team1985.com/api/detect \
  -H "X-License-Key: <회사코드>" \
  -F file=@photo.jpg \
  -F group_key=A
응답 200
{
  "status": "success",
  "recognized": true,
  "similarity": 0.987,   // 1위 유사도
  "similarity2": 0.141,  // 2위 유사도
  "gap": 0.846,          // 1-2위 차이
  "ambiguous": false,    // 닮은 사람과 구분 애매 여부
  "verified_by": null,      // 2차 모델(buffalo_l)로 확정 시 모델명, 아니면 null
  "threshold": 0.35,
  "margin": 0.05,
  "elapsed_ms": 176,
  "person": {
    "id": 12,
    "unique_key": "member-1024",
    "group_key": "A",
    "extra": { "name": "홍길동", "mobile": "01012345678" }
  },
  "num_faces_in_frame": 1
}
recognized: false여도 status는 success입니다 — 인식 여부는 항상 recognized 필드로 판단하세요. 1·2위 유사도 차이가 margin보다 작으면 ambiguous: true와 함께 인식이 보류됩니다(재촬영 유도 권장). 응답에는 선택된 얼굴 좌표(selected)와 프레임 내 전체 얼굴(all_faces)도 포함됩니다.

멀티모델 2차검증 & 후보 반환

내부적으로 1차 모델이 애매(ambiguous)로 판정한 건은 변별력이 더 높은 2차 모델(buffalo_l)로 자동 재검증합니다. 2차가 확정하면 recognized: true + verified_by: "buffalo_l"로 응답합니다(앱은 그대로 두어도 됩니다 — 필드만 추가). 일란성 쌍둥이처럼 얼굴만으로 자동 확정이 불가한 경우에는 recognized는 false로 두되, 아래처럼 후보 명단(candidates)을 함께 내려줍니다. 도입사에서 이 후보로 최종 선택 UI(둘 중 택1 등)를 구성할 수 있습니다.

애매 판정 시 응답 (후보 반환)
{
  "status": "success",
  "recognized": false,   // 자동 확정 안 함
  "ambiguous": true,
  "verified_by": null,
  "gap": 0.012,
  "message": "닮은 사람과 구분이 어렵습니다. 한 번 더 촬영해 주세요.",
  "candidates": [       // '이 중 하나' 후보(top-2)
    { "unique_key": "member-1024", "group_key": "A",
      "extra": { "name": "홍길동" }, "similarity": 0.712 },
    { "unique_key": "member-1025", "group_key": "A",
      "extra": { "name": "홍길순" }, "similarity": 0.700 }
  ]
}
활용 가이드
  • candidates는 ambiguous: true일 때만 포함됩니다.
  • 각 후보 형태는 person과 동일 + similarity(유사도) 추가.
  • recognized는 여전히 false — 서버는 자동 확정하지 않습니다.
  • 후보로 최종 선택 UI를 만들지, 재촬영을 유도할지는 도입사 정책입니다.
  • 기존 앱은 모르는 필드를 무시하므로 하위호환됩니다.

POST 다중탐지 · N:N

이미지 속 모든 얼굴을 탐지해 각자 1:N 매칭합니다. CCTV·단체 프레임에서 "지금 화면에 누가 보이는지" 명단을 한 번에 받습니다.

POST/api/multi-detect

요청 파라미터 · multipart/form-data

필드타입설명
file 필수file탐지할 이미지
group_key 선택string지정 시 해당 그룹에 등록된 얼굴만 후보로 매칭
threshold 선택float매칭 임계값. 미지정 시 회사 설정 → 전역 기본(0.35)
license_key 선택string회사코드 (헤더 미사용 시)
요청 예시
curl -X POST https://facematch.team1985.com/api/multi-detect \
  -H "X-License-Key: <회사코드>" \
  -F file=@crowd.jpg
응답 200
{
  "status": "success",
  "num_faces": 3,
  "recognized_count": 2,
  "elapsed_ms": 880,
  "faces": [
    {
      "recognized": true,
      "similarity": 0.987,
      "person": {
        "unique_key": "member-1024",
        "extra": { "name": "홍길동" }
      },
      "bbox": [88,60,240,250]
    },
    {
      "recognized": false,
      "similarity": 0.21,
      "person": null,
      "bbox": [410,90,560,270]
    }
  ]
}

GET 등록 목록

자기 회사에 등록된 얼굴 전체 목록입니다. 부가정보는 복호화되어 내려가고, name/mobile은 부가정보에서 병합된 값입니다.

GET/api/faces
요청 예시
curl https://facematch.team1985.com/api/faces \
  -H "X-License-Key: <회사코드>"
응답 200
{
  "status": "success",
  "list": [
    {
      "id": 12,
      "unique_key": "member-1024",
      "group_key": "A",
      "extra": { "name": "홍길동", "mobile": "01012345678" },
      "name": "홍길동",
      "mobile": "01012345678",
      "image_path": "/images/reg_....jpg",
      "created_at": 1782301234
    }
  ]
}

GET 등록 상세 · 방향별 이미지

등록 얼굴 1건의 방향별 등록 이미지와 복호화된 부가정보를 조회합니다.

GET/api/faces/{face_id}/poses
요청 예시
curl https://facematch.team1985.com/api/faces/12/poses \
  -H "X-License-Key: <회사코드>"
응답 200
{
  "status": "success",
  "unique_key": "member-1024",
  "group_key": "A",
  "extra": { "name": "홍길동" },
  "list": [
    { "pose": "frontal", "image_path": "/images/...", "quality": 312.5 },
    { "pose": "left_up", "image_path": "/images/...", "quality": 287.1 }
  ]
}

POST 등록 수정

이름·연락처를 수정합니다. 값은 암호화된 부가정보에 병합 저장되며, 임베딩·사진은 유지됩니다.

POST/api/faces/{face_id}
필드타입설명
name 선택string이름. 빈 값이면 부가정보에서 제거
mobile 선택string연락처. 빈 값이면 부가정보에서 제거
요청 예시
curl -X POST https://facematch.team1985.com/api/faces/12 \
  -H "X-License-Key: <회사코드>" \
  -F name=홍길동 -F mobile=01012345678

DELETE 등록 삭제

등록 얼굴을 삭제합니다. 삭제 즉시 인식 대상에서 제외되며, 과거 매칭 로그의 증적 이미지는 보존됩니다(소프트 삭제). 등록 시 사용한 unique_key(식별키)로 삭제하는 것이 기본이며, 내부 face_id를 아는 경우 ID 삭제도 가능합니다.

DELETE/api/faces/by-key/{unique_key}
요청 예시 — 식별키로 삭제 (권장)
curl -X DELETE "https://facematch.team1985.com/api/faces/by-key/member-1024?group_key=gangnam" \
  -H "X-License-Key: <회사코드>"

# 응답
{ "status": "success", "deleted": 1, "face_ids": [12] }
group_key는 옵션입니다 — 전달하면 해당 그룹 스코프의 등록만 삭제하고, 생략하면 회사 내 해당 식별키의 등록을 모두 삭제합니다(응답의 deleted가 삭제 건수). 일치하는 등록이 없으면 404를 반환합니다.
DELETE/api/faces/{face_id}
요청 예시 — 내부 ID로 삭제
curl -X DELETE https://facematch.team1985.com/api/faces/12 \
  -H "X-License-Key: <회사코드>"
SONIC

음파 측정 · 개요

매장 스피커가 사람 귀에 들리지 않는 초음파로 구역 ID를 방송하고, 고객 앱이 그것을 듣고 방문·체류를 기록합니다. 오프라인 캠페인의 노출과 방문을 잇는 용도이며, 본인인증 수단이 아닙니다.

얼굴인식과 같은 회사코드(license_key)를 그대로 사용합니다. 별도 키 발급이 필요 없습니다.

동작 구조

매장 스피커  ──(초음파 존ID, 1.68초마다 반복)──▶  고객 앱 마이크
                                                      │
                                       진입/이탈이 확정될 때만 ▼
                                              POST /api/sonic/event

복조기는 존ID를 1.68초마다 계속 읽지만, 서버로는 진입·이탈 두 시점에만 보냅니다. 광고주가 보는 지표는 수신 횟수가 아니라 방문 수와 체류 시간이기 때문입니다.

신호 규격

항목값
변조BFSK 연속위상 · 0 = 18kHz / 1 = 21kHz
샘플레이트44,100 Hz
심볼 길이30 ms
프레임프리앰블 16b + SFD 8b + 존ID 24b + CRC-8 8b = 56b (1.68초)
존ID 범위0 ~ 16,777,215

존 등록과 송출 음원

존은 고객사가 API로 직접 등록합니다. 얼굴등록과 같은 방식으로, 고객사가 정한 식별키(zone_key)로 존을 만들면 방송용 24비트 zone_id는 서버가 배정해 돌려줍니다 — 숫자 공간을 직접 관리할 필요가 없습니다.

등록한 존의 송출용 WAV를 API로 내려받을 수 있어, 매장에 앱을 설치하지 않고 기존 BGM 시스템이나 사이니지에 그대로 얹을 수 있습니다. 관리 콘솔(/admin → 음파 측정)에서도 같은 작업과 지표 확인이 가능합니다.

수신 시 반드시 꺼야 하는 것 — 자동이득(AGC) · 에코제거(AEC) · 잡음억제(NS). 셋 다 음성대역(8kHz 이하)을 전제로 만든 처리라 18kHz 이상을 잡음으로 보고 깎아냅니다. 안드로이드는 오디오 소스를 UNPROCESSED로 지정하세요. iOS는 백그라운드 마이크 접근이 불가하므로 앱이 열려 있는 동안만 수신됩니다.

POST 존 등록 · 등록 / 갱신

측정할 구역을 등록합니다. 얼굴등록과 같은 구조로, 같은 zone_key로 다시 보내면 갱신됩니다. 이때 zone_id는 유지되므로 이미 매장에서 송출 중인 음원을 다시 만들 필요가 없습니다.

POST/api/sonic/zones
필드타입설명
zone_key 필수string고객사가 정한 존 식별키. 매장·구역을 구분할 수 있는 값이면 무엇이든
name 선택string구역명. 예: 1층 입구
place_name 선택string매장명. 예: 강남점
campaign 선택string캠페인명. 지표를 캠페인 단위로 묶어 볼 때 사용
요청 예시
curl -X POST https://facematch.co.kr/api/sonic/zones \
  -H "X-License-Key: <회사코드>" \
  -F zone_key=gangnam-1f-entrance \
  -F "name=1층 입구" \
  -F place_name=강남점 \
  -F campaign=여름세일
응답 200
{
  "status": "success",
  "action": "inserted",   // 또는 "updated"
  "zone_key": "gangnam-1f-entrance",
  "zone_id": 1001    // 서버가 배정한 방송용 ID
}

GET 존 목록

회사에 등록된 존 목록입니다. 앱은 수신한 존ID를 사람이 읽는 이름으로 바꿔 보여줄 때 사용합니다.

GET/api/sonic/zones
요청 예시
curl https://facematch.co.kr/api/sonic/zones \
  -H "X-License-Key: <회사코드>"
응답 200
{
  "status": "success",
  "zones": [
    {
      "zone_id": 1001,
      "zone_key": "gangnam-1f-entrance",
      "name": "1층 입구",
      "place_name": "강남점",
      "campaign": "여름세일"
    }
  ]
}

GET 송출 음원 · WAV

존ID를 반복 송출하는 WAV를 내려받습니다. 매장에 별도 송신 장비나 앱이 필요 없습니다 — 기존 BGM 시스템·사이니지에서 이 파일을 반복 재생하면 됩니다.

GET/api/sonic/zones/by-key/{zone_key}/wav
파라미터타입설명
minutes 선택float음원 길이(분). 기본 3, 범위 0.5 ~ 30
요청 예시
curl "https://facematch.co.kr/api/sonic/zones/by-key/gangnam-1f-entrance/wav?minutes=10" \
  -H "X-License-Key: <회사코드>" \
  -o zone_gangnam_1f.wav
재생 시 주의 — 18~21kHz는 대부분의 스피커에서 출력이 크게 떨어집니다. 볼륨을 충분히 올리고, 블루투스 스피커는 피하세요(SBC·AAC 코덱이 16kHz 위를 버려 신호가 사라집니다). 유선 또는 내장 스피커를 쓰고, 이퀄라이저·음장 효과는 끄세요.

DELETE 존 삭제

존을 삭제합니다. 이미 쌓인 방문 지표는 그대로 유지됩니다 — 캠페인이 끝나 존을 정리해도 지난 실적 보고 근거는 남아야 하기 때문입니다.

DELETE/api/sonic/zones/by-key/{zone_key}
요청 예시
curl -X DELETE https://facematch.co.kr/api/sonic/zones/by-key/gangnam-1f-entrance \
  -H "X-License-Key: <회사코드>"

# 응답
{ "status": "success", "deleted": 1, "zone_id": 1001 }

POST 방문 이벤트 · 진입 / 이탈

존 진입과 이탈을 보고합니다. Content-Type: application/json이며, 다른 API와 달리 폼이 아닌 JSON 본문을 받습니다.

POST/api/sonic/event

파라미터

이름타입설명
license_keystring회사코드. X-License-Key 헤더로도 가능
device_idstring앱 설치 식별자. 순 방문자(UV) 집계 기준
zone_idint수신한 존ID. 콘솔에 등록되지 않은 값은 404
eventstringenter 또는 exit
entered_atint진입 시각(epoch 초). 진입·이탈을 잇는 상관키라 두 요청에서 같은 값
last_seen_atint마지막 수신 시각(epoch 초). exit 에만
dwell_secint체류 시간(초). exit 에만
frame_countint수신한 프레임 수. 신호 품질 참고용
avg_snr_dbfloat평균 심볼 SNR(dB). 신호 품질 참고용
진입
curl -X POST https://facematch.co.kr/api/sonic/event \
  -H "Content-Type: application/json" \
  -d '{
    "license_key": "<회사코드>",
    "device_id": "a3f9-...",
    "zone_id": 1001,
    "event": "enter",
    "entered_at": 1787570000
  }'
이탈
curl -X POST https://facematch.co.kr/api/sonic/event \
  -H "Content-Type: application/json" \
  -d '{
    "license_key": "<회사코드>",
    "device_id": "a3f9-...",
    "zone_id": 1001,
    "event": "exit",
    "entered_at": 1787570000,
    "last_seen_at": 1787570120,
    "dwell_sec": 120,
    "frame_count": 70,
    "avg_snr_db": 19.4
  }'
진입 전송이 실패해도 방문은 집계됩니다. 이탈만 도착하면 서버가 완결된 방문 1건으로 기록하므로, 통신 실패로 방문 수가 줄어들지 않습니다. 반대로 이탈이 오지 않은 방문은 체류 중으로 남고 평균 체류 계산에서는 제외됩니다.

GET 매칭 로그

최근 인식 시도 이력입니다. 어떤 방향(matched_pose) 등록 이미지가 매칭을 이겼는지, 시도 사진(probe_image)과 매칭된 등록 사진(matched_image)까지 증적으로 제공됩니다.

GET/api/logs?limit=100
요청 예시
curl "https://facematch.team1985.com/api/logs?limit=50" \
  -H "X-License-Key: <회사코드>"
응답 200
{
  "status": "success",
  "list": [
    {
      "id": 224,
      "mode": "detect",   // detect | multi
      "recognized": true,
      "name": "홍길동",
      "unique_key": "member-1024",
      "group_key": "A",
      "similarity": 0.723,
      "threshold": 0.35,
      "matched_pose": "left_up",
      "probe_image": "/images/probe_....jpg",
      "matched_image": "/images/reg_....jpg",
      "elapsed_ms": 180,
      "num_faces": 1,
      "created_at": 1782301234
    }
  ]
}
FaceMatch · 얼굴인식 본인인증 플랫폼  ·  OpenAPI 명세 · 연동 가이드 · SDK
https://facematch.team1985.com · © 2026 team1985