> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dgrid.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 관리 API 키

> DGrid Model API 키를 프로그래밍 방식으로 생성, 교체, 조회, 폐기하며 자격 증명, 권한 범위, 사용량 제한, 팀 단위 접근 제어를 포함한 전체 키 수명 주기를 안전하게 관리하는 Management API 사용 방법을 자세히 안내합니다.

Management API Keys는 Model API 키의 전체 생명주기를 프로그래밍 방식으로 관리하는 방법을 제공합니다. 이는 수동 콘솔 작업에 의존하지 않고 키를 생성, 배포, 교체, 활성화, 비활성화 또는 폐기해야 하는 엔터프라이즈 팀, SaaS 플랫폼, 자동화 시스템을 위해 설계되었습니다.

Management API Keys는 관리용 자격 증명이며 키 관리 작업으로만 제한됩니다. 모델 추론 또는 완성 엔드포인트를 호출하는 데는 사용할 수 없습니다.

## 1. 개요

Management API Keys는 다음과 같은 시나리오를 위해 설계되었습니다.

* 서로 다른 고객, 프로젝트, 환경에 대해 별도의 Model API 키를 발급
* 하위 Model API 키에 사용량 한도 및 자동 초기화 주기 적용
* 프로그래밍 방식으로 키를 교체, 비활성화 또는 폐기
* SaaS, 멀티 테넌트, 컴플라이언스 중심 워크플로에서 최소 권한 키 관리 시행

핵심 기능:

* 키 관리 작업에 대한 엄격한 권한 분리
* Model API 키에 대한 전체 생명주기 자동화
* 설정 가능한 사용량 한도 및 초기화 주기
* 서버 측 서비스, 내부 도구, 자동화된 프로비저닝 워크플로를 위해 설계됨

## 2. API 영역 및 인증 경계

키 관리는 각기 고유한 인증 모델을 가진 두 개의 API 영역으로 나뉩니다.

| API 영역                      | 목적                                        | 인증                                       |
| --------------------------- | ----------------------------------------- | ---------------------------------------- |
| `/v1/management-keys`       | Management API Keys 관리                    | `JWT`                                    |
| `/api/v1/model-router/keys` | Management API Key를 사용한 Model API Keys 관리 | `Authorization: Bearer <management_key>` |

중요 사항:

* `Management API Key`는 `/api/v1/model-router/keys`에서만 사용할 수 있습니다
* `Management API Key`는 `/v1/management-keys`에서 사용할 수 없습니다
* `/v1/management-keys`는 `JWT` 인증만 지원합니다
* 전체 시크릿은 키가 생성될 때 한 번만 반환되며, 이후에는 다시 조회할 수 없습니다

## 3. 기본 규칙

* 각 계정은 최대 `10`개의 Management API Keys를 생성할 수 있습니다
* Management API Keys는 생성 즉시 활성화됩니다
* 전체 Management API Key 시크릿은 한 번만 반환됩니다
* 이후의 목록 및 상세 응답에는 마스킹된 키 값만 반환됩니다
* Model API Keys는 현재 영구 삭제가 아닌 소프트 삭제 방식으로 처리됩니다

## 4. Base URL

퍼블릭 API의 base URL은 다음과 같습니다.

```text theme={null}
https://api.dgrid.ai
```

이 문서에서 사용되는 경로 접두사:

```text theme={null}
/v1/management-keys
/api/v1/model-router/keys
```

## 5. Management API Key 생성

Management API를 사용하기 전에, 먼저 DGrid 콘솔에서 Management API Key를 생성하세요.

<Steps>
  <Step title="Management API Keys 페이지 열기" />

  <Step title="Create 클릭" />

  <Step title="키 이름 입력" />

  <Step title="필수 보안 인증 완료" />

  <Step title="생성 즉시 키를 복사하여 안전하게 보관" />
</Steps>

<Note>
  이 흐름을 자체 UI에서 노출하는 경우, 시크릿이 한 번만 표시되며 즉시 저장해야 함을 사용자에게 명확히 안내하세요.
</Note>

## 6. 인증

