# Tokens de API caducados: reglas de recuperación para trabajos largos de agentes

Los trabajos largos de los agentes fallan de una forma especialmente absurda cuando nadie se encarga de la caducidad del token. El agente recibe un 401, repite la misma llamada, consume el presupuesto del límite de velocidad y a veces convierte una escritura incierta en varias. No es un simple fallo de autenticación. Es un fallo de diseño de recuperación.

Planifica la caducidad como un cambio de estado esperado. Deja la renovación en manos de un solo componente, clasifica los errores antes de actuar, vincula los reintentos al significado de la operación y deja un registro que permita al operador saber si el sistema remoto ya aceptó el trabajo. Un agente nunca debería tener que adivinar si puede crear credenciales o si es seguro repetir una escritura.

## La caducidad es un cambio de estado, no una interrupción excepcional

Un token de API caducado indica que terminó la autorización de una credencial de corta duración; no indica que la tarea haya fallado. Un trabajo largo puede haber completado diez operaciones remotas antes de que la siguiente solicitud encuentre la caducidad. El código de recuperación debe conservar esa diferencia.

Los equipos suelen agrupar cuatro eventos distintos en una sola rama llamada `auth_failed`. Ese atajo genera un comportamiento incorrecto porque cada evento necesita una respuesta diferente:

- La caducidad significa que terminó la vida útil del token y que el propietario autorizado de la renovación puede solicitar otro token de acceso.
- La revocación significa que un usuario, administrador o proveedor retiró la concesión. La renovación puede fallar intencionadamente.
- Una autenticación incorrecta de la solicitud puede deberse a un encabezado mal formado, un tipo de credencial incorrecto, una discrepancia del emisor o una audiencia equivocada.
- La falta de permisos significa que la identidad sigue siendo válida, pero no puede realizar esta operación.

OAuth establece estas diferencias por un motivo. RFC 6750 especifica el error `invalid_token` de los tokens bearer e indica que un servidor de recursos usa una respuesta 401 con un desafío `WWW-Authenticate` cuando el token ha caducado, fue revocado, está mal formado o no es válido por otro motivo. Es una guía de protocolo útil, pero no concede permiso a tu cliente para renovar a ciegas. El servidor de recursos solo informa de que rechazó esta solicitud.

Un trabajo también tiene dos líneas temporales. La **línea temporal del trabajo** registra lo que descubrió, calculó, creó y confirmó. La **línea temporal de autorización** registra la generación de credenciales que le permitió actuar. Cuando caduca un token, conserva la línea temporal del trabajo y cambia la línea temporal de autorización a `renewing` o `blocked`. No reinicies todo el trabajo y llames a eso recuperación.

Esta diferencia importa especialmente en los agentes porque encadenan llamadas dependientes. Supón que un agente crea una solicitud de cambio, sube un artefacto y pierde el acceso antes de poder adjuntar el artefacto. Reiniciar desde la primera instrucción puede crear una segunda solicitud de cambio. Un punto de control persistente después de cada efecto remoto confirmado permite que el agente continúe desde el archivo adjunto pendiente en lugar de repetir todo el plan.

Usa estados explícitos en vez de un booleano como `authenticated`:

```text
ready -> executing -> authorization_expired -> renewal_in_progress
renewal_in_progress -> executing
renewal_in_progress -> authorization_blocked
executing -> outcome_unknown
outcome_unknown -> reconciled -> executing
```

`outcome_unknown` merece su propio estado. Una conexión puede morir después de que el servicio confirme una escritura, pero antes de que el llamador reciba la respuesta. La renovación del token no resuelve esa incertidumbre. El trabajo debe consultar el identificador de la operación, el token de idempotencia o una búsqueda específica del proveedor antes de intentar escribir de nuevo.

## Un solo componente debe encargarse de la renovación

El cliente que guarda la credencial de renovación debería encargarse de renovar el token de acceso, y el agente debería solicitar una acción en lugar de recibir credenciales renovables. Esta regla parece restrictiva hasta que dos agentes encuentran la caducidad al mismo tiempo.

