Ir al contenido

Manual del agente

Este manual es para ti, agente de código (Claude Code, Cursor, Codex u otro). Trabajas para una persona que probablemente no programa: solo creó su cuenta en foliios y una clave de sandbox. Tu trabajo es llevar la integración de su proyecto hasta el verde sin más guía que este sitio.

Habla con la persona en lenguaje simple, sin jerga. Pregunta poco y una cosa a la vez. Haz tú todo lo que se pueda hacer en el proyecto; pídele a la persona solo lo que solo ella puede dar o hacer.

  • Nunca pidas la clave por el chat, ni la aceptes si la persona la pega, ni la imprimas. La clave (sk_sandbox_...) la escribe la persona en el archivo .env; tú la lees desde ahí. No la muestres en comandos, logs, respuestas ni commits. Si aparece en el chat, avísale que la clave quedó expuesta y que la revoque y cree otra desde su cuenta.
  • Detente y consulta a la persona antes de seguir si: la clave no empieza por sk_sandbox_; cualquier paso te acerca a producción o a la habilitación real ante la DIAN; un rechazo exige un dato fiscal que solo la persona tiene (un NIT real, una dirección, un régimen); o chocas con un límite del sandbox. No inventes datos fiscales.
  • Enlaza, no copies. Los límites, los escenarios, los campos, los errores y los eventos cambian con el código. Consúltalos siempre en la referencia generada y no los copies a tu código ni a tus respuestas como valores fijos:
  • Todas las llamadas van a https://api.foliios.com, con Authorization: Bearer <clave>. Todo lo que emites en sandbox es simulado: no tiene efecto fiscal y no llega a la DIAN.
  • Si el proyecto ya tiene configurado el servidor MCP de foliios, úsalo: sus herramientas llaman a lo mismo que la API. Si no, usa HTTP.

Fase 1. Detectar el lenguaje y la forma del proyecto

Sección titulada «Fase 1. Detectar el lenguaje y la forma del proyecto»

Haz: mira los archivos del proyecto (manifiestos, carpetas, scripts) y determina qué es: lenguaje, framework, si es una tienda, una planilla con scripts, un punto de venta, un servicio web o un script suelto, y dónde se genera una venta. Identifica cómo ejecuta pruebas y cómo guarda configuración.

Pregunta a la persona solo si no se deduce: «¿Este proyecto es el sistema donde registras tus ventas?».

Verifica: puedes decir en una frase qué hace el proyecto y en qué punto se produce una venta. Resúmeselo a la persona y confirma.

Si falla: si no encuentras dónde nace una venta, pregunta a la persona cómo registra hoy una venta y dónde.

Fase 2. Guardar la clave en .env, fuera del control de versiones

Sección titulada «Fase 2. Guardar la clave en .env, fuera del control de versiones»

Haz:

  1. Comprueba que .env está fuera del control de versiones antes de usarla: que esté en .gitignore y que git check-ignore .env lo confirme. Si no está, agrégalo a .gitignore primero. Si .env ya fue commiteado alguna vez, detente y avisa a la persona.
  2. Crea .env.example (sin valores) con los nombres de las variables: FOLIIOS_API_KEY, FOLIIOS_COMPANY_ID, FOLIIOS_API_URL=https://api.foliios.com.
  3. Pídele a la persona que abra .env ella misma y escriba la clave en la línea FOLIIOS_API_KEY=, y que te diga solo «listo». No le pidas que te la dé ni que la pegue en el chat.

Pregunta a la persona: «Abre el archivo .env de tu proyecto y escribe tu clave de sandbox después de FOLIIOS_API_KEY=. No me la envíes por aquí. Dime “listo” cuando la hayas guardado. ¿Y cuál es el identificador de tu empresa de pruebas? Lo ves en el portal de foliios, en el ambiente Sandbox; empieza por cmp_».

Verifica: carga .env sin imprimirlo y pregunta si la clave existe y empieza por sk_sandbox_ (solo ese prefijo, nunca el valor completo). Comprueba la conexión con GET /v1/sandbox/scenarios (no pide clave) y luego una lectura con clave, por ejemplo GET /v1/companies/{company_id}/sandbox/integration-check: debe responder 200.

Si falla: 401 es una clave mal escrita o revocada; 403 sandbox_only es una clave que no es de sandbox, así que detente y consulta a la persona; 404 es un company_id equivocado.

Haz estas preguntas en lenguaje simple y no más:

  1. ¿Qué sistema emite? ¿Una planilla, una tienda en línea, un programa de caja o un script? (Si no lo dedujiste en la fase 1.)
  2. ¿Vendes a consumidor final o a empresas? Con consumidor final no hace falta pedir datos del comprador: el documento va al consumidor final anónimo de la DIAN. Con empresas necesitas, como mínimo, su NIT (o cédula), su nombre y su correo. Consulta campos del documento para el comprador.
  3. ¿Cobras IVA? Qué porcentaje, o si vendes productos exentos o excluidos. El impuesto va en cada línea; revisa lines[].taxes en los campos.
  4. ¿De dónde salen las ventas? Un archivo, una base de datos, una pantalla, un botón. Es lo que vas a conectar.
  5. ¿Tienes una dirección pública para recibir avisos (webhooks)? Un servidor accesible desde internet con HTTPS. Si la persona no sabe qué es, la respuesta es no (ver fase 9).