이 문서는 두 가지 인증 모드를 다룹니다.

* `/v1/management-keys` 하위 엔드포인트는 `JWT`가 필요합니다
* `/api/v1/model-router/keys` 하위 엔드포인트는 Management API Key가 필요합니다

`/api/v1/model-router/keys` 엔드포인트를 호출할 때는 다음 헤더를 사용하세요.

```http theme={null}
Authorization: Bearer <management_key>
```

## 7. Management API Key 생명주기 엔드포인트

이 엔드포인트들은 Management API Keys를 생성, 조회, 업데이트, 활성화, 비활성화, 삭제하는 데 사용됩니다. 모두 `JWT` 인증이 필요합니다.

| 작업         | 메서드      | 경로                                     | 참고                  |
| ---------- | -------- | -------------------------------------- | ------------------- |
| 관리 키 생성    | `POST`   | `/v1/management-keys`                  | 전체 키는 한 번만 반환됩니다    |
| 관리 키 목록 조회 | `GET`    | `/v1/management-keys`                  | 페이지네이션 지원           |
| 관리 키 업데이트  | `PUT`    | `/v1/management-keys/{id}`             | 현재는 `name`만 업데이트 가능 |
| 관리 키 삭제    | `DELETE` | `/v1/management-keys/{id}`             | 소프트 삭제              |
| 관리 키 활성화   | `POST`   | `/v1/management-keys/{id}/enablement`  | 즉시 적용됩니다            |
| 관리 키 비활성화  | `POST`   | `/v1/management-keys/{id}/disablement` | 즉시 적용됩니다            |

생성 응답 예시:

```json theme={null}
{
  "code": 200,
  "message": "ok",
  "data": {
    "id": "3ecf9d8d-9b8f-4df6-9d30-7a693e1f0d1c",
    "name": "prod-admin",
    "key": "mk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "keyPreview": "mk-x************************xxxx5",
    "createdAt": "2026-04-23T10:00:00Z",
    "status": "Enabled",
    "enabled": true
  }
}
```

참고 사항:

* `key`는 생성 시점에 한 번만 반환됩니다
* `keyPreview`는 마스킹된 표시 값입니다

## 8. Management API Key로 Model API Keys 관리

이 섹션의 모든 엔드포인트는 다음을 사용합니다.

```http theme={null}
Authorization: Bearer <management_key>
```

### 8.1 요청 필드

현재 구현에서는 Model API Key를 생성하거나 업데이트할 때 다음 필드를 지원합니다.

| 필드          | 유형                           | 필수      | 설명              |
| ----------- | ---------------------------- | ------- | --------------- |
| `name`      | `string`                     | 생성 시 필수 | 키 이름            |
| `limit`     | `number`                     | 아니요     | 사용량 한도          |
| `cycle`     | `daily \| weekly \| monthly` | 아니요     | 한도 초기화 주기       |
| `expiredAt` | `string`                     | 아니요     | UTC 기준 만료 타임스탬프 |
| `groupId`   | `string`                     | 아니요     | 그룹 ID           |

참고 사항:

* OpenRouter의 `limit_reset`에 익숙하다면, 현재 DGrid 구현에서 가장 가까운 대응 항목은 `cycle`입니다
* `expiredAt`은 `2026-12-31T23:59:59Z`와 같은 ISO 8601 UTC 타임스탬프 형식을 사용해야 합니다

### 8.2 키 목록 조회

* 메서드: `GET`
* 경로: `/api/v1/model-router/keys`
* 쿼리 매개변수:
  * `page`: 페이지 번호, 기본값 `1`
  * `size`: 페이지 크기, 기본값 `20`, 최대 `100`

구현 참고 사항:

* 페이지네이션은 `limit`과 `offset`이 아닌 `page`와 `size`를 사용합니다
* 현재 이름 부분 검색은 지원되지 않습니다
* 현재 `disabled` 필터링은 지원되지 않습니다

요청 예시:

```bash theme={null}
curl "https://api.dgrid.ai/api/v1/model-router/keys?page=1&size=20" \
  -H "Authorization: Bearer <management_key>"
```