Si cada trabajador lleva una copia del token de renovación, todos pueden renovar de forma independiente. Se producen carreras, un historial lleno de eventos de credenciales sin relación y, si existe rotación de tokens, una renovación puede invalidar un token que otro trabajador todavía conserva. Además, habrás convertido cada proceso que puede leer una tarea en un portador de identidad de larga duración.

Coloca un intermediario de credenciales o una puerta de enlace de acciones entre los agentes y el proveedor. El intermediario guarda la credencial de renovación o de servicio, obtiene tokens de acceso de corta duración, adjunta uno solo al ejecutar una solicitud y devuelve la respuesta. El agente no recibe ni el token de acceso ni el token de renovación.

Esta separación asigna una función clara a cada actor:

- El agente decide qué operación permitida quiere realizar y proporciona sus datos de entrada.
- La puerta de enlace comprueba si la sesión puede hacer la solicitud, selecciona la referencia de credencial y ejecuta la llamada.
- El propietario de la renovación renueva una vez cuando el proveedor informa de un fallo de caducidad clasificado.
- Un operador gestiona una concesión revocada, un nuevo requisito de consentimiento o una credencial que necesita intervención humana.

No conviertas al agente en propietario de la renovación solo porque pueda llamar a un endpoint OAuth. Capacidad y autoridad son cosas distintas. Un agente que puede solicitar un evento de calendario o desplegar un artefacto no necesita automáticamente permiso para ampliar la identidad de una persona o un servicio.

RFC 6749 describe los tokens de renovación como credenciales emitidas al cliente y usadas para obtener nuevos tokens de acceso. Interpreta «cliente» literalmente en tu arquitectura. Si tu agente no es el cliente registrado, no debería heredar su token de renovación solo porque produce la solicitud de API.

Hay casos legítimos en los que el propio trabajo es propietario de la renovación. Una carga de máquina con un alcance reducido, su propio cliente registrado, su propio límite de almacenamiento y sin delegación humana puede hacerlo. Incluso entonces, un coordinador de renovación debería atender todas las operaciones simultáneas de esa identidad. Usa un mutex o un mecanismo single-flight identificado por la referencia de credencial. La primera llamada que falle renueva; las demás esperan el resultado en lugar de saturar el endpoint de tokens.

Un registro sencillo de propiedad evita diseños imprecisos:

```json
{
  "credential_ref": "billing-write-prod",
  "renewal_owner": "action-gateway",
  "access_token_lifetime": "provider-defined",
  "refresh_allowed": true,
  "reauthorization_owner": "on-call-operator",
  "concurrent_refresh": "single-flight"
}
```

El registro contiene una referencia, nunca la credencial. También indica quién debe actuar cuando la renovación deje de funcionar. Si nadie puede responder a esa pregunta antes del despliegue, el trabajo la responderá mal durante la noche.

## Un 401 necesita pruebas antes de activar la renovación

Renueva solo cuando la respuesta y el registro de credenciales respaldan el diagnóstico de caducidad. Tratar cada 401 como una caducidad oculta errores de configuración y puede generar una larga cadena de intentos de renovación inútiles.

Empieza por el cuerpo de error, los encabezados y las expectativas sobre el formato del token documentados por el proveedor. Algunas API devuelven valores `WWW-Authenticate` compatibles con OAuth. Otras devuelven códigos de error JSON. Algunas colocan los fallos de autenticación detrás de una puerta de enlace que usa otro código de estado. Tu clasificador debe usar las señales documentadas para ese proveedor y, si no puede clasificarlas, terminar de forma segura.

Este contrato de clasificación es práctico:

```json
{
  "http_status": 401,
  "provider_code": "invalid_token",
  "www_authenticate": "Bearer error=\"invalid_token\"",
  "credential_ref": "reports-read",
  "token_generation": 17,
  "decision": "renew_once"
}
```

Una respuesta solo puede optar a `renew_once` si se cumplen todas estas condiciones: la solicitud usó una credencial emitida o seleccionada por tu puerta de enlace, esa credencial tiene una vía de renovación, la señal del proveedor coincide con la condición documentada de caducidad o token no válido y este trabajo todavía no ha renovado la generación 17.

