# Parámetros destructivos de API: valida antes de eliminar

Los agentes de IA realizan llamadas destructivas a las API con demasiada facilidad cuando una solicitud parece correcta desde el punto de vista sintáctico. Un cuerpo JSON válido, un nombre de cuenta conocido y una búsqueda exitosa no demuestran que el agente vaya a eliminar el objeto correcto. Antes de que una solicitud externa elimine, revoque, desconecte, cancele o sobrescriba datos, valida la identidad, la propiedad, el alcance y la vigencia directamente con el servicio.

He visto a ingenieros cuidadosos leer una solicitud como «elimina este entorno de pruebas», mientras que la API la interpretaba como «elimina todos los entornos de esta organización». En sentido estricto, el código muchas veces no tenía un error. Aceptaba un selector formado de manera imprecisa, resolvía un nombre en la cuenta equivocada o confiaba en datos obtenidos antes durante la ejecución. Un agente autónomo comete esos errores cotidianos más rápido y con mayor confianza.

Esto no se soluciona pidiendo al modelo que sea más prudente. Coloca una barrera determinista entre la solicitud propuesta por el agente y la solicitud autenticada. La barrera debe rechazar la ambigüedad por defecto y hacer que una persona apruebe el objetivo y el efecto reales.

## Una solicitud bien formada aún puede referirse al objeto equivocado

Una solicitud solo está suficientemente preparada para enviarse después de establecer cuatro hechos independientes: el recurso es del tipo correcto, su identificador inmutable es el previsto, pertenece a la cuenta principal aprobada y la operación afectará al conjunto aprobado. Los equipos suelen mezclar estos hechos en una sola búsqueda. Ese atajo es el origen de muchos problemas al eliminar.

Considera un servicio que expone tanto un nombre visible como un ID:

```json
{
  "id": "env_7d3a",
  "name": "staging",
  "account_id": "acct_blue",
  "state": "active"
}
```

Un agente que busca `staging` ha encontrado un candidato, no una autorización para eliminarlo. Muchas cuentas tienen un entorno `staging`. Incluso dentro de una cuenta, los nombres pueden reutilizarse cuando desaparece un objeto antiguo. La única solicitud de acción segura es la que usa el ID inmutable devuelto y lo comprueba contra el ID principal esperado.

La diferencia importa especialmente cuando una API admite una ruta principal como `/accounts/{account_id}/environments/{environment_id}`. Comprueba ambos segmentos. No deduzcas la propiedad porque el ID apareciera en una respuesta de búsqueda anterior o porque el agente incluyera el mismo nombre de cuenta en su plan. La cuenta principal de la URL de acción forma parte del límite de autorización.

El tipo de recurso merece su propia comprobación. Las API suelen usar un punto de búsqueda compartido o devolver registros mezclados. Un resultado llamado `staging` puede ser un entorno, un proyecto, un grupo de acceso o una plantilla guardada. Si el servicio tiene distintos puntos de eliminación, selecciona el punto solo después de verificar el tipo devuelto. No construyas un punto concatenando una cadena de tipo producida por el modelo.

Por último, define el efecto esperado antes de inspeccionar los candidatos. «Eliminar la implementación antigua» puede significar borrar un registro de implementación, detener un trabajo en ejecución, revocar un token emitido para él o borrar todo el entorno. Esos efectos no pueden compartir una operación genérica `delete`. Haz explícitos el verbo solicitado y el tipo de objeto en el contrato de acción.

## Los nombres ayudan a las personas, los ID controlan la solicitud

Usa un ID inmutable para dirigirte a un recurso, pero muestra a quien aprueba suficiente contexto humano para detectar una mala elección. Una tarjeta de aprobación que solo dice `env_7d3a` invita a aprobar sin revisar. Una que solo dice `staging` deja espacio para la ambigüedad. Muestra ambos datos al operador, junto con la cuenta principal y las consecuencias de la operación.

Un registro de objetivo útil puede tener este aspecto:

```json
{
  "operation": "delete_environment",
  "account": {"id": "acct_blue", "name": "Blue Team"},
  "target": {"id": "env_7d3a", "name": "staging", "type": "environment"},
  "expected_state": "active",
  "effect": "permanently removes this environment and its managed resources"
}
```

