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):
응답 (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) |
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)
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)
키 비활성화¶
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)
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을 반환합니다.