Usa un resultado distinto para cada clase de fallo. Un encabezado mal formado pertenece a `configuration_error`, para que un desarrollador pueda revisar el generador de solicitudes. Una discrepancia de audiencia pertenece a `credential_binding_error`, que requiere corregir la solicitud del token o la configuración del recurso. Una concesión revocada pertenece a `reauthorization_required`, donde el sistema detiene las acciones externas e indica al operador adecuado qué identidad necesita consentimiento. Un 403 pertenece a `permission_denied`; renovarlo es un comportamiento aprendido sin fundamento.

Los problemas del reloj provocan una parte sorprendente de los diagnósticos falsos. Un cliente que calcula la caducidad local puede rechazar pronto un token utilizable, mientras que un cliente con el reloj desfasado puede enviar uno caducado. Registra la caducidad proporcionada por el emisor cuando recibas el token, conserva un pequeño margen de seguridad y usa un reloj de sistema fiable. No permitas que cada agente calcule su propia caducidad a partir de una afirmación del token decodificado. Eso duplica la lógica del protocolo y fomenta discrepancias.

No inspecciones el contenido de un token solo para decidir si puedes confiar en él. Un JSON Web Token puede contener una afirmación `exp`, pero decodificar su contenido base64url no verifica su firma, emisor, audiencia ni estado de revocación. Úsalo solo como indicio después de que el componente que lo recibió haya completado la validación documentada por el proveedor. Los tokens de acceso opacos no ofrecen ningún contenido que inspeccionar, otra buena razón para que el llamador dependa del tratamiento de respuestas y no de la arqueología de tokens.

## Los límites de reintentos protegen el sistema remoto y tus pruebas

Después de una caducidad clasificada, permite una renovación coordinada y una repetición controlada. Más reintentos no mejoran la autorización; sobre todo ocultan una ruta de renovación rota y dificultan la lectura de la auditoría.

La regla de repetición depende de lo que pueda hacer la operación. Una solicitud de lectura normalmente tolera una repetición después de la renovación. Una escritura necesita pruebas más sólidas porque el servicio remoto pudo procesarla antes de que la respuesta de caducidad, el tiempo de espera o la pérdida de conexión llegaran al llamador.

Clasifica las operaciones al diseñar la interfaz de acciones:

| Clase de operación | Ejemplo | Recuperación después de la renovación |
| --- | --- | --- |
| Lectura | Obtener un registro | Repetir una vez si la solicitud no tiene efectos externos |
| Escritura idempotente | Sustituir un documento en una versión conocida | Repetir una vez si el proveedor garantiza la idempotencia para ese método y condición |
| Escritura con token de idempotencia | Crear un borrador de factura | Reutilizar exactamente el mismo token y el mismo contenido una vez |
| Escritura no idempotente | Enviar un mensaje o activar un pago | Conciliar primero y actuar solo si el sistema remoto confirma que no hubo un efecto anterior |

Los nombres de los métodos HTTP no resuelven esto. `PUT` suele expresar una intención idempotente, pero un proveedor puede asociarle una notificación por correo o una acción descendente asíncrona. `POST` puede ser seguro si el proveedor admite un campo de idempotencia. Lee el contrato del endpoint y prueba el comportamiento real.

Para las operaciones con token de idempotencia, créalo antes de la primera llamada de red y persístelo junto con una huella canónica de la solicitud. En cada reintento, envía exactamente el mismo token y un contenido lógicamente idéntico. No generes un token nuevo después de un 401. Un token nuevo indica al proveedor que se trata de una operación distinta y anula el objetivo.

```json
{
  "operation_id": "job-84f3/create-draft",
  "idempotency_token": "a stable random value stored before send",
  "request_fingerprint": "method, path, normalized body hash",
  "attempt": 1,
  "authorization_generation": 17
}
```

La expresión «normalized body hash» importa. Si el generador de reintentos cambia una marca de tiempo, el orden de una matriz o una etiqueta generada, puede convertir silenciosamente el mismo token de idempotencia en una solicitud incompatible. Algunos proveedores rechazan esa discrepancia. Otros la gestionan de forma incoherente. Conserva el primer cuerpo serializado o canonicalízalo una vez y reutilízalo.

