Skip to content

API 키 관리

API 키를 만들고 관리하는 관리자 엔드포인트입니다. - 브라우저 관리자 콘솔: 쿠키 기반 관리자 세션 - 직접 관리자 API를 호출하는 클라이언트: HTTP Basic 인증 - 모든 /api/v1/admin/api-keys* 엔드포인트는 데이터베이스 기반 모드가 필요하며, DB 계층이 비활성화되어 있으면 501을 반환합니다.

브라우저 관리자 세션 엔드포인트: - POST /api/v1/admin/auth/login - GET /api/v1/admin/auth/session - POST /api/v1/admin/auth/logout

키는 해시 형태로 저장합니다. 전체 원본 키는 생성 시점에 한 번만 반환하고, 이후 목록/조회에서는 표시용 메타데이터만 제공합니다.


POST /api/v1/admin/api-keys

새 API 키를 생성합니다. 데이터베이스 기반 모드가 필요하며, DB 계층이 비활성화되어 있으면 501을 반환합니다.

  • 요청 형식: JSON
  • 응답 형식: JSON (ApiKeyCreatedResponse)

요청 본문 (JSON):

{ "name": "운영 앱" }

응답 (201):

{
  "id": "a1b2c3d4-e5f6-...",
  "key": "xyl_7f3a8b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e",
  "name": "운영 앱",
  "key_prefix": "xyl_7f3a8b2",
  "key_suffix": "9b0c1d2e",
  "key_preview": "xyl_7f3a8b2...9b0c1d2e",
  "is_active": true,
  "request_count": 0,
  "last_used_at": null,
  "created_at": "2026-04-06T10:30:00+00:00"
}

예제

curl -u admin:password -X POST \
  https://face-api.xylolabs.com/api/v1/admin/api-keys \
  -H "Content-Type: application/json" \
  -d '{"name": "운영 앱"}'
import requests
from requests.auth import HTTPBasicAuth

response = requests.post(
    "https://face-api.xylolabs.com/api/v1/admin/api-keys",
    auth=HTTPBasicAuth("admin", "password"),
    json={"name": "운영 앱"},
)
print(response.json())
const basic = btoa("admin:password");
const response = await fetch("https://face-api.xylolabs.com/api/v1/admin/api-keys", {
  method: "POST",
  headers: {
    Authorization: `Basic ${basic}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ name: "운영 앱" }),
});
console.log(await response.json());
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Basic",
    Convert.ToBase64String(Encoding.ASCII.GetBytes("admin:password"))
);

using var content = new StringContent("{\"name\":\"운영 앱\"}", Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://face-api.xylolabs.com/api/v1/admin/api-keys", content);
Console.WriteLine(await response.Content.ReadAsStringAsync());

GET /api/v1/admin/api-keys

API 키 전체 목록과 사용량을 조회합니다. 전체 원본 키는 반환하지 않고 메타데이터만 돌려줍니다. 데이터베이스 기반 모드가 필요하며, DB 계층이 비활성화되어 있으면 501을 반환합니다.

  • 요청 형식: 없음(쿼리 문자열만 사용)
  • 응답 형식: JSON (PaginatedResponse[ApiKeyResponse])
파라미터 타입 기본값 설명
page int 1 페이지 번호
per_page int 20 페이지당 항목 수 (1-100)
curl -u admin:password https://face-api.xylolabs.com/api/v1/admin/api-keys
import requests
from requests.auth import HTTPBasicAuth

response = requests.get(
    "https://face-api.xylolabs.com/api/v1/admin/api-keys",
    auth=HTTPBasicAuth("admin", "password"),
)
print(response.json())
const basic = btoa("admin:password");
const response = await fetch("https://face-api.xylolabs.com/api/v1/admin/api-keys", {
  headers: { Authorization: `Basic ${basic}` },
});
console.log(await response.json());
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Basic",
    Convert.ToBase64String(Encoding.ASCII.GetBytes("admin:password"))
);

var response = await client.GetAsync("https://face-api.xylolabs.com/api/v1/admin/api-keys");
Console.WriteLine(await response.Content.ReadAsStringAsync());
{
  "items": [
    {
      "id": "a1b2c3d4-e5f6-...",
      "name": "운영 앱",
      "key_prefix": "xyl_7f3a8b2",
      "key_suffix": "9b0c1d2e",
      "key_preview": "xyl_7f3a8b2...9b0c1d2e",
      "is_active": true,
      "request_count": 142,
      "last_used_at": "2026-04-12T10:30:00+00:00",
      "created_at": "2026-04-06T10:30:00+00:00"
    }
  ],
  "total": 1,
  "page": 1,
  "per_page": 20,
  "pages": 1
}

GET /api/v1/admin/api-keys/{key_id}

특정 API 키의 상세 정보와 사용량을 조회합니다. 전체 원본 키는 반환하지 않고 메타데이터만 돌려줍니다. 데이터베이스 기반 모드가 필요하며, DB 계층이 비활성화되어 있으면 501을 반환합니다.

  • 요청 형식: 없음
  • 응답 형식: JSON (ApiKeyResponse)
curl -u admin:password https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-...
import requests
from requests.auth import HTTPBasicAuth

response = requests.get(
    "https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-...",
    auth=HTTPBasicAuth("admin", "password"),
)
print(response.json())
const basic = btoa("admin:password");
const response = await fetch("https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-...", {
  headers: { Authorization: `Basic ${basic}` },
});
console.log(await response.json());
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Basic",
    Convert.ToBase64String(Encoding.ASCII.GetBytes("admin:password"))
);

var response = await client.GetAsync("https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-...");
Console.WriteLine(await response.Content.ReadAsStringAsync());
{
  "id": "a1b2c3d4-e5f6-...",
  "name": "운영 앱",
  "key_prefix": "xyl_7f3a8b2",
  "key_suffix": "9b0c1d2e",
  "key_preview": "xyl_7f3a8b2...9b0c1d2e",
  "is_active": true,
  "request_count": 142,
  "last_used_at": "2026-04-12T10:30:00+00:00",
  "created_at": "2026-04-06T10:30:00+00:00"
}

PATCH /api/v1/admin/api-keys/{key_id}

키 이름을 변경하거나 비활성화/재활성화합니다. 데이터베이스 기반 모드가 필요하며, DB 계층이 비활성화되어 있으면 501을 반환합니다.

  • 요청 형식: JSON
  • 응답 형식: JSON (ApiKeyResponse)
{ "name": "새 이름", "is_active": false }

키 비활성화

curl -u admin:password -X PATCH \
  https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-... \
  -H "Content-Type: application/json" \
  -d '{"is_active": false}'
import requests
from requests.auth import HTTPBasicAuth

response = requests.patch(
    "https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-...",
    auth=HTTPBasicAuth("admin", "password"),
    json={"is_active": False},
)
print(response.json())
const basic = btoa("admin:password");
const response = await fetch("https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-...", {
  method: "PATCH",
  headers: {
    Authorization: `Basic ${basic}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ is_active: false }),
});
console.log(await response.json());
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Basic",
    Convert.ToBase64String(Encoding.ASCII.GetBytes("admin:password"))
);

