
# 멤버십 수정

`PATCH /v1/organizations/{org_id}/memberships/{membership_id}` · 권한 `organizations:write`

JSON 병합 패치(`Content-Type: application/merge-patch+json`)로 멤버십의 `role_ids`(수동으로 할당된 역할의 전체 집합) 또는 `expires_at`을 변경합니다. 두 필드를 같은 요청에서 함께 변경할 수 있으며, 단일 트랜잭션으로 적용되므로 둘 다 기록되거나 둘 다 기록되지 않습니다. 감사 레코드 `organization_membership.updated`가 하나 기록됩니다. `expires_at`은 미래 시점이어야 하며, 멤버십 생성 후 최대 365일 이내여야 합니다. `null`을 지정하면 종료일이 제거됩니다. `role_ids: null`은 아무 효과가 없습니다. 역할 규칙은 `PUT …/roles`와 동일합니다. 현재 값과 같은 필드는 기록되지 않습니다. 이 쓰기 작업은 `organization_membership.updated`로 감사 기록됩니다.

## 요청

| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
| `org_id` | 경로 | 예 | 조직 ID(`org_…`)입니다. |
| `membership_id` | 경로 | 예 | 멤버십 ID(`mem_…`)입니다. |
| `If-Match` | 헤더 | 아니요 | 현재 `ETag`입니다. 지정하면 일치해야 하며(`412`), 쓰기가 해당 버전에 고정됩니다. |
| `role_ids` | string[] | 아니요 | 수동으로 할당된 역할 ID(`rol_…`)의 전체 집합으로, 최대 50개입니다. |
| `expires_at` | string \| null | 아니요 | 멤버십의 종료 시점(RFC 3339)입니다. `null`이면 종료일이 제거됩니다. |

## 응답

수정된 멤버십과 해당 `ETag`를 담은 `200 OK`를 반환합니다.

```json
{
  "id": "mem_01j9xn5p9s0t4v6w8x0y2z4a6c",
  "organization_id": "org_01j9xk3m7q8r2s4t6v8w0y2z4a",
  "user_id": "usr_01j9xh2k6p7q1r3s5t7v9w1x3y",
  "status": "active",
  "source": "admin_api",
  "reason": null,
  "roles": [
    {
      "role_id": "rol_01j9xr7s1t2w6x8y0z2a4b6c8e",
      "source": "admin_api",
      "assigned_at": "2026-10-12T09:31:00.123456Z"
    }
  ],
  "expires_at": "2027-01-01T00:00:00Z",
  "created_at": "2026-10-12T09:31:00.123456Z",
  "activated_at": "2026-10-12T09:31:00.123456Z",
  "suspended_at": null,
  "removed_at": null,
  "updated_at": "2026-10-12T10:00:00Z",
  "version": 2
}
```

## 오류

| HTTP | code | 발생 조건 |
|---|---|---|
| 404 | `not_found` | 이 조직에 해당 멤버십이 없습니다. |
| 403 | `role_not_assignable` | 역할이 이 환경의 역할도 아니고 이 조직의 역할도 아닙니다. |
| 409 | `last_org_admin` | 변경 후 조직에 활성 관리자가 남지 않습니다. |
| 412 | `precondition_failed` | `If-Match`가 현재 버전과 일치하지 않습니다. |
| 415 | `unsupported_media_type` | 본문이 `application/merge-patch+json`이 아닙니다. |
| 422 | `validation_failed` | `expires_at`이 범위를 벗어났거나 멤버십이 제거된 상태(`invalid_state`)이거나, 값이 잘못되었습니다. |
| 401 | `unauthenticated` | 자격 증명이 없거나 잘못되었거나 취소되었습니다. |
| 403 | `insufficient_permissions` | 자격 증명에 필요한 권한이 없습니다. 권한이 필요한 역할(`is_privileged`)을 할당하려면 `roles:write`도 필요하며, 없으면 아무것도 기록되지 않고 `required_permissions`에 두 권한이 모두 표시됩니다. |
| 429 | `rate_limited` | 요청 한도를 초과했습니다. `Retry-After` 기간 이후에 다시 시도하십시오. |
| 503 | `service_unavailable` | 종속 서비스를 일시적으로 사용할 수 없습니다. `Retry-After` 기간 이후에 다시 시도하십시오. |

## 예제

```bash
curl -i -X PATCH "https://api.eu.keenauth.com/v1/organizations/org_01j9xk3m7q8r2s4t6v8w0y2z4a/memberships/mem_01j9xn5p9s0t4v6w8x0y2z4a6c" \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/merge-patch+json" \
  -d '{"expires_at":"2027-01-01T00:00:00Z"}'
```

## 개인정보

이 엔드포인트는 개인정보를 다룹니다: 조직 멤버십과 초대. [KeenAuth API의 개인정보](/docs/policies/personal-data)에서 저장되는 항목, 보관 기간, 삭제 방법을 설명합니다. 어떤 필드에도 특수 범주의 개인정보(건강, 생체 정보, 종교, 정치적 견해, 성적 지향)를 보내지 마십시오.
