Mandé un payload incompleto y el proveedor me devolvió un 200 cada vez. Lo descubrí leyendo la salida impresa: un campo mostraba un valor genérico que yo nunca había elegido. El proveedor nunca lo mencionó. Se portó como el de la ventanilla que te sella el formulario incompleto porque él mismo rellenó los casilleros vacíos con lo primero que se le ocurrió. Uno se va contento con su sello hasta que lee lo que escribió.
Un payload es el paquete de datos que un sistema le envía a otro. Al mío le faltaban tres campos, y el código HTTP no distingue entre "tus datos están bien" y "te completé lo que faltaba a mi criterio".
Lo armé probando combinaciones
Cuando construí la integración no tenía ejemplo oficial, así que armé el cuerpo de la petición probando combinaciones y me quedé con la que no fallaba. Mi único criterio de éxito era que la respuesta no explotara, y ese criterio aguanta mucho tiempo sin avisar que es malo.
Avísame cuando falte un campo
El texto no venía de la plantilla
Al ver ese valor asumí que el proveedor imprimía un texto fijo y que tocaba pedirle un cambio de formato. La teoría duró poco: ese texto salía de un campo que yo nunca mandé y que el proveedor completaba con su propio default.
Tres destinos para un campo que falta
El diff contra el ejemplo oficial fue incómodo: faltaban tres campos y ninguno había dado error nunca. El proveedor trata un campo incompleto de tres maneras: lo ignora, lo rellena con un default o lo rechaza. Las dos primeras terminan en 200.
El único campo que sí me rechazaban es uno de texto libre con formato estricto, que escribe una persona a mano. Su valor por defecto traía un espacio, y el espacio no pasa el filtro. Ese error ruidoso lo arreglé mucho antes, porque era el único que daba error.
Lo que va escrito y lo que omito a propósito
Ahora escribo ese campo en la petición en vez de dejarlo al criterio del proveedor. El identificador de catálogo va en 0 a propósito: es el valor que el ejemplo oficial usa para una línea que no sale del catálogo. El identificador del emisor se lee de la configuración y, cuando no está, lo omito en lugar de mandarlo vacío, porque para una API permisiva un campo vacío es peor que uno ausente: parece una decisión. El campo que escribe la persona se sanea antes de salir, ajustado al formato que exige el filtro.
No armé un validador de esquema propio ni un cliente generado del contrato. El proveedor no publica un esquema formal, así que inventarlo habría significado mantener mi suposición en dos lugares en vez de uno.
Archivábamos el borrador en vez del envío
Guardábamos el borrador del documento en lugar del cuerpo que salía por la red, así que durante toda la investigación leí mi intención en lugar de mi envío. Ahora una función pura reconstruye el body real y eso es lo que archivamos: recibe datos y devuelve datos, sin tocar nada más.
En el camino encontré un test unitario que afirmaba el valor equivocado, en verde desde el primer día, defendiendo el bug con una convicción admirable.
No di por bueno ningún arreglo con la respuesta HTTP: verifiqué cada uno leyendo la salida impresa, el único lugar donde el error era visible para una persona. Sigo sin un esquema formal del contrato, así que ningún payload nuevo sale sin su diff campo por campo contra el ejemplo oficial. El 200 dejó de contar como evidencia, y si el proveedor quiere convencerme de que mi paquete está completo, tendrá que hacerlo con algo más que un sello.