El nombre visible de la cuenta ayuda al operador a detectar que el agente terminó en el tenant equivocado. El nombre del recurso le ayuda a reconocer el objeto. Los ID hacen que la solicitud no sea ambigua. El efecto declarado evita un fallo común de aprobación: la persona cree haber aprobado una detención reversible, cuando el proveedor va a eliminar datos.

No resuelvas un nombre tomando el primer resultado de búsqueda. Los puntos de búsqueda suelen ordenar por relevancia, devolver coincidencias parciales o paginar. Si una tarea proporciona un nombre exacto, exige exactamente un candidato después de filtrar por la cuenta principal y el tipo esperado. Cero candidatos debe hacer fallar la operación. Más de un candidato también. Pedir al agente que elija uno no arregla el problema, porque carece de pruebas para distinguirlos.

El tratamiento de mayúsculas y minúsculas también necesita una regla específica del servicio. Algunos proveedores distinguen entre mayúsculas y minúsculas; otros normalizan los nombres. No normalices los nombres por tu cuenta y supongas que el proveedor actuará del mismo modo. Usa el recurso devuelto por el proveedor como autoridad y conserva la etiqueta exacta en el registro de aprobación.

Las etiquetas, los rótulos y las descripciones aportan contexto, no identidad. Cambian con frecuencia y los usuarios pueden escribir casi cualquier cosa en ellos. Una etiqueta como `temporary=true` puede acotar una lista revisada, pero no debe sustituir la vinculación con una cuenta ni un ID de recurso inmutable.

## El alcance debe ser concreto antes de que el agente pida aprobación

Una operación destructiva tiene un alcance aunque su cuerpo de solicitud contenga un solo ID. El alcance incluye la cuenta principal, los recursos seleccionados, los recursos secundarios que el proveedor elimine automáticamente y cualquier filtro que amplíe la selección. Hazlo concreto antes de pedir la aprobación.

La eliminación de un solo objeto tiene un contrato sencillo: un ID inmutable, una cuenta principal esperada y un tipo de recurso. La eliminación masiva necesita otro contrato. Primero debe producir un conjunto resuelto y después obtener aprobación para ese conjunto o para un resumen acotado que una persona pueda revisar. Enviar un selector directamente a un punto destructivo deja que el servicio externo decida el alcance después de la aprobación.

Supón que un agente propone esta solicitud:

```json
{
  "account_id": "acct_blue",
  "filter": {"label": "cleanup-candidate"},
  "delete": true
}
```

Ese cuerpo oculta el único dato que el operador necesita: qué recursos coinciden ahora mismo. Expande el selector mediante una llamada de listado de solo lectura, rechaza cualquier paginación que no hayas inspeccionado por completo y normaliza el resultado en ID. Después muestra un recuento y una muestra breve con nombres. Si el conjunto supera el límite aprobado, detente y exige una instrucción nueva.

Nunca permitas que un filtro omitido signifique «todos». En los esquemas de solicitud, distingue entre una lista vacía obligatoria y un selector ausente. Mejor aún, prohíbe que los puntos destructivos acepten filtros dentro de la interfaz de acciones del agente. Haz que la puerta de enlace acepte únicamente una lista de ID resueltos para el trabajo masivo.

Una estructura práctica es:

```json
{
  "operation": "delete_resources",
  "account_id": "acct_blue",
  "resource_type": "snapshot",
  "resource_ids": ["snap_104", "snap_105"],
  "selection_observed_at": "2025-03-08T14:32:11Z"
}
```

Rechaza una matriz `resource_ids` vacía, salvo que el flujo permita explícitamente ese caso. Rechaza los ID duplicados. Rechaza los ID de otra cuenta. Aplica un máximo que encaje con la operación aprobada por la persona. Un límite no sustituye la revisión, pero evita que un bucle mal formado convierta una limpieza de dos recursos en un incidente de mil recursos.

Las eliminaciones en cascada también forman parte del alcance. Si borrar un proyecto elimina repositorios, claves de implementación, entornos o registros de facturación, indícalo antes de la aprobación. Si el proveedor solo muestra los detalles de la cascada después de una llamada previa, conserva esa respuesta y exige que el agente la presente. «Eliminar proyecto» es demasiado impreciso cuando el proyecto tiene objetos dependientes.

## Lee el servicio dos veces cuando el tiempo pueda cambiar el objetivo