응답 예시:

```json theme={null}
{
  "code": 200,
  "message": "ok",
  "data": {
    "total": 2,
    "page": 1,
    "items": [
      {
        "id": "e8f9c547-4f0c-4d8b-8e1b-8ef9b0aa1111",
        "name": "prod-key",
        "key": "sk-a************************f9x2d",
        "limit": 1000,
        "usageInCycle": 12.34,
        "usageInTotal": 98.76,
        "enabled": true,
        "cycle": "monthly",
        "expiredAt": "2026-12-31T23:59:59Z",
        "groupId": null,
        "groupName": ""
      }
    ]
  }
}
```

필드 설명:

* `key`: 마스킹된 API 키 값
* `usageInCycle`: 현재 주기 동안의 사용량
* `usageInTotal`: 누적 사용량
* `enabled`: 현재 활성화 상태
* `groupName`: 그룹 이름

### 8.3 Model API Key 생성

* 메서드: `POST`
* 경로: `/api/v1/model-router/keys`

요청 예시:

```bash theme={null}
curl -X POST "https://api.dgrid.ai/api/v1/model-router/keys" \
  -H "Authorization: Bearer <management_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "prod-key",
    "limit": 1000,
    "cycle": "monthly",
    "expiredAt": "2026-12-31T23:59:59Z"
  }'
```

요청 본문 예시:

```json theme={null}
{
  "name": "prod-key",
  "limit": 1000,
  "cycle": "monthly",
  "expiredAt": "2026-12-31T23:59:59Z"
}
```

응답 예시:

```json theme={null}
{
  "code": 200,
  "message": "ok",
  "data": {
    "id": "e8f9c547-4f0c-4d8b-8e1b-8ef9b0aa1111",
    "key": "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  }
}
```

참고 사항:

* `key`는 전체 API 키 값이며 한 번만 반환됩니다
* 즉시 안전한 시크릿 관리 시스템에 저장하세요

### 8.4 키 상세 정보 조회

* 메서드: `GET`
* 경로: `/api/v1/model-router/keys/{id}`

구현 참고 사항:

* 현재 구현은 `key_hash`가 아닌 `id (UUID)`를 사용합니다

요청 예시:

```bash theme={null}
curl "https://api.dgrid.ai/api/v1/model-router/keys/e8f9c547-4f0c-4d8b-8e1b-8ef9b0aa1111" \
  -H "Authorization: Bearer <management_key>"
```

응답 예시:

```json theme={null}
{
  "code": 200,
  "message": "ok",
  "data": {
    "id": "e8f9c547-4f0c-4d8b-8e1b-8ef9b0aa1111",
    "name": "prod-key",
    "key": "sk-a************************f9x2d",
    "limit": 1000,
    "usageInCycle": 12.34,
    "usageInTotal": 98.76,
    "enabled": true,
    "cycle": "monthly",
    "expiredAt": "2026-12-31T23:59:59Z",
    "groupId": null,
    "groupName": ""
  }
}
```

### 8.5 Model API Key 업데이트

* 메서드: `PUT`
* 경로: `/api/v1/model-router/keys/{id}`

현재 지원되는 업데이트 필드:

* `name`
* `limit`
* `cycle`
* `groupId`

요청 예시:

```bash theme={null}
curl -X PUT "https://api.dgrid.ai/api/v1/model-router/keys/e8f9c547-4f0c-4d8b-8e1b-8ef9b0aa1111" \
  -H "Authorization: Bearer <management_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "prod-key-v2",
    "limit": 2000,
    "cycle": "monthly"
  }'
```

요청 본문 예시:

```json theme={null}
{
  "name": "prod-key-v2",
  "limit": 2000,
  "cycle": "monthly"
}
```

구현 참고 사항:

* 현재 업데이트 메서드는 `PATCH`가 아닌 `PUT`입니다

### 8.6 Model API Key 비활성화

* 메서드: `POST`
* 경로: `/api/v1/model-router/keys/{id}/disablement`

요청 예시:

```bash theme={null}
curl -X POST "https://api.dgrid.ai/api/v1/model-router/keys/e8f9c547-4f0c-4d8b-8e1b-8ef9b0aa1111/disablement" \
  -H "Authorization: Bearer <management_key>"
```

비활성화 후에는 해당 API 키를 모델 호출에 사용할 수 없습니다.

### 8.7 Model API Key 활성화

* 메서드: `POST`
* 경로: `/api/v1/model-router/keys/{id}/enablement`

요청 예시:

```bash theme={null}
curl -X POST "https://api.dgrid.ai/api/v1/model-router/keys/e8f9c547-4f0c-4d8b-8e1b-8ef9b0aa1111/enablement" \
  -H "Authorization: Bearer <management_key>"
```

### 8.8 Model API Key 삭제

* 메서드: `DELETE`
* 경로: `/api/v1/model-router/keys/{id}`

요청 예시:

```bash theme={null}
curl -X DELETE "https://api.dgrid.ai/api/v1/model-router/keys/e8f9c547-4f0c-4d8b-8e1b-8ef9b0aa1111" \
  -H "Authorization: Bearer <management_key>"
```

구현 참고 사항:

* 삭제는 현재 물리적 삭제가 아닌 소프트 삭제입니다

## 9. 사용 예시

<CodeGroup>
  ```python Python theme={null}
  import requests

  BASE = "https://api.dgrid.ai/api/v1/model-router"
  MANAGEMENT_KEY = "mk-your-management-key"

  headers = {
      "Authorization": f"Bearer {MANAGEMENT_KEY}",
      "Content-Type": "application/json"
  }

  # 1) List keys
  resp = requests.get(
      f"{BASE}/keys",
      headers=headers,
      params={"page": 1, "size": 20}
  )
  print("LIST:", resp.json())

  # 2) Create key
  resp = requests.post(
      f"{BASE}/keys",
      headers=headers,
      json={
          "name": "prod-key",
          "limit": 1000,
          "cycle": "monthly",
          "expiredAt": "2026-12-31T23:59:59Z"
      }
  )
  create_data = resp.json()
  print("CREATE:", create_data)

  key_id = create_data["data"]["id"]

  # 3) Get key
  resp = requests.get(f"{BASE}/keys/{key_id}", headers=headers)
  print("GET:", resp.json())

  # 4) Update key
  resp = requests.put(
      f"{BASE}/keys/{key_id}",
      headers=headers,
      json={
          "name": "prod-key-v2",
          "limit": 2000,
          "cycle": "monthly"
      }
  )
  print("UPDATE:", resp.json())

  # 5) Disable key
  resp = requests.post(f"{BASE}/keys/{key_id}/disablement", headers=headers)
  print("DISABLE:", resp.json())

  # 6) Enable key
  resp = requests.post(f"{BASE}/keys/{key_id}/enablement", headers=headers)
  print("ENABLE:", resp.json())

  # 7) Delete key
  resp = requests.delete(f"{BASE}/keys/{key_id}", headers=headers)
  print("DELETE:", resp.json())
  ```

  ```typescript TypeScript theme={null}
  const BASE = "https://api.dgrid.ai/api/v1/model-router";
  const MANAGEMENT_KEY = "mk-your-management-key";

  const headers: HeadersInit = {
    Authorization: `Bearer ${MANAGEMENT_KEY}`,
    "Content-Type": "application/json",
  };

  async function main() {
    const listResp = await fetch(`${BASE}/keys?page=1&size=20`, {
      method: "GET",
      headers,
    });
    console.log("LIST:", await listResp.json());

    const createResp = await fetch(`${BASE}/keys`, {
      method: "POST",
      headers,
      body: JSON.stringify({
        name: "prod-key",
        limit: 1000,
        cycle: "monthly",
        expiredAt: "2026-12-31T23:59:59Z",
      }),
    });
    const createData = await createResp.json();
    console.log("CREATE:", createData);

    const keyId = createData.data.id;

    const getResp = await fetch(`${BASE}/keys/${keyId}`, {
      method: "GET",
      headers,
    });
    console.log("GET:", await getResp.json());

    const updateResp = await fetch(`${BASE}/keys/${keyId}`, {
      method: "PUT",
      headers,
      body: JSON.stringify({
        name: "prod-key-v2",
        limit: 2000,
        cycle: "monthly",
      }),
    });
    console.log("UPDATE:", await updateResp.json());

    const disableResp = await fetch(`${BASE}/keys/${keyId}/disablement`, {
      method: "POST",
      headers,
    });
    console.log("DISABLE:", await disableResp.json());

    const enableResp = await fetch(`${BASE}/keys/${keyId}/enablement`, {
      method: "POST",
      headers,
    });
    console.log("ENABLE:", await enableResp.json());

    const deleteResp = await fetch(`${BASE}/keys/${keyId}`, {
      method: "DELETE",
      headers,
    });
    console.log("DELETE:", await deleteResp.json());
  }

  main().catch(console.error);
  ```

  ```javascript JavaScript theme={null}
  const BASE = "https://api.dgrid.ai/api/v1/model-router";
  const MANAGEMENT_KEY = "mk-your-management-key";

  const headers = {
    Authorization: `Bearer ${MANAGEMENT_KEY}`,
    "Content-Type": "application/json",
  };

  async function main() {
    let resp = await fetch(`${BASE}/keys?page=1&size=20`, {
      method: "GET",
      headers,
    });
    console.log("LIST:", await resp.json());

    resp = await fetch(`${BASE}/keys`, {
      method: "POST",
      headers,
      body: JSON.stringify({
        name: "prod-key",
        limit: 1000,
        cycle: "monthly",
        expiredAt: "2026-12-31T23:59:59Z",
      }),
    });
    const createData = await resp.json();
    console.log("CREATE:", createData);

    const keyId = createData.data.id;

    resp = await fetch(`${BASE}/keys/${keyId}`, {
      method: "GET",
      headers,
    });
    console.log("GET:", await resp.json());

    resp = await fetch(`${BASE}/keys/${keyId}`, {
      method: "PUT",
      headers,
      body: JSON.stringify({
        name: "prod-key-v2",
        limit: 2000,
        cycle: "monthly",
      }),
    });
    console.log("UPDATE:", await resp.json());

    resp = await fetch(`${BASE}/keys/${keyId}/disablement`, {
      method: "POST",
      headers,
    });
    console.log("DISABLE:", await resp.json());

    resp = await fetch(`${BASE}/keys/${keyId}/enablement`, {
      method: "POST",
      headers,
    });
    console.log("ENABLE:", await resp.json());

    resp = await fetch(`${BASE}/keys/${keyId}`, {
      method: "DELETE",
      headers,
    });
    console.log("DELETE:", await resp.json());
  }

  main().catch(console.error);
  ```