La lógica de reintentos por límite de velocidad y por red debe compartir este presupuesto. Un agente que usa tres reintentos de red, después un reintento de renovación y luego otros tres reintentos de red ha creado siete oportunidades para duplicar o sobrecargar una operación. Define un presupuesto de intentos para toda la operación. Por ejemplo, una lectura segura podría permitir una llamada inicial, una ruta de renovación y una repetición. Una acción similar a un pago quizá solo permita una llamada inicial y después la conciliación.

Registra también cada reintento suprimido. Los operadores necesitan ver que el sistema se detuvo intencionadamente después de `renewal_attempted=true`, no suponer que el agente se bloqueó. Ese registro también señala con claridad cuándo un proveedor cambia el formato de un error y el clasificador empieza a rechazar una recuperación que antes permitía.

## Los puntos de control permiten reanudar un trabajo sin inventar su pasado

Una tarea larga de un agente debería guardar por separado los efectos remotos completados y la intención pendiente, porque un token renovado no puede decir qué ocurrió antes del fallo. El patrón defectuoso habitual guarda solo una transcripción de chat o el plan final del agente y después le pide reconstruir el estado tras una interrupción.

Usa un diario de tareas con registros que un programa pueda conciliar. Cada llamada externa prevista necesita un ID de operación estable. Cada resultado confirmado necesita el ID del recurso del proveedor, su versión o ETag si existe y la huella de la solicitud. Cada resultado incierto necesita la regla de búsqueda que lo resuelva.

Un punto de control compacto podría verse así:

```json
{
  "task_id": "release-2025-04-17-42",
  "completed": [
    {"operation_id": "create-change", "remote_id": "CR-819", "version": "6"},
    {"operation_id": "upload-bundle", "remote_id": "asset-552"}
  ],
  "pending": {
    "operation_id": "attach-bundle",
    "request_fingerprint": "POST /changes/CR-819/assets body-sha256:...",
    "reconcile": "list assets for CR-819 and match asset-552"
  },
  "authorization_state": "authorization_expired"
}
```

El trabajo no necesita guardar cada pensamiento intermedio. Necesita suficientes datos para determinar la siguiente acción remota segura. Mantén los secretos, los encabezados bearer y las respuestas de renovación fuera de este diario. Esos materiales pertenecen al propietario de las credenciales, no al almacenamiento general de tareas.

Las condiciones de versión importan durante una pausa. Si la tarea leyó la versión 6 de un documento antes de la caducidad y se reanuda una hora después, otro actor puede haberlo cambiado. Usa ETags, números de revisión, encabezados condicionales o campos de concurrencia específicos del proveedor cuando estén disponibles. Si la condición falla, indica al agente que el plan antiguo ya no es aplicable. No renueves el token y sobrescribas un estado más reciente porque la tarea crea que controla el mundo.

Aquí es donde el trabajo autónomo necesita un límite para el criterio. Un trabajo puede reanudar con seguridad una carga cuyo destino y hash ya registró. No debería replantear sin más un despliegue, modificar un registro de aprobación ni elegir otro destino después de que su contexto original haya envejecido. Marca esas operaciones para exigir una confirmación nueva después de la recuperación.

## La respuesta de recuperación debe indicar al agente qué puede hacer

Una puerta de enlace de acciones debería devolver un resultado de recuperación estructurado, no una frase vaga de autenticación que invite al agente a improvisar. El resultado debe indicar si la llamada se ejecutó, si la puerta de enlace renovó la autorización, si se permite repetirla y si debe intervenir una persona.

Un resultado útil separa el estado de ejecución del estado de las credenciales:

```json
{
  "operation_id": "attach-bundle",
  "execution_state": "not_sent",
  "authorization_state": "reauthorization_required",
  "retry_allowed": false,
  "credential_ref": "release-api",
  "operator_action": "Reauthorize the release-api connection, then resume task release-2025-04-17-42",
  "safe_resume_from": "attach-bundle"
}
```

`not_sent` significa que la puerta de enlace se detuvo antes de entregar la solicitud al cliente de red. `outcome_unknown` significa que no puede afirmarlo. No permitas que ambos casos se reduzcan a `failed`. El primero puede esperar a la reautorización. El segundo debe conciliarse con el servicio remoto antes de cualquier reintento.

