
# 멤버십 종료일 설정

`PATCH /fapi/v1/organizations/{org_id}/memberships/{membership_id}` · 환경의 로그인 호스트에서 호출

조직 프로필 화면에서 멤버십이 끝나는 날짜(`expires_at`)를 설정하거나 해제합니다. 날짜가 지나면 멤버십이 삭제되고, 이 조직 컨텍스트에서 회원의 세션과 토큰이 취소됩니다. 만료 7일 전에는 `membership_expiry_warned` 이벤트가 발행됩니다. 역할은 [회원 역할 설정](/docs/api/fapi/organizations/memberships/set-roles)에서 변경하며, 이 엔드포인트는 종료일만 변경합니다.

인증된 사용자의 `__Host-idp_session` 쿠키(정확한 `Origin` 포함) 또는 자사 앱의 Front API 액세스 토큰(DPoP 증명을 포함한 `Authorization: DPoP <token>`, [Front API 액세스 토큰](/docs/api/fapi/front-api-token) 참조)이 필요합니다. 사용자는 조직의 활성 회원이어야 하며 `idp_members:update_roles`와 `idp_members:remove` 권한을 모두 가져야 합니다. 회원이 아닌 사용자는 `404 not_found`를 받고, 두 권한 중 하나라도 없는 회원은 `403 insufficient_permissions`를 받습니다. 여기서 본인의 멤버십은 변경할 수 없으며(`403 insufficient_permissions`), 특권 역할을 가진 회원은 관리자 역할을 가진 사용자만 변경할 수 있습니다(`403 role_not_assignable`).

`@keenauth/js` 사용 시: `await keenauth.organizations.setMemberExpiry(orgId, membershipId, "2027-03-31T00:00:00Z", { expectedVersion: 3 })`. 날짜를 해제하려면 `null`을 전달하십시오.

## 요청

헤더:

| 헤더 | 필수 여부 | 설명 |
| --- | --- | --- |
| `Idp-Client` | 예 | SDK 식별자입니다. |
| `Origin` | 쿠키 사용 시 예 | `https://<sign-in host>`. |
| `If-Match` | 아니요 | 회원 목록에서 읽은 버전 하나, `"<version>"`입니다. 일치하지 않는 버전이거나 버전이 둘 이상이면 `412 precondition_failed`를 반환합니다. |

`org_id`: 조직 식별자(`org_…`)입니다. `membership_id`: 멤버십 식별자(`mem_…`)입니다.

본문: `{"expires_at": "<RFC 3339 시각>"}` 또는 날짜를 해제하려면 `{"expires_at": null}`. `expires_at`은 필수입니다. 시각은 미래여야 하며, 멤버십 생성 후 최대 365일 이내여야 합니다.

## 응답

멤버십(위 [회원 목록](/docs/api/fapi/organizations/memberships/list)의 항목과 같은 필드)과 함께 `200 OK`, `ETag: "<version>"`을 반환합니다.

## 오류

| 상태 | 코드 | 발생 조건 |
| --- | --- | --- |
| 400 | `unknown_field` | 본문에 `expires_at` 외의 필드가 있습니다. |
| 400 | `malformed_request` | `If-Match` 형식이 잘못되었습니다. |
| 401 | `unauthenticated` | 유효한 세션 쿠키나 토큰이 없습니다. |
| 403 | `insufficient_permissions` | 사용자에게 `idp_members:update_roles` 또는 `idp_members:remove` 권한이 없거나, 본인의 멤버십입니다. |
| 403 | `role_not_assignable` | 회원이 특권 역할을 가지고 있고 사용자에게 관리자 역할이 없습니다. |
| 403 | `origin_not_allowed` | `Origin`이 일치하지 않습니다. |
| 404 | `not_found` | 이 사용자에게 해당 조직이나 멤버십이 없습니다. 알 수 없거나 형식이 잘못되었거나 다른 조직의 식별자도 같은 응답을 받습니다. |
| 409 | `organization_disabled` | 조직이 비활성화되었거나 삭제 대기 중입니다(`organization_pending_deletion`). |
| 412 | `precondition_failed` | `If-Match`가 멤버십의 현재 버전을 가리키지 않습니다. |
| 422 | `validation_failed` | `expires_at`이 없거나, RFC 3339 시각이 아니거나, 과거이거나, 멤버십 생성 후 365일을 초과합니다. |
| 503 | `service_unavailable` | 일시적으로 사용할 수 없습니다. `Retry-After` 이후에 다시 시도하십시오. |

## 예시

```bash
curl -X PATCH "https://example.keenauth.app/fapi/v1/organizations/org_01jbz6x8kq3m2n4p5r6s7t8v9a/memberships/mem_01jbz6x8kq3m2n4p5r6s7t8v9b" \
  -H "Idp-Client: my-app/1.0.0 (node/22.0.0)" \
  -H "Origin: https://example.keenauth.app" \
  -H 'If-Match: "3"' \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"expires_at": "2027-03-31T00:00:00Z"}'
```

```json
{
  "id": "mem_01jbz6x8kq3m2n4p5r6s7t8v9b",
  "user_id": "usr_01jbz6x8kq3m2n4p5r6s7t8v9c",
  "status": "active",
  "roles": [{ "role_id": "rol_01jbz6x8kq3m2n4p5r6s7t8v9w", "source": "admin_api" }],
  "created_at": "2026-11-03T09:00:00Z",
  "activated_at": "2026-11-03T09:00:00Z",
  "updated_at": "2026-11-03T11:00:00Z",
  "version": 4
}
```

## 개인정보

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