Una lectura previa verifica la intención, pero no congela el objeto. El recurso puede cambiar, trasladarse a otra cuenta o desaparecer entre la validación y la eliminación. Para operaciones sensibles, vuelve a leer el objetivo justo antes de la mutación y utiliza el control de concurrencia del servicio cuando exista.

HTTP ofrece un mecanismo estándar para este patrón. RFC 9110 define solicitudes condicionales con `If-Match`: el servidor ejecuta el método solicitado solo si la representación actual coincide con una etiqueta de entidad proporcionada por el cliente. Un `GET` puede devolver un `ETag`, y un `DELETE` posterior puede incluir exactamente ese valor.

```http
GET /v1/accounts/acct_blue/environments/env_7d3a HTTP/1.1
Authorization: Bearer [injected credential]

HTTP/1.1 200 OK
ETag: "v42"
Content-Type: application/json

{"id":"env_7d3a","account_id":"acct_blue","name":"staging","state":"active"}
```

Después de comparar el cuerpo con el objetivo aprobado, envía:

```http
DELETE /v1/accounts/acct_blue/environments/env_7d3a HTTP/1.1
If-Match: "v42"
Authorization: Bearer [injected credential]
```

Si el servicio devuelve `412 Precondition Failed`, considéralo una barrera activada correctamente. No pidas al agente que vuelva a intentar la eliminación sin condición. Obtén de nuevo el recurso, compáralo con los hechos aprobados y solicita una aprobación nueva si cambió algún dato relevante. Un conflicto de versión demuestra que la aprobación anterior quizá ya no sea válida.

Algunos servicios usan números de revisión, marcas de actualización, campos de generación o tokens de solicitud en lugar de ETag HTTP. Usa el mecanismo documentado por el proveedor. Si no ofrece ninguno, reduce el intervalo entre la lectura final y la escritura, haz que la acción sea secuencial y acepta que no puedes demostrar que el objetivo permaneció sin cambios. Esa limitación debe influir en si permites eliminaciones sin supervisión.

No confundas un `GET` exitoso con permiso para modificar. La credencial de lectura puede ver más de lo que la de escritura puede cambiar, y la autorización puede cambiar de forma independiente del estado del objeto. La respuesta de la mutación sigue determinando si el proveedor aceptó la solicitud.

## La validación pertenece al límite de las credenciales

La validación hecha únicamente en una instrucción para el agente o en código generado es orientativa. El componente que conserva o inyecta la credencial debe aplicar las comprobaciones, porque es el último punto capaz de impedir una solicitud saliente.

Este límite debe recibir una propuesta de acción estructurada, no una URL libre ni encabezados arbitrarios. Una definición de acción limitada puede exigir campos como el ID de cuenta, el ID de recurso, el método, el tipo esperado y la versión esperada. Puede construir la ruta saliente a partir de segmentos validados y rechazar parámetros de consulta que amplíen el alcance.

No aceptes una URL completa del agente para intentar deducir su seguridad después. La codificación de URL, las claves de consulta repetidas, los nombres de host alternativos y la normalización de rutas convierten esto en una disputa entre analizadores. Acepta campos tipados, valida cada uno contra el contrato del proveedor y construye tú la URL. Aplica la misma regla a los cuerpos de solicitud. Genera una estructura conocida en lugar de pasar un bloque cuyos campos no inspeccionaste.

Una barrera mínima puede imponer esta secuencia:

1. Confirma que la operación solicitada existe en una lista permitida y que su método es destructivo por diseño.
2. Resuelve cada objetivo declarado con una llamada de lectura realizada bajo la misma cuenta principal.
3. Compara el ID, el tipo, la cuenta principal y el estado requerido devueltos con la propuesta estructurada.
4. Obtén aprobación para el efecto resuelto, vuelve a comprobar la vigencia y envía la mutación.
5. Registra el resultado, incluido el ID de solicitud del proveedor cuando lo devuelva.

Mantén pequeña la lista permitida. Una vía de escape genérica llamada `raw_http` anula todas las comprobaciones de este artículo porque el agente puede volver a introducir destinos, métodos y cuerpos arbitrarios. Los ingenieros añaden estas vías cuando falta un punto y luego olvidan que existen, hasta que eluden las barreras que creían tener.

