
# API 업데이트

`PATCH /v1/apis/{api_id}` · 권한 `apis:write`

JSON 병합 패치(RFC 7396, `application/merge-patch+json`)로 API를 업데이트합니다: 보내는 필드는 변경되고, 생략한 필드는 유지됩니다. `If-Match`는 필수입니다. `key` 및 `identifier`는 생성 후 변경할 수 없으며, 시스템 API는 수정할 수 없습니다. 아무것도 변경하지 않는 패치는 아무것도 쓰지 않고 버전을 유지합니다.

## 요청

| 매개변수 | 위치 | 설명 |
|---|---|---|
| `api_id` | path | API ID. |
| `If-Match` | header | 마지막 읽기의 `ETag`(필수). |

본문: 아래의 쓰기 가능한 필드 중 하나(모두 선택적). 필드는 `null`로 제거할 수 없습니다.

| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
| `key` | string | 아니오 | 안정적인 구성 키, `^[a-z0-9_-]{1,64}$`. 불변입니다. |
| `identifier` | string | 아니오 | 토큰 대상: https URL(쿼리 또는 조각 없음) 또는 `urn:<nid>:<nss>`; 정규 형식으로 저장됩니다. 불변입니다. `urn:idp:` 네임스페이스는 예약됩니다. |
| `name` | string | 아니오 | 1-128자. |
| `access_token_lifetime_s` | integer | 아니오 | 60-86400. 기본값 3600(`mcp_profile`시 600). |
| `token_format` | string | 아니오 | `jwt`(기본값) 또는 `opaque`. |
| `rbac_mode` | string | 아니오 | 역할과 권한이 토큰에 반영되는 방식: `claims`(기본값)는 `permissions`와 `org_permissions`를 추가합니다. `off`는 둘 다 추가하지 않습니다(`org_roles`는 여전히 추가됨). `enforce`는 이들을 추가하고 부여된 스코프를 사용자의 유효 권한으로도 제한합니다. |
| `allow_offline_access` | boolean | 아니오 | 기본값 `true`. |
| `require_resource_indicator` | boolean | 아니오 | 기본값 `false`(`mcp_profile`시 `true`). |
| `dpop_required` | boolean | 아니오 | 기본값 `false`. |
| `mcp_profile` | boolean | 아니오 | https `identifier`와 `require_resource_indicator: true`가 필요합니다. |
| `first_party_access` | string | 아니오 | `all`(기본값) 또는 `granted_only`. |
| `introspection_applications` | array | 아니오 | 이 API의 토큰을 내부검사할 수 있는 애플리케이션(`app_…`), 최대 50개, 중복 없음. 동일한 환경의 `web`과 `m2m` 애플리케이션만 가능합니다. 목록은 전체적으로 교체됩니다. `[]`는 비우고 `null`는 거부됩니다. |

### 내부검사 애플리케이션

내부검사 끝점(`POST <issuer>/oauth2/introspect`, RFC 7662)은 접근 토큰에 대해 두 호출자에게만 `active: true`로 응답합니다: 토큰이 발급된 애플리케이션 또는 토큰의 `aud`에 명시된 API의 `introspection_applications` 목록에 있는 애플리케이션. 다른 모든 호출자는 `active: false`를 받으므로 API는 서로의 토큰을 읽을 수 없습니다. 동일한 환경의 기밀 애플리케이션(`web`, `m2m`)만 나열할 수 있습니다. 목록은 집합으로 저장되고 ID 순서로 반환됩니다. 일시 중단된 애플리케이션은 목록에 남지만 내부검사를 할 수 없습니다. 시스템 API의 목록은 작성할 수 없습니다.

## 응답

새로운 `ETag`가 있는 `200 OK`:

```json
{
  "id": "api_01h455vb4pex5vsknk084sn02q",
  "key": "orders",
  "identifier": "https://api.example.com/orders",
  "name": "Orders API",
  "is_system": false,
  "access_token_lifetime_s": 3600,
  "token_format": "jwt",
  "rbac_mode": "off",
  "allow_offline_access": true,
  "require_resource_indicator": false,
  "dpop_required": false,
  "mcp_profile": false,
  "first_party_access": "all",
  "introspection_applications": ["app_01h455vb4pex5vsknk084sn02r"],
  "created_at": "2026-10-05T12:00:00Z",
  "updated_at": "2026-10-05T12:00:00Z"
}
```

## 오류

| HTTP | code | 언제 |
|---|---|---|
| 400 | `unknown_field` | 패치에 읽기 전용 또는 알 수 없는 필드가 포함되어 있습니다. |
| 404 | `not_found` | 이 환경에 그러한 API가 없습니다. |
| 409 | `conflict` | API가 시스템 API입니다. |
| 412 | `precondition_failed` | `If-Match`가 현재 버전과 일치하지 않습니다. |
| 415 | `unsupported_media_type` | 콘텐츠 유형이 `application/merge-patch+json`이 아닙니다. |
| 422 | `validation_failed` | 값이 유효하지 않거나, 필드가 `null`로 설정되었거나, `key` / `identifier`가 변경되었습니다. |
| 422 | `validation_failed` | `introspection_applications` 항목이 이 환경에 존재하지 않거나(`unknown_application`), 기밀이 아니거나(`not_confidential`), 중복이거나(`duplicate`) `app_…` ID가 아닙니다(`invalid_value`). 포인터는 요청의 인덱스를 나타냅니다. |
| 428 | `precondition_required` | `If-Match`가 누락되었습니다. |

## 예제

```bash
curl -X PATCH https://api.eu.keenauth.com/v1/apis/api_01h455vb4pex5vsknk084sn02q \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/merge-patch+json" \
  -H 'If-Match: "1"' \
  -d '{"access_token_lifetime_s":900}'
```
