
# Informe de uso de obsolescencias

`GET /v1/reports/deprecation-usage` · permiso `reports:read`

Muestra qué clientes siguen llamando a operaciones programadas para su eliminación, para que puedas migrarlos antes de la fecha de eliminación (consulta la [política de versionado](/docs/policies/versioning)). Las llamadas se cuentan por día UTC y se agrupan por obsolescencia, cliente y versión del SDK. Cuando ninguna operación está obsoleta, el informe está vacío.

El entorno siempre procede de la clave de API; no hay ningún parámetro de entorno. Las filas no contienen dirección IP, `User-Agent` ni identificadores de usuario final.

## Solicitud

| Parámetro | En | Obligatorio | Descripción |
|---|---|---|---|
| `from` | query | no | Primer día incluido, fecha completa (`YYYY-MM-DD`, UTC). Predeterminado: `to` menos 30 días. Limitado por el historial de informes de tu plan (ver abajo). |
| `to` | query | no | Último día incluido, fecha completa (`YYYY-MM-DD`, UTC). Predeterminado: hoy. `from` no puede ser posterior a `to`. |
| `limit` | query | no | Tamaño de página, de 1 a 100. Predeterminado 50. |
| `cursor` | query | no | `next_cursor` de la página anterior. Solo es válido para el entorno, `from` y `to` para los que se emitió. |

Las filas se ordenan por `deprecation_id`, `client_id`, `sdk_package`, `sdk_version`. Ambos extremos del rango son inclusivos. Un cursor obtenido con un rango predeterminado (sin enviar `from`/`to`) deja de funcionar cuando cambia el día UTC; envía `from` y `to` explícitamente al paginar.

### Historial de informes por plan

| Plan | Historial |
|---|---|
| Free | 30 días |
| Startup, Business | 13 meses |
| Enterprise | 25 meses, con un límite de 400 días |

La tabla de informes conserva 400 días, así que ningún plan llega más atrás de 400 días. Un `from` más antiguo que el historial permitido falla con `422 report_range_exceeds_plan`; la fecha más antigua permitida aparece en el mensaje y en `errors[0].detail` (`earliest_allowed_date=YYYY-MM-DD`).

## Respuesta

`200 OK`. `data` contiene las filas de la página; `next_cursor` es `null` en la última página.

| Campo | Descripción |
|---|---|
| `deprecation_id` | Identificador en el registro de obsolescencias (`dep-YYYY-NN`). |
| `client_id` | `key_…` para una clave de API de gestión, `app_…` para un cliente OAuth de gestión, `dashboard` para el panel. |
| `sdk_package` | Paquete del SDK tomado de la cabecera `Idp-Client`; `other` para un paquete fuera del catálogo, `none` cuando falta la cabecera. |
| `sdk_version` | Versión SemVer, o una cadena vacía. |
| `call_count` | Llamadas en el rango. |
| `last_seen_at` | Última llamada en el rango (RFC 3339, UTC). |
| `sunset_at` | Fecha de eliminación del registro de obsolescencias (`YYYY-MM-DD`), `null` si se desconoce. Después de este día, la operación responde `410 endpoint_sunset`. |

```json
{
  "data": [
    {
      "deprecation_id": "dep-2027-01",
      "client_id": "key_01j9xk3m7q8r2s4t6v8w0y2z4d",
      "sdk_package": "keenauth-go",
      "sdk_version": "1.4.0",
      "call_count": 1280,
      "last_seen_at": "2027-03-02T09:30:00Z",
      "sunset_at": "2028-01-17"
    }
  ],
  "next_cursor": null
}
```

### CSV

Envía `Accept: text/csv` para recibir las mismas columnas en CSV (RFC 4180) con una fila de encabezado. Las celdas de texto que empiezan por `=`, `+`, `-`, `@`, un tabulador o un retorno de carro llevan un apóstrofo delante para que las hojas de cálculo no las ejecuten como fórmulas. El cursor de la página siguiente está en la cabecera `Link` (`rel="next"`); el rango se detalla en ese enlace.

```text
deprecation_id,client_id,sdk_package,sdk_version,call_count,last_seen_at,sunset_at
dep-2027-01,key_01j9xk3m7q8r2s4t6v8w0y2z4d,keenauth-go,1.4.0,1280,2027-03-02T09:30:00Z,2028-01-17
```

## Notas

Los recuentos se recogen por minuto y se escriben en el informe de forma asíncrona, por lo que las llamadas más recientes pueden tardar unos minutos en aparecer. Un fallo puede perder hasta 60 segundos de recuentos. El informe no es una entrada de facturación.

## Errores

Cada error es un cuerpo `application/problem+json` con un `code` estable.

| HTTP | `code` | Cuándo |
|---|---|---|
| 400 | `invalid_filter` | `from` o `to` no es una fecha `YYYY-MM-DD`, una fecha está repetida o `from` es posterior a `to`. |
| 400 | `invalid_cursor` | El cursor está mal formado o no se emitió para este entorno y rango. |
| 422 | `report_range_exceeds_plan` | `from` es más antiguo que el historial de informes de tu plan. |
| 422 | `validation_failed` | `limit` está fuera de rango o un parámetro está repetido. |
| 401 | `unauthenticated` | La clave de API falta, no es válida o está revocada. |
| 403 | `insufficient_permissions` | La clave no tiene `reports:read`. |
| 429 | `rate_limited` | Se superó el límite de peticiones; respeta `Retry-After`. |
| 503 | `service_unavailable` | El servicio de informes no está disponible temporalmente; vuelve a intentarlo más tarde. |

## Ejemplo

```bash
curl "https://api.eu.keenauth.com/v1/reports/deprecation-usage?from=2027-03-01&to=2027-03-31" \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY"
```

```bash
keenauth reports deprecation-usage --from 2027-03-01 --to 2027-03-31
keenauth reports deprecation-usage --format csv > deprecation-usage.csv
```