</CodeGroup>

## 10. HTTP 상태 및 오류 코드

| HTTP 상태 | 오류 코드   | 설명                                          |
| ------- | ------- | ------------------------------------------- |
| `400`   | `40001` | 잘못된 요청 매개변수                                 |
| `401`   | `40101` | 요청 헤더에 Management API Key가 없음               |
| `401`   | `40102` | Management API Key가 유효하지 않거나, 만료되었거나, 비활성화됨 |
| `403`   | `40301` | 이 엔드포인트에 대한 권한이 부족하거나 키 유형이 올바르지 않음         |
| `404`   | `40401` | 대상 키를 찾을 수 없거나 현재 계정에 속하지 않음                |
| `429`   | `42901` | 요청 한도 초과                                    |
| `500`   | `50001` | 내부 서버 오류                                    |

## 11. 표준 응답 형식

성공적인 응답은 다음과 같은 형식을 사용합니다.

```json theme={null}
{
  "code": 200,
  "message": "ok",
  "data": {}
}
```

참고 사항:

* 성공한 요청은 HTTP `200`을 반환합니다
* 생성 응답에는 `data`에 전체 시크릿 키가 포함될 수 있습니다
* 목록 및 상세 엔드포인트는 일반적으로 마스킹된 키 값을 반환합니다
* Management API Keys와 Model API Keys 모두의 전체 시크릿은 한 번만 반환됩니다