Los agentes también necesitan un vocabulario reducido para la recuperación. Dales resultados como `completed`, `renewed_and_replayed`, `needs_reconciliation`, `reauthorization_required`, `permission_denied` y `configuration_error`. Cada resultado debe corresponder a un único comportamiento permitido. Por ejemplo, un agente puede continuar después de `renewed_and_replayed`; puede ejecutar una conciliación de solo lectura documentada después de `needs_reconciliation`; y debe detener las escrituras externas después de `reauthorization_required`.

Evita devolver respuestas sin procesar del proveedor como única señal. Los detalles originales ayudan al diagnóstico, pero los agentes pueden interpretarlos mal, sobre todo cuando los proveedores usan textos incoherentes. Guarda la respuesta original en un registro de diagnóstico protegido y devuelve al llamador una decisión estable y legible por máquinas.

Un buen mensaje de error nombra la referencia de identidad y la operación bloqueada sin exponer secretos. «La credencial `release-api` necesita reautorización antes de poder ejecutar `attach-bundle`» indica al operador dónde actuar. «No autorizado» no informa de nada.

## Las credenciales de renovación necesitan controles más estrictos que los tokens de acceso

Una credencial de renovación merece una protección mayor porque normalmente puede durar más que el token de acceso al que sustituye. No resuelvas la caducidad distribuyendo esta credencial de mayor duración a cada espacio de trabajo del agente, directorio de compilación, variable de entorno o transcripción.

OAuth 2.0 Security Best Current Practice, RFC 9700, recomienda la rotación de tokens de renovación o tokens de renovación vinculados al emisor para clientes públicos. El soporte exacto depende del proveedor, pero la lección de seguridad se aplica incluso cuando el proveedor usa otro protocolo: una credencial renovable robada ofrece una ventana de abuso mucho mayor que un token bearer normal de corta duración.

Mantén el material de renovación dentro del almacén cifrado de credenciales de la puerta de enlace de acciones. Restringe qué definiciones de acciones pueden seleccionarlo, exige aprobación humana explícita cuando la acción lo justifique y convierte la reautorización en una acción separada del operador. Un almacén bloqueado debe denegar el trabajo, no permitir que los agentes recurran a secretos copiados en archivos de configuración.

Sallyport sigue este modelo para las acciones HTTP y SSH compatibles: su bóveda cifrada conserva la credencial, mientras el agente solicita una acción a través de su adaptador MCP y recibe solo el resultado. Esta separación es útil porque el agente no puede imprimir una credencial de renovación en su propio contexto cuando la recuperación falla.

Sé cuidadoso con las respuestas de renovación. Algunos proveedores rotan la credencial de renovación e invalidan el valor antiguo al usarlo. El propietario de la renovación debe sustituir el valor almacenado de forma atómica antes de liberar las llamadas en espera. Si escribe el nuevo token de acceso, pero pierde la credencial de renovación sustituida, el trabajo puede funcionar brevemente y fallar de forma permanente en la siguiente caducidad.

No registres nunca estos campos: `Authorization`, token de acceso, token de renovación, secreto del cliente, afirmación firmada ni el cuerpo completo del endpoint de tokens. El enmascarado no basta cuando los sistemas copian objetos de solicitud sin procesar antes de que se ejecute el enmascarador. Diseña el registrador para aceptar referencias de credenciales y números de generación de tokens en lugar de estructuras que contengan secretos.

## La aprobación humana debe restaurar la autoridad, no crear una tormenta de reintentos

La aprobación humana solo ayuda cuando corresponde a una decisión clara: permitir esta ejecución del agente, permitir esta llamada sensible o restaurar una autorización revocada. Un botón genérico de «reintentar» después de una caducidad suele convertir al operador en un sello automático de una acción poco clara.

Separa las aprobaciones. La reautorización proporciona al propietario de la credencial una concesión nueva o una ruta de renovación utilizable. La autorización de sesión decide si este proceso concreto del agente puede solicitar acciones. La aprobación por llamada decide si una operación sensible puede ocurrir ahora. Son decisiones diferentes, y fusionarlas produce avisos molestos o permisos permanentes excesivos.