Las credenciales deben permanecer fuera del contexto del agente. El agente necesita el resultado de una acción permitida, no un token de portador que pueda copiar en un comando curl, un registro o una integración de terceros. Sallyport sigue este modelo para sus acciones HTTP: conserva la credencial en su bóveda cifrada, ejecuta la solicitud por sí mismo y devuelve el resultado al agente.

## DELETE no significa que la solicitud sea sencilla o reversible

Los nombres de los métodos HTTP no describen todo el efecto empresarial. RFC 9110 indica que `DELETE` pide al servidor de origen eliminar la asociación entre un recurso objetivo y su funcionalidad actual. La norma no promete que los datos desaparezcan de inmediato, que los datos relacionados sobrevivan ni que un reintento sea inocuo en una API concreta.

La documentación del proveedor debe definir el efecto real. Algunas API marcan un objeto para eliminarlo más adelante. Algunas crean un tombstone. Otras lo separan de su cuenta principal. Otras eliminan también los elementos secundarios. Lee los códigos de respuesta del punto y las notas sobre su ciclo de vida antes de clasificar una operación como de bajo riesgo.

No envíes un cuerpo con `DELETE` salvo que el proveedor lo documente explícitamente. RFC 9110 indica que el contenido recibido en una solicitud `DELETE` no tiene una semántica general definida y puede hacer que las implementaciones rechacen la solicitud. Una API de eliminación que dependa de filtros en un cuerpo puede ser válida para ese proveedor, pero merece pruebas adicionales mediante su ruta de cliente documentada. No debe convertirse en una excusa para pasar selectores libres desde un agente.

Los reintentos requieren el mismo cuidado. Un tiempo de espera de red crea un resultado desconocido: el proveedor puede haber completado la eliminación después de que el cliente dejara de esperar. Reintentar de inmediato puede generar registros engañosos, provocar un segundo efecto en un punto mal diseñado o eliminar un recurso sustituido si el reintento vuelve a resolver por nombre.

Gestiona un resultado desconocido con una regla de inspección previa. Consulta el ID inmutable exacto bajo la cuenta principal exacta. Si el objeto ya no existe y el modelo de eliminación del proveedor permite esa interpretación, registra la operación como completada, indicando que la primera respuesta era incierta. Si todavía existe, inspecciona su estado y el historial de solicitudes del proveedor cuando esté disponible antes de decidir si reintentas. Reutiliza una clave de idempotencia en las operaciones que la admitan, pero no inventes idempotencia cuando el proveedor no la ofrece.

Un `204 No Content` solo indica que el servidor aceptó y completó la interacción HTTP tal como la define ese punto. No demuestra que toda la limpieza posterior haya terminado. Si la siguiente acción del agente depende de que la eliminación esté completa, consulta el estado documentado de la operación o del recurso en lugar de tratar el cuerpo vacío como una garantía.

## La aprobación debe mostrar las consecuencias, no el transporte en bruto

Las personas toman mejores decisiones cuando la aprobación describe el efecto en términos corrientes e incluye los identificadores necesarios para verificarlo. Mostrar un método, una ruta y un cuerpo JSON es útil para un ingeniero de API, pero obliga a la persona que debe detectar un objetivo incorrecto a hacer demasiadas interpretaciones.

Para un recurso individual, la aprobación debe indicar qué cambiará, nombrar la cuenta principal, mostrar el nombre y el ID del recurso y mencionar los efectos irreversibles o en cascada. Para una operación masiva, muestra el recuento, una muestra limitada, la regla de selección usada para crear la lista y el hecho de que la acción final utiliza los ID congelados, no la regla.

No pidas una aprobación amplia al principio de una ejecución larga del agente y la uses para todas las eliminaciones posteriores. El conjunto objetivo cambia a medida que el agente descubre recursos. Vincula la aprobación a una sesión y a la acción resuelta. Si cambia el proceso del agente, la aprobación no debe seguir silenciosamente a un proceso nuevo que quizá tenga código o instrucciones diferentes.

El fallo opuesto es la fatiga de aprobación. Pedir que alguien haga clic para cada lectura inofensiva le enseña a hacer clic sin leer, y después da a una eliminación peligrosa el mismo peso visual. Mantén las lecturas sin interacción cuando corresponda, exige autorización de sesión para un agente que acaba de iniciarse y reserva la confirmación por acción para credenciales o acciones con efecto destructivo. Una persona que ve menos avisos puede examinar los importantes.

