
# Establecer la fecha de fin de una membresía

`PATCH /fapi/v1/organizations/{org_id}/memberships/{membership_id}` · llamado desde el host de inicio de sesión del entorno

Establece o elimina la fecha en la que termina una membresía (`expires_at`), para una pantalla de perfil de organización. Cuando llega la fecha, la membresía se elimina y se revocan las sesiones y los tokens del miembro en el contexto de esta organización. Se publica un evento `membership_expiry_warned` siete días antes. Los roles se cambian mediante [Establecer los roles de un miembro](/docs/api/fapi/organizations/memberships/set-roles); este endpoint solo cambia la fecha de fin.

Requiere la cookie `__Host-idp_session` del usuario autenticado con un `Origin` exacto, o un token de acceso de la API frontal de una aplicación propia (`Authorization: DPoP <token>` con una prueba DPoP; véase [Token de acceso de la API frontal](/docs/api/fapi/front-api-token)). El usuario debe ser miembro activo de la organización con ambos permisos, `idp_members:update_roles` e `idp_members:remove`. Un usuario que no es miembro recibe `404 not_found`; un miembro sin ambos permisos recibe `403 insufficient_permissions`. No puede cambiar aquí su propia membresía (`403 insufficient_permissions`), y solo un miembro que tenga el rol de administrador puede cambiar a un miembro que tenga un rol privilegiado (`403 role_not_assignable`).

Con `@keenauth/js`: `await keenauth.organizations.setMemberExpiry(orgId, membershipId, "2027-03-31T00:00:00Z", { expectedVersion: 3 })`; pase `null` para eliminar la fecha.

## Solicitud

Encabezados:

| Encabezado | Obligatorio | Descripción |
| --- | --- | --- |
| `Idp-Client` | sí | Identificador del SDK. |
| `Origin` | sí, con la cookie | `https://<host de inicio de sesión>`. |
| `If-Match` | no | Una versión, `"<versión>"`, de la lista de miembros. Una versión que no coincide, o más de una versión, devuelve `412 precondition_failed`. |

`org_id`: el identificador de la organización (`org_…`). `membership_id`: el identificador de la membresía (`mem_…`).

Cuerpo: `{"expires_at": "<hora RFC 3339>"}` o `{"expires_at": null}` para eliminar la fecha. `expires_at` es obligatorio. La hora debe estar en el futuro y ser como máximo 365 días posterior a la creación de la membresía.

## Respuesta

`200 OK` con la membresía (los mismos campos que un elemento de [la lista de miembros](/docs/api/fapi/organizations/memberships/list)) y `ETag: "<versión>"`.

## Errores

| Estado | Código | Cuándo |
| --- | --- | --- |
| 400 | `unknown_field` | El cuerpo contiene un campo distinto de `expires_at`. |
| 400 | `malformed_request` | `If-Match` está mal formado. |
| 401 | `unauthenticated` | No hay una cookie de sesión o un token válidos. |
| 403 | `insufficient_permissions` | El usuario no tiene `idp_members:update_roles` o `idp_members:remove`, o la membresía es la propia del usuario. |
| 403 | `role_not_assignable` | El miembro tiene un rol privilegiado y el usuario no tiene el rol de administrador. |
| 403 | `origin_not_allowed` | El `Origin` no coincide. |
| 404 | `not_found` | No existe la organización o la membresía para este usuario; un identificador desconocido, mal formado o ajeno recibe la misma respuesta. |
| 409 | `organization_disabled` | La organización está deshabilitada o pendiente de eliminación (`organization_pending_deletion`). |
| 412 | `precondition_failed` | `If-Match` no indica la versión actual de la membresía. |
| 422 | `validation_failed` | `expires_at` falta, no es una hora RFC 3339, está en el pasado o es más de 365 días posterior a la creación de la membresía. |
| 503 | `service_unavailable` | No disponible temporalmente; reintente después de `Retry-After`. |

## Ejemplo

```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
}
```

## Datos personales

Este endpoint contiene datos personales: membresías de organizaciones e invitaciones. [Datos personales en las API de KeenAuth](/docs/policies/personal-data) describe qué se almacena, cuánto tiempo se conserva y cómo se elimina. No envíe categorías especiales de datos personales (salud, datos biométricos, religión, opiniones políticas, orientación sexual) en ningún campo.