Cuando una credencial necesita reautorización, muestra la referencia afectada, la etiqueta de identidad o conexión, la operación bloqueada y el punto de control de la tarea. No muestres el token. Una vez restaurada la autorización, la puerta de enlace debe reanudar solo la operación pendiente registrada en el punto de control. No debe repetir silenciosamente todas las llamadas fallidas de la transcripción del agente.

La escala fija de decisiones de Sallyport encaja bien con este límite: una bóveda bloqueada deniega todas las acciones, los procesos nuevos de agentes necesitan aprobación de sesión de forma predeterminada y las credenciales seleccionadas pueden exigir aprobación en cada uso. Los controles no intentan inferir la intención a partir de un montón de reglas, lo que ayuda cuando una credencial caducada interrumpe una ejecución legítima.

La fatiga por aprobaciones suele ser un error de diseño. Si un operador recibe una docena de avisos porque diez llamadas paralelas detectan la misma caducidad, el coordinador de renovación no agrupó el evento. Presenta una sola solicitud de reautorización, haz que las demás llamadas esperen y comunica la decisión resultante a cada tarea.

No uses la aprobación para ocultar un resultado de escritura desconocido. En ese caso, la solicitud correcta pregunta si el operador quiere que el sistema concilie el estado remoto, no si quiere reintentar. Una persona puede autorizar por error un duplicado con la misma facilidad que un agente.

## Las pruebas de caducidad deben incluir escrituras inciertas y trabajadores simultáneos

Una prueba de renovación que devuelve un 401 sintético antes de una lectura demuestra muy poco. Los fallos más dañinos aparecen en los límites: durante un trabajo paralelo, después de confirmar una escritura, cuando se produce una rotación de renovación o cuando se revoca una concesión.

Construye un proveedor de prueba o un accesorio HTTP controlable que registre los identificadores de operaciones recibidos y pueda inyectar fallos en puntos definidos. Tus comprobaciones deben inspeccionar tanto el registro del efecto remoto como el registro de auditoría local. Una respuesta final correcta puede ocultar una creación duplicada.

Ejecuta estos casos antes de confiar en un agente de larga duración:

1. Caduca el token de acceso antes de una lectura. Confirma que solo un trabajador renueva y que todas las lecturas en espera usan la nueva generación.
2. Caduca antes de una escritura idempotente. Confirma que la puerta de enlace renueva una vez y repite la operación con el ID original y el cuerpo original.
3. Descarta la respuesta después de que el proveedor registre una escritura no idempotente. Confirma que el trabajo entra en `outcome_unknown`, ejecuta su búsqueda y no crea un segundo efecto.
4. Revoca la concesión antes de la renovación. Confirma que cada acción dependiente se detiene con `reauthorization_required` y que ningún bucle llama repetidamente al endpoint de tokens.
5. Devuelve una credencial de renovación rotada y después interrumpe el almacenamiento. Confirma que la puerta de enlace detecta la sustitución incompleta y bloquea futuras renovaciones en lugar de usar una copia antigua.

Prueba el tiempo de forma explícita. Inyecta un reloj en el componente de credenciales para poder situar la caducidad justo antes de construir la solicitud, justo después de construir el encabezado y mientras una solicitud espera en una cola. Esperar a que caduque un token real hace que las pruebas sean lentas y deja sin probar los casos importantes de sincronización.

Por último, prueba la ruta de auditoría sin acceso a la bóveda. Debes poder verificar que la secuencia de llamadas intentadas y decisiones de renovación no ha cambiado, aunque no puedas descifrar todos los registros. `sp audit verify` de Sallyport comprueba su cadena de hashes sobre datos de auditoría cifrados sin necesitar una clave de la bóveda, que es la forma adecuada de verificación durante un incidente en el que el acceso a las credenciales puede seguir bloqueado.

Un trabajo que gestiona bien la caducidad hace menos cosas después de que falle la autorización. Se detiene, clasifica el fallo, permite que un único propietario renueve, concilia la incertidumbre y reanuda solo la operación registrada. Esa contención evita efectos secundarios duplicados y deja al operador pruebas útiles en lugar de un montón de reintentos.