El registro de aprobación necesita una caducidad clara. Cuanto más espera un agente después de la validación, menos significa la comprobación previa. Si la acción no puede ejecutarse pronto, haz que vuelva a resolver el objetivo y solicite otra aprobación. Puede parecer estricto durante una tarea de limpieza. Es menos costoso que explicar por qué una aprobación de hace una hora se aplicó a un recurso que entretanto había sido creado de nuevo.

## Las pruebas de auditoría deben reconstruir la decisión sin exponer secretos

Un registro de auditoría útil responde a algo más que «¿ocurrió una solicitud?». Debe permitir reconstruir lo que propuso el agente, lo que informó el servicio antes de la mutación, lo que aprobó una persona, lo que envió la puerta de enlace y lo que devolvió el servicio.

Captura los campos normalizados, no solo una cadena de solicitud sin procesar. Registra el nombre de la acción, la identidad de la sesión del agente, el ID de la cuenta principal, los ID de los recursos, las versiones esperadas, la hora de selección, el efecto aprobado, la hora de aprobación, el método y la ruta salientes, el estado de respuesta y el ID de solicitud del proveedor. Guarda hashes o formas redactadas del material de solicitud cuando pueda contener valores sensibles. Un registro que copia encabezados de autorización se ha convertido en un segundo almacén de credenciales.

Conserva la respuesta previa o un resumen protegido mediante integridad. Sin ella, un revisor posterior no puede saber si el agente eliminó el objeto equivocado porque falló la validación, porque el objeto cambió después de validarse o porque el servicio externo se comportó de forma distinta a la documentada. La diferencia determina cómo reparar el problema.

La evidencia contra manipulaciones importa cuando la misma máquina ejecuta el agente y la puerta de enlace de acciones. Un registro de texto modificable puede ser editado por el proceso que causó el incidente. Sallyport genera sus diarios Sessions y Activity a partir de un registro de auditoría cifrado y encadenado mediante hashes, y `sp audit verify` puede comprobar esa cadena sin conexión y sin una clave de la bóveda. Eso no convierte una mala aprobación en una buena, pero dificulta ocultar una alteración posterior del registro.

Prueba los registros tanto con solicitudes fallidas como exitosas. Los rechazos, las aprobaciones caducadas, los conflictos de versión y los ID mal formados muestran si los controles realmente bloquearon el trabajo. Un diario lleno de éxitos dice muy poco sobre si la barrera rechazaría una solicitud peligrosa.

## Construye las acciones destructivas como contratos limitados

La acción de API más segura es deliberadamente limitada. Acepta una operación conocida, exige una cuenta principal y un ID de objeto conocidos, realiza una comprobación previa y declara el efecto. Las interfaces generales parecen productivas hasta que un agente hace una solicitud inesperada y tu única defensa consiste en esperar que un operador detecte un parámetro sutil.

Empieza haciendo un inventario de las acciones que pueden eliminar, revocar, rotar, desactivar, sobrescribir, publicar o generar cargos. Para cada acción, anota los campos de objetivo inmutables, los campos principales, los estados permitidos, el comportamiento en cascada, el mecanismo de vigencia, el comportamiento de los reintentos y el texto de aprobación. Si no puedes expresar esos hechos, todavía no ofrezcas la acción a un agente autónomo.

Después introduce deliberadamente entradas incorrectas en la barrera: un ID de recurso válido bajo la cuenta equivocada, un nombre visible coincidente con dos resultados, un ETag obsoleto, una lista masiva vacía, un filtro omitido, un objetivo creado de nuevo con el mismo nombre y un tiempo de espera después del envío. Estas entradas revelan si el límite valida el significado o solo valida el JSON.

No resuelvas la ambigüedad haciendo que el agente escriba un plan más largo. Haz que la ambigüedad no pueda representarse en el contrato de acción. Un agente puede proponer una intención y reunir pruebas. El límite de las credenciales debe decidir si esas pruebas identifican un efecto permitido, en este momento y bajo esta cuenta. Esta división te proporciona un sistema que puedes inspeccionar cuando la solicitud es rutinaria y en el que puedes confiar cuando no lo es.