Guarda las respuestas en el proyecto (un comentario o un archivo de notas), sin datos sensibles.

Haz: escribe en el proyecto una función que arme una factura desde una venta y la emita con POST /v1/companies/{company_id}/documents. Detalles que debes respetar, todos en la API v1 y en los campos:

  • Cabecera Idempotency-Key obligatoria (un identificador único por venta, estable entre reintentos).
  • Montos y cantidades como texto decimal ("4000.00"), no como números JSON.
  • resolution_id es obligatorio en una factura: tómalo de list_companies (MCP) o de lo que muestre el portal para la empresa de pruebas.
  • La respuesta es 202 con el documento en pending. No es el resultado final: consulta el documento con GET /v1/companies/{company_id}/documents/{document_id} hasta que pase a accepted.

Prueba primero con una venta de ejemplo, no con datos reales de la persona.

Verifica: el documento termina en accepted. Guarda su id, su number y su cufe (los necesitas en la fase 5).

Si falla: 422 trae en errors los campos que rompen una regla: corrige tú lo que sea de formato; si falta un dato que solo la persona tiene, pregúntaselo. Un rejected se trata en la fase 7.

Fase 5. Notas crédito y débito sobre esa factura

Sección titulada «Fase 5. Notas crédito y débito sobre esa factura»

Haz: agrega al proyecto la emisión de una nota crédito (type: credit_note, por ejemplo una devolución parcial) y una nota débito (type: debit_note, por ejemplo un interés) sobre la factura aceptada de la fase 4. Cada nota lleva correction con concept, description y billing_reference (number, cufe y issue_date de esa factura, que salen de GET .../documents/{document_id}). Una nota no necesita resolution_id: se numera con su propia serie. Los conceptos válidos y las reglas están en campos (correction).

Verifica: una nota crédito y una nota débito terminan en accepted, con la referencia a esa factura.

Si falla: revisa que el número y el CUFE de la referencia sean exactamente los de la factura aceptada, y que la factura sea de la misma empresa. Una referencia a una factura de otra empresa no cuenta.

Haz: haz que el proyecto reintente con seguridad. Si una emisión falla por red o responde 503, se reintenta con la misma Idempotency-Key y el mismo cuerpo: el resultado es el mismo documento (cabecera Idempotent-Replayed: true), no uno nuevo. Con la misma clave y otro cuerpo la respuesta es 409 idempotency_key_reused. Ante 429, espera y reintenta con pausa creciente; los límites vigentes están en escenarios, sección «Límites».

Verifica: emite dos veces la misma venta con la misma clave y comprueba que la segunda devuelve el mismo documento sin crear otro. Esta comprobación la declaras tú: la verificación final (fase 11) no puede comprobarla, así que repórtala a la persona como no verificada.

Si falla: si la segunda llamada crea otro documento, la clave no se está guardando entre intentos: debe salir de la venta (por ejemplo su número interno), no generarse de nuevo en cada intento.

Haz: provoca un rechazo a propósito con un escenario del sandbox (cabecera Foliios-Sandbox-Scenario con un escenario de rechazo, por ejemplo uno de los rejected_*; el catálogo completo, con la regla que dispara cada uno, está en escenarios). Verifica que el proyecto detecta el estado rejected, muestra la regla que rechazó (dian_messages) y permite corregir y reenviar con POST .../documents/{document_id}/resubmit.

Verifica: un documento rejected se corrige y se reenvía, y termina en accepted. Esta comprobación también la declaras tú; repórtala como no verificada.

Si falla: si la regla que rechaza exige un dato fiscal real (un NIT, un dígito de verificación, un correo del emisor) que solo la persona tiene, detente y consúltale.

Haz: emite un documento con el escenario dian_unavailable. La DIAN simulada «no responde», el documento pasa por contingencia y, pasados unos minutos, termina aceptado. Haz que el proyecto no trate la contingencia como un error: el documento sigue su curso y queda accepted solo. Consulta su estado hasta que lo esté. Los eventos asociados están en eventos de webhook.

Verifica: ese documento termina en accepted conservando la marca de que pasó por contingencia. Es lo que comprueba contingency en la verificación final.

Si falla: no lo reenvíes ni lo emitas de nuevo: espera. Si pasaron muchos minutos y sigue pending, revisa el estado con la API y consulta a la persona antes de insistir.

Los webhooks son avisos que foliios envía a una dirección del proyecto cuando le pasa algo a un documento.