using var request = new HttpRequestMessage(new HttpMethod("PATCH"), "https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-...")
{
    Content = new StringContent("{\"is_active\":false}", Encoding.UTF8, "application/json"),
};
var response = await client.SendAsync(request);
Console.WriteLine(await response.Content.ReadAsStringAsync());

DELETE /api/v1/admin/api-keys/{key_id}

API 키를 영구 삭제합니다. 데이터베이스 기반 모드가 필요하며, DB 계층이 비활성화되어 있으면 501을 반환합니다. 성공 시 204를 반환합니다.

  • 요청 형식: 없음
  • 응답 형식: 빈 바디 (204 No Content)
curl -u admin:password -X DELETE \
  https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-...
import requests
from requests.auth import HTTPBasicAuth

response = requests.delete(
    "https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-...",
    auth=HTTPBasicAuth("admin", "password"),
)
print(response.status_code)
const basic = btoa("admin:password");
const response = await fetch("https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-...", {
  method: "DELETE",
  headers: { Authorization: `Basic ${basic}` },
});
console.log(response.status);
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Basic",
    Convert.ToBase64String(Encoding.ASCII.GetBytes("admin:password"))
);

var response = await client.DeleteAsync("https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-...");
Console.WriteLine(response.StatusCode);

키별 지표

키마다 다음 정보를 추적한다:

필드 설명
key_prefix 키를 식별할 때 쓰는 앞부분
key_suffix 키를 식별할 때 쓰는 뒷부분
key_preview prefix + suffix를 합친 표시용 미리보기
request_count 이 키로 호출한 총 횟수
last_used_at 마지막 사용 시각
is_active 키 활성 여부

지표는 요청마다 비동기로 갱신됩니다.

키 사용법

X-API-Key 헤더에 키를 넣으면 된다:

  • 요청 형식: multipart/form-data
  • 응답 형식: JSON (DetectResponse)
curl -X POST https://face-api.xylolabs.com/api/v1/detect \
  -H "X-API-Key: xyl_your_key_here" \
  -F "image=@photo.jpg"
import requests

response = requests.post(
    "https://face-api.xylolabs.com/api/v1/detect",
    headers={"X-API-Key": "xyl_your_key_here"},
    files={"image": open("photo.jpg", "rb")},
)
print(response.json())
const formData = new FormData();
formData.append("image", fileInput.files[0]);

const response = await fetch("https://face-api.xylolabs.com/api/v1/detect", {
  method: "POST",
  headers: { "X-API-Key": "xyl_your_key_here" },
  body: formData,
});
console.log(await response.json());
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "xyl_your_key_here");

using var content = new MultipartFormDataContent();
content.Add(new ByteArrayContent(File.ReadAllBytes("photo.jpg")), "image", "photo.jpg");

var response = await client.PostAsync("https://face-api.xylolabs.com/api/v1/detect", content);
Console.WriteLine(await response.Content.ReadAsStringAsync());

비활성 키는 403, 잘못된 키는 401을 반환합니다.