Haz: una de dos, según lo que respondió la persona en la fase 3.

  • Si el proyecto puede recibirlos (tiene una URL pública con HTTPS): pídele a la persona que registre el endpoint en el portal, en el ambiente Sandbox de su empresa, y que te diga el secreto del endpoint por un canal seguro, no por el chat (guárdalo en .env como FOLIIOS_WEBHOOK_SECRET, igual que la clave). Implementa el receptor: valida la firma HMAC (X-Webhook-Signature), deduplica por el id del evento y responde 2xx rápido. Una redirección (3xx) no cuenta como entrega. Dispara un evento de prueba con POST /v1/sandbox/events. Formato y firma: eventos de webhook.
  • Si no puede recibirlos (una planilla, un script que corre en el computador de la persona): no registres ningún endpoint y consulta el estado de cada documento con GET .../documents/{document_id} hasta que sea final. Sin endpoint configurado, webhook_delivered queda en not_applicable y no bloquea el criterio de terminado.

Verifica: con endpoint, al menos una entrega exitosa (2xx); sin endpoint, el proyecto consulta el estado y no depende de avisos.

Si falla: la persona no sabe si puede recibirlos: asume que no. No le pidas que abra puertos ni exponga su computador.

Fase 10. Pruebas automáticas en el proyecto de la persona

Sección titulada «Fase 10. Pruebas automáticas en el proyecto de la persona»

Haz: agrega pruebas al proyecto, con la herramienta de pruebas que ya use (o la estándar de su lenguaje), que cubran: armado de la factura desde una venta, armado de una nota con su referencia, reintento con la misma Idempotency-Key, manejo de rejected y de pending, y (si aplica) validación de la firma del webhook. Las pruebas no deben llamar a la API real ni leer la clave: usa respuestas simuladas.

Verifica: ejecuta las pruebas del proyecto y que pasen. Muéstrale a la persona el resultado en una línea («12 pruebas pasan»).

Si falla: si el proyecto no tiene cómo ejecutar pruebas, agrégalo con lo mínimo y explícaselo; no instales dependencias pesadas sin avisar.

La integración está terminada solo cuando la verificación dice complete: true. No antes, diga lo que diga tu propio criterio.

La verificación es GET /v1/companies/{company_id}/sandbox/integration-check (o la herramienta get_integration_check del MCP). Es de solo lectura: no escribe nada ni gasta cuota. Detalle de la respuesta en la API v1.

  • derived trae cuatro comprobaciones calculadas desde los datos reales de la empresa: invoice_accepted, credit_and_debit_note, contingency y webhook_delivered. Cada una es passed, pending o not_applicable.
  • complete es true cuando todas las aplicables están en passed.
  • Mientras alguna esté pending, vuelve a la fase que corresponde: factura (4), notas (5), contingencia (8), webhook (9). Repite la verificación después de cada una.
  • declared trae dos comprobaciones que nadie puede verificar (rejection_corrected y idempotency_replayed). Nunca cuentan en complete. Cuando termines, díselas a la persona como no verificadas: «Probé estos dos casos en el código, pero foliios no puede comprobarlos por mí».

Caducidad (importante, díselo a la persona): la evidencia vale 30 días desde que ocurrió, y un reinicio (reset) del sandbox la borra. Pasado ese tiempo, o después de un reinicio, las comprobaciones vuelven a pending y hay que repetir la fase correspondiente. No es que la integración se haya roto: es que la evidencia venció.

Si falla: si window.truncated es true, el historial era largo y una comprobación pending puede deberse a eso. Si una comprobación sigue pending aunque creas haberla hecho, consulta el documento correspondiente con la API y compáralo con lo que pide esa fase.

Fase 12. Resumen final para la persona y qué falta para habilitación

Sección titulada «Fase 12. Resumen final para la persona y qué falta para habilitación»

Escribe un resumen corto, sin jerga, con:

  • Qué quedó hecho en su proyecto (en qué archivos y qué hace ahora cuando hay una venta).
  • Que la verificación dio complete: true (la fecha) y que vence a los 30 días o con un reinicio.
  • Los dos casos declarados como no verificados.
  • Si usa avisos (webhooks) o consulta de estado.
  • Que todo esto fue en sandbox: lo emitido es simulado y no tiene efecto fiscal. Para facturar de verdad hace falta la habilitación ante la DIAN de su empresa (certificado de firma, resolución de numeración y el set de pruebas de la DIAN); eso lo hace la persona desde su cuenta y no es parte de este manual. No avances hacia ello por tu cuenta: detente y dile a la persona que ese es el siguiente paso.

Dejar instrucciones permanentes en el proyecto

Sección titulada «Dejar instrucciones permanentes en el proyecto»

Para que cualquier agente futuro trabaje con estas reglas, copia en el proyecto de la persona la plantilla de instrucciones con el nombre que use tu herramienta.