# Trevol: documentación completa > Trevol es una plataforma de facturación y documentos electrónicos ante la DIAN (Colombia). Su API REST permite emitir facturas, notas crédito y débito, documento soporte, documentos equivalentes, nómina electrónica y eventos RADIAN, con llaves de pruebas (tvl_test_, habilitación de la DIAN) y de producción (tvl_live_), Idempotency-Key obligatoria, errores application/problem+json y webhooks firmados con Standard Webhooks. En este archivo cada operación lleva su ejemplo en cURL. --- # Documentación de la API > Emite facturas, notas, documento soporte, documentos equivalentes, nómina y eventos RADIAN ante la DIAN desde tu propio software, con la API de Trevol. Fuente: https://platform.trevol.si/docs Con la API de Trevol emites **facturas electrónicas, notas crédito y débito, documento soporte, documentos equivalentes (POS), nómina electrónica y eventos RADIAN** ante la DIAN desde tu propio software. Tú mandas los datos en JSON; Trevol valida, calcula los impuestos y los totales, numera, firma, envía a la DIAN y entrega el PDF y el correo en los documentos que los llevan. Las llaves de pruebas (`tvl_test_…`) emiten contra la **habilitación real de la DIAN**: sin validez legal y sin costo. Cuando tu integración funcione, creas una llave de producción (`tvl_live_…`). > **Los SDK se publican pronto:** Hay SDK en Node.js, Python, PHP, Java, C#, Go y Ruby, y colecciones para Postman, Insomnia y Bruno. Los SDK todavía no están publicados: mientras tanto usa cURL o la [especificación OpenAPI](/openapi.json). Los ejemplos de cada página están en cURL por ahora. ## Empieza aquí | Quiero… | Ve a | | -------------------------------------------- | ---------------------------------------- | | Emitir mi primera factura en pruebas | [Empezar](/docs/empezar) | | Crear una llave y entender los entornos | [Llaves y entornos](/docs/llaves) | | Instalar un SDK o importar una colección | [SDK y herramientas](/docs/sdks) | | Enterarme cuando la DIAN valida un documento | [Webhooks](/docs/webhooks) | | Entender qué responde la API en cada caso | [Estados y respuestas](/docs/respuestas) | | Evitar documentos duplicados al reintentar | [Idempotencia](/docs/idempotencia) | | Resolver un error | [Errores](/docs/errores) | ## Qué puedes emitir | Documento | Guía | Referencia | | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | Factura de venta, exportación y operaciones especiales | [Facturas](/docs/facturas), [Operaciones especiales](/docs/operaciones-especiales) | [Facturas](/docs/api/invoices) | | Notas crédito y débito | [Notas crédito y débito](/docs/notas) | [Notas crédito](/docs/api/credit-notes), [Notas débito](/docs/api/debit-notes) | | Documento soporte (05) y su nota de ajuste (95) | [Documento soporte](/docs/documento-soporte) | [Documento soporte](/docs/api/support-documents) | | Tiquete POS (20), otros nueve documentos equivalentes y su ajuste | [Documento equivalente](/docs/documento-equivalente) | [Documento equivalente](/docs/api/equivalent-documents) | | Nómina electrónica (102) y su ajuste (103) | [Nómina electrónica](/docs/nomina) | [Nómina](/docs/api/payrolls) | | Eventos RADIAN 030 a 034 | [Eventos RADIAN](/docs/eventos-radian) | [Eventos RADIAN](/docs/api/radian-events) | | Saldo y recargas | [Saldo](/docs/saldo) | [Saldo](/docs/api/balance) | ## Herramientas - **[La consola](https://platform.trevol.si)**: crea llaves y webhooks, mira el registro de peticiones y el consumo, y prueba la API con el Playground. - **[Referencia de la API](/docs/api)**: cada operación con sus parámetros, su cuerpo y ejemplos de código. - **[`openapi.json`](/openapi.json)**: la especificación OpenAPI 3.1 completa. - **[Para IA](/docs/ia)**: cada página en Markdown, `llms.txt` y un servidor MCP de solo lectura. - **[Registro de cambios](/docs/cambios)**: qué cambió en cada versión de la API. --- # Empezar > Crea una llave de pruebas y emite tu primera factura contra la habilitación real de la DIAN, en cURL o en siete lenguajes. Fuente: https://platform.trevol.si/docs/empezar En cinco pasos pasas de cero a una factura validada por la DIAN en el ambiente de pruebas. ## 1. Crea tu cuenta y tu empresa Crea tu cuenta y tu empresa en [app.trevol.si](https://app.trevol.si/registro) y termina su habilitación ante la DIAN (Trevol la hace por ti). Para emitir, la empresa tiene que estar **habilitada**; hasta entonces la API responde `422 company_not_enabled`. ## 2. Crea una llave de pruebas En la [consola](https://platform.trevol.si/llaves), con el entorno **Pruebas**, crea una llave. Empieza por `tvl_test_` y **se muestra una sola vez**: guárdala como un secreto en tu servidor. Más en [Llaves y entornos](/docs/llaves). Con una sesión abierta en la consola, esta documentación te muestra tu empresa, tu entorno y el prefijo de tu llave, y el botón «Crear una llave para probar» crea una de Pruebas sin salir de aquí. Guarda la llave en una variable de entorno. Los ejemplos de toda la documentación la leen de `TREVOL_API_KEY`: ```bash export TREVOL_API_KEY="tvl_test_…" ``` ## 3. Revisa que la llave funciona `GET /v1/company` devuelve la empresa de la llave y sus `capabilities`: lo que esa llave puede emitir ahora (rangos de documento soporte y POS, el estado de la nómina en su ambiente, si hay representante legal para los eventos RADIAN). Sirve para saber, antes de pedir, qué le falta a la empresa. `GET /v1/company` (`retrieveCompany`) #### cURL ```bash curl "https://api.trevol.si/v1/company" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveCompany) ## 4. Calcula sin emitir `POST /v1/invoices/preview` recibe el mismo cuerpo que la factura y devuelve los impuestos y los totales **sin numerar ni enviar nada** (no pide `Idempotency-Key`). Úsalo para comprobar tus datos. `POST /v1/invoices/preview` (`previewInvoice`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/invoices/preview" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "VENTA-2026-000123", "customer": { "document_type": "31", "document_number": "900123456", "name": "COMERCIALIZADORA ANDINA SAS", "person_type": "legal", "email": "facturacion@comercializadoraandina.example", "address": "Calle 72 # 10-34, Oficina 501", "municipality_code": "11001" }, "items": [ { "code": "LIC-ANUAL", "description": "Licencia anual de software", "quantity": "2", "unit_price": "150000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "code": "SOP-MES", "description": "Soporte técnico mensual", "quantity": "1", "unit_price": "80000", "taxes": [ { "code": "01", "rate": "19" } ] } ], "payment": { "form": "credit", "term_days": 30 }, "notes": [ "Gracias por su compra" ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=previewInvoice&ejemplo=venta) ## 5. Emite tu primera factura `POST /v1/invoices` valida, numera, firma, envía a la DIAN y responde apenas la DIAN valida. **`Idempotency-Key` es obligatoria**: los SDK la generan por ti y la repiten en los reintentos; con cURL pon un UUID nuevo por documento. `POST /v1/invoices` (`createInvoice`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/invoices" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "VENTA-2026-000123", "customer": { "document_type": "31", "document_number": "900123456", "name": "COMERCIALIZADORA ANDINA SAS", "person_type": "legal", "email": "facturacion@comercializadoraandina.example", "address": "Calle 72 # 10-34, Oficina 501", "municipality_code": "11001" }, "items": [ { "code": "LIC-ANUAL", "description": "Licencia anual de software", "quantity": "2", "unit_price": "150000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "code": "SOP-MES", "description": "Soporte técnico mensual", "quantity": "1", "unit_price": "80000", "taxes": [ { "code": "01", "rate": "19" } ] } ], "payment": { "form": "credit", "term_days": 30 }, "notes": [ "Gracias por su compra" ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createInvoice&ejemplo=venta) > **El ejemplo es una venta nacional:** El primer ejemplo de la especificación es una venta nacional en pesos (COP): un cliente persona jurídica con NIT, dos ítems con IVA del 19 % y pago a crédito a 30 días. La factura de **exportación** en dólares es el segundo ejemplo de la operación. Para consumidor final: `"customer": {"final_consumer": true}`. La respuesta trae `status: "accepted"` cuando la DIAN valida (201). Si la DIAN no contesta, recibes un `202` con `status: "processing"` y Trevol termina el documento y te avisa por webhook: **no crees otro documento para esa venta** ([Estados y respuestas](/docs/respuestas)). El PDF, el ZIP y el correo al comprador se hacen enseguida en segundo plano: `delivery.status` llega `pending`. Entérate con el evento `invoice.delivered` o consulta el documento; si pides el PDF antes, se genera al momento ([Archivos](/docs/archivos)). ## Después - Mira la petición en la consola, en [Registro](https://platform.trevol.si/registro) (con su `Request-Id`). - Recibe el resultado por [webhook](/docs/webhooks) en lugar de preguntar. - Manda siempre un `external_id` tuyo y una `Idempotency-Key` derivada de tu venta ([Idempotencia](/docs/idempotencia)). - Cuando todo funcione, crea una llave de producción (`tvl_live_…`): emite con la resolución de numeración de la empresa y cada documento validado se descuenta del [saldo](/docs/saldo). --- # Llaves y entornos > Cada llave es de una sola empresa y un solo ambiente: pruebas contra la habilitación de la DIAN (tvl_test_) o producción (tvl_live_), con permisos e IP permitidas. Fuente: https://platform.trevol.si/docs/llaves > **Cada llave es de una sola empresa y un solo ambiente:** La empresa sale de la llave, nunca del pedido. Si tienes varias empresas, cada una tiene sus llaves, y una llave nunca puede emitir ni consultar documentos de otra. ## Dos entornos | Prefijo | Entorno | Qué hace | | ------------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------- | | `tvl_test_…` | **Pruebas** (habilitación) | Emite contra la habilitación real de la DIAN. Sin validez legal y sin costo. | | `tvl_live_…` | **Producción** | Emite con la resolución de numeración de la empresa. Cada documento validado se descuenta del [saldo](/docs/saldo). | El entorno se deduce del prefijo de la llave: no hay un parámetro `environment` en el pedido. Los documentos y los webhooks de un entorno no se ven desde el otro. ## Cómo se manda La llave va en la cabecera `Authorization`: ```http Authorization: Bearer tvl_test_… ``` Sin la cabecera la API responde [`authentication_required`](/docs/errores#authentication_required); con una llave que no existe o fue revocada, [`invalid_api_key`](/docs/errores#invalid_api_key). ## Permisos e IP - **Permisos:** «Emitir y consultar» o «Solo consultar». Con una llave de solo lectura, cualquier `POST`, `PUT`, `PATCH` o `DELETE` es [`permission_denied`](/docs/errores#permission_denied). - **IP permitidas** (opcional): hasta 10 direcciones o redes; la llave solo sirve desde ellas. - Solo el **titular** de la cuenta crea o revoca llaves, y cada vez le avisamos por correo. Revocar es inmediato. Hay un tope de 10 llaves activas por empresa, sumando Pruebas y Producción. ## Cuídala - Guarda la llave como un secreto **en tu servidor**: nunca en una página web ni en una app de celular. Si tu aplicación corre en el navegador, llama a tu propio servidor y que él llame a Trevol ([Backend intermediario](/docs/backend-intermediario)). - Trevol solo guarda su huella (SHA-256): el token completo se muestra una sola vez, al crearla. Si lo pierdes, crea otra y revoca la anterior. - No la subas a un repositorio. Usa variables de entorno (`TREVOL_API_KEY`) o el gestor de secretos de tu plataforma. --- # SDK y herramientas > SDK de Trevol en Node.js, Python, PHP, Java, C#, Go y Ruby, colecciones de Postman, Insomnia y Bruno, y la especificación OpenAPI. Fuente: https://platform.trevol.si/docs/sdks > **Los SDK se publican pronto:** Los siete SDK (Node.js, Python, PHP, Java, C#, Go y Ruby) están construidos y probados, pero **todavía no están publicados**. Esta documentación anunciará los nombres de los paquetes y sus comandos de instalación cuando lo estén. Mientras tanto usa cURL o la [especificación OpenAPI](/openapi.json), y las colecciones de [Descargas](/docs/descargas). ## Qué hace un SDK de Trevol Los siete SDK se generan de la misma especificación con un generador abierto y llevan encima una capa propia, delgada y probada, que se comporta igual en todos los lenguajes: - **`Idempotency-Key` automática**: se genera una por operación y es **la misma en los reintentos**. - **Reintentos con espera creciente** en los errores que Trevol marca como reintentables (`409 request_in_progress`, `429`, `5xx`), respetando `Retry-After`. Un `202` es un resultado, no un error. - **Errores tipados** por el `code` del problema (`ValidationError`, `RateLimitError`, `ConflictError`…), con el `request_id`. - **Entorno deducido** del prefijo de la llave y `Trevol-Version` fijada. - **Verificación de webhooks** con las bibliotecas abiertas de [Standard Webhooks](https://www.standardwebhooks.com) (propia en C#). - **Paginación con iteradores** y **descarga** de PDF, XML y ZIP. La llave se lee de la variable de entorno `TREVOL_API_KEY`. ## Colecciones y especificación - **[`openapi.json`](/openapi.json)**: la especificación OpenAPI 3.1, la misma que genera esta documentación y los SDK. - **Postman, Insomnia y Bruno**: colecciones con las 109 operaciones y una variable `TREVOL_API_KEY`. Se descargan en [Descargas](/docs/descargas). ## Los ejemplos de la documentación Cada operación de esta documentación trae su ejemplo en cURL. Los ejemplos de los siete SDK se agregarán cuando se publiquen: saldrán del generador de fragmentos de los SDK, y cada fragmento se compila y se corre contra un servidor simulado desde la especificación. Cada ejemplo tiene un botón «Probar en el Playground» que abre la operación y el ejemplo en el [Playground](https://platform.trevol.si/playground) de la consola (con tu sesión; nunca envía nada por sí solo). --- # Descargas > Descarga las colecciones de Postman, Insomnia y Bruno de la API de Trevol y la especificación OpenAPI 3.1. Fuente: https://platform.trevol.si/docs/descargas Las colecciones se generan de la especificación y traen las 109 operaciones con su ejemplo, agrupadas por recurso. **Ninguna lleva una llave**: define la variable `TREVOL_API_KEY` con tu llave de pruebas. Cada petición que crea o corrige un documento manda una `Idempotency-Key` nueva por envío. | Archivo | Para | | ---------------------------------------------------------------------------------- | ------------------------------------------------------------ | | [`trevol.postman_collection.json`](/docs/descargas/trevol.postman_collection.json) | Postman (colección v2.1) | | [`trevol.insomnia.json`](/docs/descargas/trevol.insomnia.json) | Insomnia (importar datos) | | [`trevol.bruno.zip`](/docs/descargas/trevol.bruno.zip) | Bruno (descomprime y abre la carpeta como colección) | | [`openapi.json`](/openapi.json) | OpenAPI 3.1: cualquier herramienta que lea la especificación | ## Cómo usarlas 1. Importa el archivo en tu cliente (en Bruno, abre la carpeta `trevol-bruno`). 2. Crea una variable de entorno `TREVOL_API_KEY` con tu llave `tvl_test_…`. 3. Ejecuta `retrieveCompany` para comprobar que la llave funciona. La URL base es `https://api.trevol.si/v1`. --- # Convenciones > Formatos, identificadores, fechas, decimales y códigos de la API de Trevol. Fuente: https://platform.trevol.si/docs/convenciones - **Dirección:** `https://api.trevol.si/v1`. JSON en UTF-8; los campos van en inglés y los mensajes en español. - **Montos:** en los pedidos, número o texto decimal con punto (`"150000.50"`); en las respuestas, siempre texto con 2 decimales. Las cantidades admiten hasta 6 decimales. Los SDK mandan los montos como texto, sin errores de coma flotante. - **Precios sin impuestos:** `unit_price` va antes de IVA; Trevol calcula impuestos, retenciones y totales. - **Códigos de la DIAN como texto:** `"01"`, `"31"`, `"47"`. - **Identificadores con prefijo:** `inv_` factura, `cn_` nota crédito, `dn_` nota débito, `sd_` documento soporte, `sda_` su nota de ajuste, `pos_` documento equivalente (de cualquier tipo), `posa_` su nota de ajuste, `pr_` nómina, `pra_` su nota de ajuste y `rev_` evento RADIAN. - **`external_id`:** tu identificador, opcional. Es único entre facturas, notas, documentos soporte y equivalentes (y sus notas); aparte, entre nóminas y sus ajustes; y aparte, entre eventos RADIAN. El `existing_id` de un conflicto puede ser el id de cualquiera de los recursos de su grupo. - **Código de seguridad:** se llama como lo llama la DIAN: `cufe` en la factura, `cude` en las notas, el documento equivalente y los eventos, `cuds` en el documento soporte y `cune` en la nómina. - **Fechas:** ISO 8601; `issued_at` en hora de Bogotá (`-05:00`). - **Campos desconocidos:** un campo que Trevol no conoce es un error ([`validation_error`](/docs/errores#validation_error)); no se ignora en silencio. - **Cabeceras de respuesta:** `Request-Id` (`req_…`) en cada respuesta, `Trevol-Version` con la versión usada e `Idempotent-Replayed: true` cuando se repite una respuesta guardada. > **Cuando pidas ayuda:** Comparte el `Request-Id` (`req_…`). Con él ubicamos tu petición en el [registro](https://platform.trevol.si/registro) sin ver los datos de tus clientes. --- # Idempotencia > Repetir una petición con la misma Idempotency-Key devuelve la misma respuesta sin volver a emitir; cómo usar external_id y qué hacer tras un reinicio. Fuente: https://platform.trevol.si/docs/idempotencia Emitir un documento no se puede hacer «a ciegas dos veces»: duplicaría una factura. Por eso **`Idempotency-Key` es obligatoria** al crear o corregir un documento. > **Evita los duplicados tras un reinicio:** La clave automática de un SDK protege los reintentos **de una misma llamada**. Si tu proceso muere y vuelves a llamar, el SDK genera otra clave y podrías duplicar el documento. **Manda siempre un `external_id` tuyo y una `idempotencyKey` derivada de tu venta** (por ejemplo `VENTA-2026-000123`). Si la API responde `external_id_in_use` o `duplicate_document`, usa `error.problem.existing_id` para seguir con el documento que ya existe. ## Cómo funciona - Manda un UUID nuevo (o una clave derivada de tu venta, de 1 a 255 caracteres ASCII visibles) por cada documento. - Repetir la **misma clave con el mismo cuerpo** (durante 7 días) devuelve la misma respuesta, con `Idempotent-Replayed: true`, sin volver a emitir. - La misma clave con **otro cuerpo** es [`idempotency_key_reused`](/docs/errores#idempotency_key_reused): nunca te devolvemos otro documento en silencio. - Si la primera petición no terminó (402, 503, o el 202 `processing`), el reintento con la misma clave sigue con el **mismo** documento y el mismo número. En la versión 2026-10-22, el reintento sobre un documento que Trevol está resolviendo no envía nada: responde otro 202 con el estado actual (o el 201 si ya se validó). - Mientras la primera petición sigue en curso, la segunda recibe [`request_in_progress`](/docs/errores#request_in_progress) (409): consulta su estado en unos segundos. ## `external_id` Tu identificador del documento, opcional. Repetido es [`external_id_in_use`](/docs/errores#external_id_in_use) con el `existing_id` del que ya existe. Es la forma de que dos procesos distintos no emitan lo mismo. ## En los SDK Los SDK generan la `Idempotency-Key` si no la pasas y la repiten en todos los reintentos. Para controlarla, pásala tú: `POST /v1/invoices` (`createInvoice`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/invoices" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "VENTA-2026-000123", "customer": { "document_type": "31", "document_number": "900123456", "name": "COMERCIALIZADORA ANDINA SAS", "person_type": "legal", "email": "facturacion@comercializadoraandina.example", "address": "Calle 72 # 10-34, Oficina 501", "municipality_code": "11001" }, "items": [ { "code": "LIC-ANUAL", "description": "Licencia anual de software", "quantity": "2", "unit_price": "150000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "code": "SOP-MES", "description": "Soporte técnico mensual", "quantity": "1", "unit_price": "80000", "taxes": [ { "code": "01", "rate": "19" } ] } ], "payment": { "form": "credit", "term_days": 30 }, "notes": [ "Gracias por su compra" ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createInvoice&ejemplo=venta) En los fragmentos, el parámetro `idempotencyKey` (o su equivalente en cada lenguaje) viene comentado: descoméntalo con una clave derivada de tu venta. Las operaciones que no la declaran (por ejemplo `POST /v1/webhook_endpoints` o `…/send`) no la reciben. --- # Estados y respuestas > Qué responde la API cuando la DIAN valida, rechaza o no contesta (201, 202, 402, 422, 503) y cómo continuar sin duplicar documentos. Fuente: https://platform.trevol.si/docs/respuestas Trevol valida, numera, firma, envía a la DIAN y **responde apenas la DIAN valida** (la meta es 3 segundos o menos; en la prueba del 23 de octubre de 2026, en habilitación, la mediana fue 1,9 s). ## Qué responde | HTTP | Qué pasó | | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 201 | La DIAN validó el documento (`status: accepted`). | | 422 [`dian_rejected`](/docs/errores#dian_rejected) | La DIAN lo rechazó; `document` trae el documento y sus reglas traducidas. | | 422 [`validation_error`](/docs/errores#validation_error) | Trevol lo rechazó antes de numerar: no se gastó nada. | | 402 [`insufficient_balance`](/docs/errores#insufficient_balance) | Producción sin saldo: queda `pending` con su número. | | 202 `processing` (versión 2026-10-22) | La DIAN no contestó. **Trevol termina el documento** (le pregunta por el CUFE y, si hace falta, lo reenvía) y te avisa por webhook con `….accepted`, `….rejected` o `….error`. **No crees otro documento para esa venta.** Trae `Location` y `Retry-After: 30`. | | 202 `sending` (versión 2026-10-22) | Otra petición, o el fondo, lo está enviando ahora. Igual que el anterior. | | 202 `validating` (las dos versiones) | La propia DIAN responde después: el set de pruebas de habilitación de la nómina o del documento equivalente todavía valida el documento, o un evento RADIAN quedó en «en proceso de validación» (98). No es un error y **no se reenvía**: Trevol solo consulta y te avisa por webhook. | | 503 [`dian_unavailable`](/docs/errores#dian_unavailable) (versión 2026-10-21) | La DIAN no contestó: reintenta con la misma `Idempotency-Key`. Trevol además lo verifica en segundo plano (solo consulta; no reenvía). | Todos los errores están en el [catálogo de errores](/docs/errores). ## Un 202 no es un error ```http respuesta HTTP/1.1 202 Accepted Location: /v1/invoices/inv_08cd6774e31e401c9fa0fbf92b3fbdc0 Retry-After: 30 Trevol-Version: 2026-10-22 { "id": "inv_08cd6774e31e401c9fa0fbf92b3fbdc0", "object": "invoice", "status": "processing", "number": "SETP994307052", "cufe": null, "dian": { "status_code": null, "rules": [] } } ``` Un 202 ya tiene su `id` y su número. Para saber cómo terminó, **espera el webhook** o consulta el documento: `GET /v1/invoices/{id}` (`retrieveInvoice`) #### cURL ```bash curl "https://api.trevol.si/v1/invoices/inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveInvoice) Los SDK no reintentan un 202 ni lo convierten en excepción: lo devuelven como cualquier respuesta, con su `status`. ## Los estados de un documento | `status` | Significado | | ------------- | ---------------------------------------------------------------------------------------- | | `pending` | Numerado, sin enviar (por ejemplo, sin saldo). | | `sending` | Se está enviando a la DIAN. | | `processing` | Solo en 2026-10-22: la DIAN no confirmó y Trevol lo resuelve. | | `validating` | Nómina, documento equivalente y eventos: la DIAN responde después, en las dos versiones. | | `accepted` | Validado por la DIAN. | | `rejected` | La DIAN lo rechazó; se corrige con `PUT` y el mismo número. | | `error` | No se pudo enviar. | | `contingency` | Documento equivalente en contingencia. | ## Rechazos y correcciones - **Rechazado por la DIAN:** corrige con `PUT` (cuerpo completo, nueva `Idempotency-Key`). Sale con el mismo número. Un rechazo no bloquea los demás envíos. Cada regla trae su explicación en español: [Reglas de la DIAN](/docs/errores/reglas-dian). - **Pendiente o en error:** `POST …/send` lo vuelve a enviar (por ejemplo, después de recargar saldo). - **Validado:** ya no se cambia; se corrige con una [nota crédito o débito](/docs/notas). --- # Reintentos > Qué errores se reintentan, con qué espera y cómo lo hacen los SDK de Trevol; cuándo reintentar con cURL. Fuente: https://platform.trevol.si/docs/reintentos Cada error de la API trae `retryable`: si es `true`, repetir la petición puede funcionar; si es `false`, corrige primero. Los SDK reintentan solos lo reintentable y **nunca** reintentan lo que podría duplicar un efecto. ## Qué se reintenta Con los SDK (hasta 2 reintentos por defecto, `maxRetries`): - un error de red o un tiempo agotado, sin respuesta; - los estados `429`, `500`, `502`, `503` y `504`; - un `409` con `code` `request_in_progress` o `previous_event_pending`; - cualquier problema con `retryable: true`, salvo `402`. **Solo cuando la operación es segura de repetir:** los `GET`, los `preview`, los `PATCH` y todo `POST`/`PUT` con `Idempotency-Key` (la misma clave en cada intento). Los `POST` sin clave —crear o rotar un endpoint de webhook, `test`, `resend`, `send`— y los `DELETE` solo se reintentan con `429`. ## Qué no se reintenta Nunca se reintentan las respuestas `2xx` (incluidos todos los `202`), ni `400`, `401`, `402`, `403`, `404`, `422`, ni los `409` finales: [`invalid_state`](/docs/errores#invalid_state), [`external_id_in_use`](/docs/errores#external_id_in_use), [`duplicate_document`](/docs/errores#duplicate_document), [`file_unavailable`](/docs/errores#file_unavailable) y [`webhook_limit_reached`](/docs/errores#webhook_limit_reached). ## Cuánto se espera Espera creciente con azar: `min(0,5 · 2^(intento−1), 8)` segundos por un factor entre 0,75 y 1. Si la respuesta trae `Retry-After`, se espera lo que sea mayor entre ese valor y la espera calculada. Si `Retry-After` pide más que `maxRetryAfter` (60 s), el SDK no espera: lanza el error con `retryAfter` para que decidas tú. ## Con cURL Reintenta tú, con espera creciente, los `429`, `5xx` y los errores con `retryable: true`, y **manda la misma `Idempotency-Key`** en cada intento. Respeta `Retry-After`. No reintentes un `202`: espera el [webhook](/docs/webhooks) o consulta el documento. --- # Límites > Límites de uso de la API de Trevol por llave y por empresa, y qué hacer cuando se superan. Fuente: https://platform.trevol.si/docs/limites | Límite | Valor | | ----------------------------------- | ------------------------------------------ | | Envíos por llave | 120 por minuto y 20.000 al día | | Consultas por llave | 600 por minuto | | Envíos por empresa | 50.000 al día, entre todas sus llaves | | Aceptación tácita (consulta previa) | 30 por minuto por llave | | Webhooks | 5 por empresa y ambiente | | Llaves activas | 10 por empresa, entre Pruebas y Producción | Al pasarse de un límite, la API responde [`rate_limited`](/docs/errores#rate_limited) (429) con `Retry-After` en segundos. Los SDK lo respetan: si pide esperar más de 60 s lanzan `RateLimitError` con `retryAfter`. La empresa y el ambiente salen de la llave; los límites no se comparten entre Pruebas y Producción. Mira tu consumo en la [consola](https://platform.trevol.si/consumo). --- # Paginación > Las listas de la API de Trevol usan cursores (starting_after); los SDK ofrecen iteradores que recorren todas las páginas. Fuente: https://platform.trevol.si/docs/paginacion Las listas devuelven un sobre `{object: "list", data, has_more, next_cursor}`. Pide la página siguiente con `starting_after=`; `limit` cambia el tamaño de la página. `GET /v1/invoices` (`listInvoices`) #### cURL ```bash curl "https://api.trevol.si/v1/invoices?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listInvoices) - Los listados de documentos filtran por `status`, `external_id`, `created_from` y `created_to`; los de nómina, por `employee_code` y `period_end_from`/`period_end_to` (nunca por la cédula). - Si una página con `has_more` verdadero no trae `next_cursor`, usa el `id` del último elemento. - `GET /v1/events` guarda los eventos 30 días y `GET /v1/requests`, el registro de peticiones, otros 30. ## Iteradores en los SDK Cada SDK trae un iterador perezoso con su idioma: `paginate` devuelve un `AsyncIterable` en Node, un generador en Python y PHP, un `Iterable` en Java, un `IAsyncEnumerable` en C#, un `iter.Seq2` en Go y un `Enumerator::Lazy` en Ruby. Pides la primera página, y el iterador sigue solo con `next_cursor` mientras `has_more`. En el README de cada SDK hay un ejemplo completo. --- # Versiones > Las versiones de la API de Trevol (2026-10-21 y 2026-10-22), cómo se elige la de una llave y qué cambia entre ellas. Fuente: https://platform.trevol.si/docs/versiones La versión de la API se elige con la cabecera `Trevol-Version`. Si no viene, vale la de la llave: las llaves anteriores quedaron en `2026-10-21` y las nuevas nacen en `2026-10-22`. **El encabezado manda sobre la versión de la llave.** Cada respuesta dice la versión usada en `Trevol-Version`, y `GET /v1/company` dice la de la llave (`key.api_version`). ## 2026-10-22 La versión de las llaves nuevas. Con la DIAN sin contestar responde el `202 processing`: Trevol termina el documento y te avisa por webhook. ## 2026-10-21 La versión de las llaves anteriores. Con la DIAN sin contestar responde el `503 dian_unavailable` de siempre. Para probar la nueva con una llave vieja, manda `Trevol-Version: 2026-10-22`. ## Qué no depende de la versión El documento soporte, el documento equivalente, la nómina y los eventos RADIAN no traen versión nueva: valen las dos, con el mismo significado que en las facturas. Webhooks, eventos, el registro de peticiones y `delivery.status` valen para las dos versiones. Los cambios que no rompen nada (campos nuevos, `operationId`, ejemplos) no estrenan versión: están en el [registro de cambios](/docs/cambios). > **Los SDK fijan la versión:** Cada SDK manda `Trevol-Version` en todas sus peticiones (la versión de su especificación, hoy `2026-10-22`). Una llave que estaba fijada en `2026-10-21` y empieza a usar un SDK pasa a `2026-10-22`: recibe un `202 processing` donde antes recibía un `503`. Si todavía no quieres cambiar, crea el cliente con `apiVersion: '2026-10-21'` (en cada lenguaje, la opción equivalente). --- # Archivos > Descargar el PDF, el XML, el ZIP y la respuesta de la DIAN de un documento, y entender cuándo están listos. Fuente: https://platform.trevol.si/docs/archivos Cada documento validado tiene sus archivos, que se piden con un `GET` y llegan en binario, listos para guardar: | Ruta | Archivo | | ---------------------- | ------------------------------- | | `…/{id}/pdf` | La representación gráfica (PDF) | | `…/{id}/xml` | El XML firmado | | `…/{id}/zip` | El ZIP con el XML y el PDF | | `…/{id}/dian_response` | La respuesta de la DIAN | `GET /v1/invoices/{id}/pdf` (`downloadInvoicePdf`) #### cURL ```bash curl "https://api.trevol.si/v1/invoices/inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/pdf" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o invoice_pdf.pdf ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadInvoicePdf) No todos los documentos tienen todos los archivos: el documento soporte, la nómina y los eventos RADIAN no llevan PDF ni correo (`xml`, `zip` y `dian_response`; los eventos solo `xml`). ## Cuándo están listos El PDF, el ZIP y el correo al comprador se hacen **después** del `201`, en segundo plano. `delivery.status` es: - `null` si el documento no está `accepted`; - `pending` mientras Trevol genera el PDF y el ZIP y manda el correo; - `completed` cuando hay PDF, ZIP y el correo quedó en un estado final (`sent`, `skipped`, `failed`, `bounced` o `complained`). Un `failed` que Trevol todavía va a reintentar cuenta como `pending`. Entérate con el evento `….delivered` ([Webhooks](/docs/webhooks)) o consulta el documento. Si pides el PDF o el ZIP antes de que estén, se generan al momento; si el documento todavía no está validado, es [`file_unavailable`](/docs/errores#file_unavailable) (409), y si no se pudieron preparar ahora, un 503 reintentable. ## En los SDK Los métodos `download…` devuelven el binario propio de cada lenguaje (`Blob`, `bytes`, `File`, `Stream`, `*os.File`…). El README de cada SDK muestra cómo guardarlo y cómo leer `Request-Id`. --- # Facturas > Emitir una factura electrónica por la API de Trevol, corregirla si la DIAN la rechaza, consultarla y descargar sus archivos. Fuente: https://platform.trevol.si/docs/facturas La factura electrónica de venta se pide con `POST /v1/invoices`. Trevol valida los datos, calcula los impuestos y los totales, numera con la resolución de la empresa, firma, envía a la DIAN y **responde apenas la DIAN valida**. ## Emitir una factura `POST /v1/invoices` (`createInvoice`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/invoices" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "VENTA-2026-000123", "customer": { "document_type": "31", "document_number": "900123456", "name": "COMERCIALIZADORA ANDINA SAS", "person_type": "legal", "email": "facturacion@comercializadoraandina.example", "address": "Calle 72 # 10-34, Oficina 501", "municipality_code": "11001" }, "items": [ { "code": "LIC-ANUAL", "description": "Licencia anual de software", "quantity": "2", "unit_price": "150000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "code": "SOP-MES", "description": "Soporte técnico mensual", "quantity": "1", "unit_price": "80000", "taxes": [ { "code": "01", "rate": "19" } ] } ], "payment": { "form": "credit", "term_days": 30 }, "notes": [ "Gracias por su compra" ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createInvoice&ejemplo=venta) - **Consumidor final:** `"customer": {"final_consumer": true}`. - **Calcular sin emitir:** `POST /v1/invoices/preview` con el mismo cuerpo (no numera ni pide `Idempotency-Key`). - **Precios sin impuestos:** `unit_price` va antes de IVA; Trevol calcula impuestos, retenciones y totales. - La factura cubre todo lo del Anexo técnico 1.9: impuestos porcentuales y nominales, descuentos y recargos, retenciones, muestras gratis, anticipos y redondeo, y también la exportación, la moneda extranjera, el AIU, los mandatos, el transporte de carga, los notarios, las divisas y el sector salud ([Operaciones especiales](/docs/operaciones-especiales)). Quedan fuera la contingencia 03 y 04 de la factura y los eventos RADIAN 035 a 051. `POST /v1/invoices/preview` (`previewInvoice`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/invoices/preview" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "VENTA-2026-000123", "customer": { "document_type": "31", "document_number": "900123456", "name": "COMERCIALIZADORA ANDINA SAS", "person_type": "legal", "email": "facturacion@comercializadoraandina.example", "address": "Calle 72 # 10-34, Oficina 501", "municipality_code": "11001" }, "items": [ { "code": "LIC-ANUAL", "description": "Licencia anual de software", "quantity": "2", "unit_price": "150000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "code": "SOP-MES", "description": "Soporte técnico mensual", "quantity": "1", "unit_price": "80000", "taxes": [ { "code": "01", "rate": "19" } ] } ], "payment": { "form": "credit", "term_days": 30 }, "notes": [ "Gracias por su compra" ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=previewInvoice&ejemplo=venta) > **Qué responde:** `201` cuando la DIAN valida, `422 dian_rejected` con `document` cuando la rechaza, `422 validation_error` si Trevol la rechaza antes de numerar (no se gasta ningún número), `402` sin saldo, y con la DIAN sin contestar un `202 processing` (versión 2026-10-22) o un `503 dian_unavailable` (2026-10-21). Todo está en [Estados y respuestas](/docs/respuestas). `Idempotency-Key` es obligatoria ([Idempotencia](/docs/idempotencia)) y `external_id` es opcional. ## Rechazos y reintentos - **Rechazado por la DIAN:** corrige con `PUT /v1/invoices/{id}` (cuerpo completo, nueva `Idempotency-Key`). Sale con el mismo número. Un rechazo no bloquea los demás envíos. `PUT /v1/invoices/{id}` (`correctInvoice`) #### cURL ```bash curl -X PUT "https://api.trevol.si/v1/invoices/inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "VENTA-2026-000123", "customer": { "document_type": "31", "document_number": "900123456", "name": "COMERCIALIZADORA ANDINA SAS", "person_type": "legal", "email": "facturacion@comercializadoraandina.example", "address": "Calle 72 # 10-34, Oficina 501", "municipality_code": "11001" }, "items": [ { "code": "LIC-ANUAL", "description": "Licencia anual de software", "quantity": "2", "unit_price": "150000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "code": "SOP-MES", "description": "Soporte técnico mensual", "quantity": "1", "unit_price": "80000", "taxes": [ { "code": "01", "rate": "19" } ] } ], "payment": { "form": "credit", "term_days": 30 }, "notes": [ "Gracias por su compra" ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=correctInvoice&ejemplo=venta) - **Pendiente o en error:** `POST /v1/invoices/{id}/send` lo vuelve a enviar (por ejemplo, después de recargar saldo). `POST /v1/invoices/{id}/send` (`sendInvoice`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/invoices/inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/send" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=sendInvoice) - **Validado:** ya no se cambia; se corrige con una [nota crédito o débito](/docs/notas). ## Consultar y listar `GET /v1/invoices/{id}` (`retrieveInvoice`) #### cURL ```bash curl "https://api.trevol.si/v1/invoices/inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveInvoice) `GET /v1/invoices` lista por cursor (`limit`, `starting_after`, y los filtros `status`, `external_id`, `created_from` y `created_to`); ver [Paginación](/docs/paginacion). `GET /v1/dian/acquirer?document_type=31&document_number=…` devuelve lo que la DIAN tiene del adquiriente (nombre y correo). ## Archivos `GET /v1/invoices/{id}/pdf`, `/xml`, `/zip` y `/dian_response` entregan los archivos en binario ([Archivos](/docs/archivos)). ## Más documentos Todo lo que Trevol emite se pide igual: `POST` con `Idempotency-Key`, los mismos estados, errores, webhooks y archivos. Cada familia tiene su página: [notas crédito y débito](/docs/notas), [documento soporte](/docs/documento-soporte), [documento equivalente](/docs/documento-equivalente), [nómina electrónica](/docs/nomina) y [eventos RADIAN](/docs/eventos-radian). --- # Notas crédito y débito > Corregir una factura validada con una nota crédito (1 a 6) o débito (1 a 4) por la API de Trevol. Fuente: https://platform.trevol.si/docs/notas Una factura validada por la DIAN ya no se cambia. Se corrige con una **nota crédito** (devoluciones, anulaciones, descuentos) o una **nota débito** (intereses, gastos, cambio de valor). Se piden igual que la factura: `POST` con `Idempotency-Key`. ## Nota crédito `POST /v1/credit_notes` (`createCreditNote`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/credit_notes" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "DEV-2026-000045", "concept": "1", "description": "Devolución de una unidad por empaque dañado", "invoice": { "id": "inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Café especial 500 g", "code": "CAF-500", "unit": "94", "quantity": "1", "unit_price": "32000", "taxes": [ { "code": "01", "rate": "19" } ] } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createCreditNote&ejemplo=devolucion) - Con una factura de Trevol, la nota usa su cliente si no envías `customer`. - Trevol revisa **antes de numerar** que la nota crédito no pase del saldo de la factura. - Conceptos de la DIAN: nota crédito 1 a 6; nota débito 1 a 4. - `invoice` identifica la factura por `id` (`inv_…`) o por `number` + `cufe` + `issue_date` (para facturas emitidas fuera de Trevol). - Las notas no admiten moneda extranjera, exportación ni tipos de operación. ## Nota débito `POST /v1/debit_notes` (`createDebitNote`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/debit_notes" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "INT-2026-000012", "concept": "1", "description": "Intereses de mora de la factura de septiembre", "invoice": { "id": "inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Intereses de mora", "unit": "94", "quantity": "1", "unit_price": "45000" } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createDebitNote&ejemplo=intereses) ## Rechazos, reintentos y consultas Una nota rechazada se corrige con `PUT` y el mismo número; una pendiente o en error se reenvía con `POST …/send`: `PUT /v1/credit_notes/{id}` (`correctCreditNote`) #### cURL ```bash curl -X PUT "https://api.trevol.si/v1/credit_notes/cn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "DEV-2026-000045", "concept": "1", "description": "Devolución de una unidad por empaque dañado", "invoice": { "id": "inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Café especial 500 g", "code": "CAF-500", "unit": "94", "quantity": "1", "unit_price": "32000", "taxes": [ { "code": "01", "rate": "19" } ] } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=correctCreditNote&ejemplo=devolucion) Consulta y archivos son los de la factura: [Facturas](/docs/facturas) y [Archivos](/docs/archivos). Las referencias completas están en [Notas crédito](/docs/api/credit-notes) y [Notas débito](/docs/api/debit-notes). --- # Documento soporte > Documento soporte (05) en adquisiciones a no obligados a facturar y su nota de ajuste (95) por la API de Trevol. Fuente: https://platform.trevol.si/docs/documento-soporte Genera el documento soporte (05) de una compra a quien no está obligado a facturar —una persona natural, un campesino, un proveedor del exterior— y su nota de ajuste (95). Trevol valida, numera con tu rango, firma y envía a la DIAN. ## Antes de emitir La empresa necesita, en la app de Trevol: - la habilitación ante la DIAN y su **software** asociado al modo de documento soporte (si falta: `422 company_not_enabled`, `reason: "missing_software"`); - un **rango de documento soporte** registrado, vigente y con números libres (si no: `422 numbering_unavailable`, `reason: "no_resolution"`). `GET /v1/company` lo lista en `capabilities.support_documents.numbering`, con el `next` de cada uno; - el certificado de firma. La nota de ajuste no usa rango: tiene su consecutivo propio. Todo esto se comprueba **antes de numerar**: un pedido que no puede salir no gasta número. ## Emitir un documento soporte `POST /v1/support_documents` (`createSupportDocument`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/support_documents" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "OC-991", "seller": { "resident": true, "person_type": "natural", "document_type": "31", "document_number": "1020304050", "name": "PEREZ GOMEZ ANA MARIA", "address": "Vereda El Silencio, finca La Esperanza", "municipality_code": "25053", "postal_code": "252007", "tax_responsibilities": [ "R-99-PN" ] }, "items": [ { "description": "Leche cruda", "unit": "LTR", "quantity": "120", "unit_price": "1800" } ], "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "notes": [ "Compra de la semana 42 de octubre" ] }' ``` #### Respuesta 201 ```json { "id": "sd_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "support_document", "environment": "test", "status": "accepted", "number": "DS994001", "prefix": "DS", "external_id": "OC-991", "origin": "api", "cuds": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe-hab.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "seller": { "resident": true, "person_type": "natural", "document_type": "31", "document_number": "1020304050", "name": "PEREZ GOMEZ ANA MARIA", "address": "Vereda El Silencio, finca La Esperanza", "municipality_code": "25053", "postal_code": "252007", "tax_responsibilities": [ "R-99-PN" ] }, "items": [ { "description": "Leche cruda", "unit": "LTR", "quantity": "120", "unit_price": "1800" } ], "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "transmission": "per_operation", "adjustment_note": null, "totals": { "subtotal": "216000.00", "discounts": "0.00", "line_extension": "216000.00", "tax_exclusive": "0.00", "taxes": [], "allowances": "0.00", "charges": "0.00", "rounding": "0.00", "total": "216000.00", "withholdings": [], "prepayments": "0.00", "net_payable": null, "items": [ { "gross": "216000.00", "discount": "0.00", "line_extension": "216000.00" } ] }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "delivery": { "status": null, "pdf": false, "zip": false, "xml": true, "email": null }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createSupportDocument&ejemplo=compra) Se genera, no se expide: no hay PDF, ZIP ni correo. Se descargan `…/xml` y `…/dian_response` ([Archivos](/docs/archivos)). ## La nota de ajuste `POST /v1/support_document_adjustments` (`createSupportDocumentAdjustment`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/support_document_adjustments" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "OC-991-ANULA", "concept": "2", "description": "Anulación: la compra de leche se registró dos veces en el ERP", "support_document": { "id": "sd_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Leche cruda", "unit": "LTR", "quantity": "120", "unit_price": "1800" } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createSupportDocumentAdjustment&ejemplo=anulacion) ## Lo que hay que saber - Las respuestas son las de [toda la API](/docs/respuestas): `201`, `422 dian_rejected` con `document`, `422 validation_error` antes de numerar, `402` sin saldo, y `202`/`503` con la DIAN sin contestar. `Idempotency-Key` es obligatoria. - El `external_id` es único entre facturas, notas, documentos soporte y equivalentes (y sus notas). - Cuenta como facturación en el cobro: lo que se cobra al validarse un documento de producción. - Los avisos (webhooks) salen por cualquier camino (la API o la app); el `data` es un resumen: nunca el vendedor ni las líneas. Tipos: `support_document.*` y `support_document_adjustment.*` ([eventos](/docs/webhooks/eventos)). Referencia: [Documento soporte](/docs/api/support-documents) y [su nota de ajuste](/docs/api/support-document-adjustments). --- # Documento equivalente > Tiquete POS (20) y los otros nueve documentos equivalentes electrónicos, con su nota de ajuste, por la API de Trevol. Fuente: https://platform.trevol.si/docs/documento-equivalente Emite el tiquete POS (20) y los otros nueve documentos equivalentes electrónicos de la DIAN —boleta de cine, espectáculos, juegos, transporte, peajes, extracto, tiquete aéreo, bolsa de valores y servicios públicos— y su nota de ajuste crédito (94) o débito (93). ## Los diez tipos | `document_type` | Documento | Información propia (`extension`) | Numeración | | ------------------ | ---------------------------------------------------- | -------------------------------------------------- | -------------------------- | | `20` (por defecto) | Tiquete POS | No lleva; lleva `pos.register` (la caja) | Tu resolución POS | | `25` | Boleta de cine | `cinema` | Consecutivo propio (`DEC`) | | `27` | Espectáculos públicos | `show` | `DEEP` | | `30` | Juegos localizados y no localizados (se acepta `32`) | `games` (sin `customer`: el adquirente es la DIAN) | `DEJL` | | `35` | Transporte de pasajeros | `transport` | `DETT` | | `40` | Peajes | No lleva | `DEP` | | `45` | Extracto | No lleva | `EXT` | | `50` | Tiquete aéreo | `air` (obligatoria) | `DETA` | | `55` | Bolsa de valores | `stock_exchange` | `DEBV` | | `60` | Servicios públicos | `public_services` | `SPD` | `extension` lleva UNA sola clave, la de su tipo (`{"cinema": {…}}`); otra clave es `validation_error`. El prefijo de los consecutivos propios se puede cambiar con `prefix`. ## Antes de emitir - La empresa habilitada ante la DIAN, con su **software** del modo de documentos equivalentes (`reason: "missing_software"` si falta). - Para el **POS**: una resolución POS registrada en la app, vigente y con números libres (`numbering_unavailable`, `reason: "no_resolution"`). Si la empresa tiene varias cajas o establecimientos, elige la resolución de la caja con `numbering: {"prefix": "POS2"}`; `GET /v1/company` → `capabilities.equivalent_documents.pos_numbering` lista los prefijos. Los demás tipos usan su consecutivo propio. - Se comprueba **antes de numerar**; el documento se arma en seco para que lo que el XML no admita salga como `validation_error` sin gastar el número. ## Emitir un tiquete POS `POST /v1/equivalent_documents` (`createEquivalentDocument`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/equivalent_documents" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "TIQ-1", "customer": { "final_consumer": true }, "pos": { "register": { "plate": "CJ-01", "location": "Local 1, Jamundí", "cashier": "PEREZ ANA", "type": "Caja principal" } }, "numbering": { "prefix": "POS2" }, "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "items": [ { "description": "Gaseosa 400 ml", "code": "GAS-1", "unit": "94", "quantity": "2", "unit_price": "3000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "description": "Almuerzo", "unit": "94", "quantity": "1", "unit_price": "18000" } ] }' ``` #### Respuesta 201 ```json { "id": "pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "equivalent_document", "environment": "test", "status": "accepted", "number": "POS2994001", "prefix": "POS2", "external_id": "TIQ-1", "origin": "api", "cude": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe-hab.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "customer": { "final_consumer": true }, "items": [ { "description": "Gaseosa 400 ml", "code": "GAS-1", "unit": "94", "quantity": "2", "unit_price": "3000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "description": "Almuerzo", "unit": "94", "quantity": "1", "unit_price": "18000" } ], "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "document_type": "20", "operation_type": "22", "pos": { "register": { "plate": "CJ-01", "location": "Local 1, Jamundí", "cashier": "PEREZ ANA", "type": "Caja principal" } }, "contingency": null, "totals": { "subtotal": "24000.00", "discounts": "0.00", "line_extension": "24000.00", "tax_exclusive": "6000.00", "taxes": [ { "code": "01", "name": "IVA", "rate": "19.00", "base": "6000.00", "amount": "1140.00" } ], "allowances": "0.00", "charges": "0.00", "rounding": "0.00", "total": "25140.00", "withholdings": [], "prepayments": "0.00", "net_payable": null, "items": [ { "gross": "6000.00", "discount": "0.00", "line_extension": "6000.00" }, { "gross": "18000.00", "discount": "0.00", "line_extension": "18000.00" } ] }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "delivery": { "status": "pending", "pdf": false, "zip": false, "xml": true, "email": { "status": "pending", "to": null, "detail": null } }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createEquivalentDocument&ejemplo=pos) En el POS, un comprador sin identificar es `"customer": {"final_consumer": true}`; la respuesta lo muestra igual. ## La nota de ajuste `POST /v1/equivalent_document_adjustments` (`createEquivalentDocumentAdjustment`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/equivalent_document_adjustments" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "TIQ-1-DEV", "note_type": "credit", "concept": "1", "equivalent_document": { "id": "pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Devolución gaseosa", "unit": "94", "quantity": "1", "unit_price": "3000", "taxes": [ { "code": "01", "rate": "19" } ] } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createEquivalentDocumentAdjustment&ejemplo=devolucion) ## Contingencia, set de pruebas y entrega - **Entrega.** PDF, ZIP y correo como la factura: se hacen después del `201` (espera `equivalent_document.delivered` o consulta `delivery.status`). - **Contingencia.** La 07 (transcribes un documento físico) se pide con `contingency`. La 08 (la DIAN no contestó) la decide Trevol: la respuesta es `202` con `status: "contingency"`, Trevol reenvía y, en el POS, manda el respaldo POS 20; `contingency.backup` lo muestra. - **Set de pruebas (habilitación).** Es automático, por tipo: no mandas ningún `test_set_id`. Mientras la DIAN valida el set, la respuesta es `202` con `status: "validating"` **en las dos versiones**: no es un error, no vuelvas a emitir; Trevol solo consulta, nunca lo manda otra vez. `PUT` en ese estado es `409 invalid_state`. - El `external_id` es único entre facturas, notas, documentos soporte y equivalentes (y sus notas). - Cuenta como facturación en el cobro. Referencia: [Documento equivalente](/docs/api/equivalent-documents) y [su nota de ajuste](/docs/api/equivalent-document-adjustments). --- # Nómina electrónica > Nómina electrónica (102) y su nota de ajuste (103, Reemplazar y Eliminar) por la API de Trevol. Fuente: https://platform.trevol.si/docs/nomina Emite el documento soporte de pago de nómina electrónica (102) de cada trabajador y, si hay que cambiarlo, su nota de ajuste (103): Reemplazar o Eliminar. Trevol calcula los totales, numera, firma y envía a la DIAN. ## Antes de emitir - **La nómina de la empresa tiene que estar habilitada** ante la DIAN (en la app: Nómina → Configurar habilitación). Con una llave de producción (`tvl_live_…`) debe estar `habilitada`; con una de pruebas basta con el set de pruebas registrado. `GET /v1/company` → `capabilities.payroll.status` dice `enabled`, `testing`, `not_registered` o `not_enabled` para el ambiente de tu llave. Si no, `422 company_not_enabled` con `reason: "payroll_not_enabled"`, antes de numerar. - Los prefijos de la nómina (`capabilities.payroll.prefixes`) se cambian en la app. ## Emitir una nómina `POST /v1/payrolls` (`createPayroll`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/payrolls" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "NOM-2026-09-001", "period": { "hire_date": "2024-03-01", "start": "2026-09-01", "end": "2026-09-30" }, "pay_period": "5", "notes": [ "Nómina de septiembre de 2026" ], "employee": { "type": "01", "subtype": "00", "high_risk_pension": false, "document_type": "13", "document_number": "1000000101", "first_surname": "PRUEBA", "second_surname": "NOMINA01", "first_name": "TRABAJADOR", "other_names": "DE PRUEBA", "workplace": { "country": "CO", "municipality_code": "76001", "address": "Calle 1 # 1-1" }, "integral_salary": false, "contract_type": "2", "salary": "1423500", "code": "TRAB-01" }, "payment": { "form": "1", "method": "42", "bank": "Banco de Pruebas", "account_type": "Ahorros", "account_number": "0001234501" }, "payment_dates": [ "2026-09-30" ], "earnings": { "basic": { "days": 30, "amount": "1423500" }, "transport": [ { "allowance": "200000" } ] }, "deductions": { "health": { "percent": "4", "amount": "56940" }, "pension": { "percent": "4", "amount": "56940" } } }' ``` #### Respuesta 201 ```json { "id": "pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "payroll", "environment": "live", "status": "accepted", "number": "NE15", "prefix": "NE", "external_id": "NOM-2026-09-001", "origin": "api", "cune": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "submission": "sync", "period": { "hire_date": "2024-03-01", "start": "2026-09-01", "end": "2026-09-30" }, "pay_period": "5", "currency": "COP", "notes": [ "Nómina de septiembre de 2026" ], "employee": { "type": "01", "subtype": "00", "high_risk_pension": false, "document_type": "13", "document_number": "1000000101", "first_surname": "PRUEBA", "second_surname": "NOMINA01", "first_name": "TRABAJADOR", "other_names": "DE PRUEBA", "workplace": { "country": "CO", "municipality_code": "76001", "address": "Calle 1 # 1-1" }, "integral_salary": false, "contract_type": "2", "salary": "1423500", "code": "TRAB-01" }, "payment": { "form": "1", "method": "42", "bank": "Banco de Pruebas", "account_type": "Ahorros", "account_number": "0001234501" }, "payment_dates": [ "2026-09-30" ], "earnings": { "basic": { "days": 30, "amount": "1423500" }, "transport": [ { "allowance": "200000" } ] }, "deductions": { "health": { "percent": "4", "amount": "56940" }, "pension": { "percent": "4", "amount": "56940" } }, "totals": { "earnings": "1623500.00", "deductions": "113880.00", "net": "1509620.00" }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "files": { "xml": true, "zip": true, "dian_response": true }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createPayroll&ejemplo=mensual) ## Reemplazar o eliminar: la nota de ajuste Reemplazar: la nómina completa corregida, más `type` y `predecessor`. Eliminar: solo el predecesor y las notas. `POST /v1/payroll_adjustments` (`createPayrollAdjustment`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/payroll_adjustments" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "NOM-2026-09-001-R", "period": { "hire_date": "2024-03-01", "start": "2026-09-01", "end": "2026-09-30" }, "pay_period": "5", "notes": [ "Nómina de septiembre de 2026" ], "employee": { "type": "01", "subtype": "00", "high_risk_pension": false, "document_type": "13", "document_number": "1000000101", "first_surname": "PRUEBA", "second_surname": "NOMINA01", "first_name": "TRABAJADOR", "other_names": "DE PRUEBA", "workplace": { "country": "CO", "municipality_code": "76001", "address": "Calle 1 # 1-1" }, "integral_salary": false, "contract_type": "2", "salary": "1423500", "code": "TRAB-01" }, "payment": { "form": "1", "method": "42", "bank": "Banco de Pruebas", "account_type": "Ahorros", "account_number": "0001234501" }, "payment_dates": [ "2026-09-30" ], "earnings": { "basic": { "days": 30, "amount": "1423500" }, "transport": [ { "allowance": "210000" } ] }, "deductions": { "health": { "percent": "4", "amount": "56940" }, "pension": { "percent": "4", "amount": "56940" } }, "type": "replace", "predecessor": { "id": "pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" } }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createPayrollAdjustment&ejemplo=reemplazar) `POST /v1/payroll_adjustments` (`createPayrollAdjustment`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/payroll_adjustments" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "NOM-2026-09-001-E", "type": "delete", "predecessor": { "id": "pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "notes": [ "Enviada por error" ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createPayrollAdjustment&ejemplo=eliminar) ## Lo que hay que saber - **Un 102 por trabajador y por mes** en producción. Otro igual es `409 duplicate_document` (con `existing_id`, solo de la misma empresa y ambiente); para cambiarlo se usa la nota de ajuste. Dos pedidos simultáneos del mismo trabajador y mes no pasan los dos. - **Set de pruebas.** En habilitación con el set pendiente, la respuesta es `202` con `status: "validating"` en las dos versiones; reintentar con la misma clave o `POST …/send` no lo reenvía, y `PUT` en ese estado es `409 invalid_state`. - **Sin PDF ni correo.** Se descargan `…/xml`, `…/zip` y `…/dian_response`. - **Datos del trabajador.** Solo salen en las respuestas de tu llave. Los avisos (webhooks) llevan un resumen sin trabajador ni montos, el registro de peticiones no guarda cuerpos, y lo que se guarda para repetir una `Idempotency-Key` es solo `{id, object, status, number}`. Por eso **no se puede filtrar por la cédula**: las listas se filtran por `employee_code` (tu código interno del empleado), `external_id` y `period_end_from` / `period_end_to`. - El `second_surname` del trabajador es obligatorio. - El `external_id` es único entre las nóminas y sus ajustes (aparte de las facturas). - **Cobro por empleado:** cada nómina o nota de ajuste de producción se cobra con la lista de nómina del canal API; la habilitación no cobra. Las respuestas son las de [toda la API](/docs/respuestas). Referencia: [Nómina electrónica](/docs/api/payrolls) y [sus ajustes](/docs/api/payroll-adjustments). --- # Eventos RADIAN > Acuse de recibo, reclamo, recibo del bien, aceptación expresa y aceptación tácita de facturas por la API de Trevol. Fuente: https://platform.trevol.si/docs/eventos-radian Registra ante la DIAN los eventos de una factura: el acuse de recibo (030), el reclamo (031), el recibo del bien o servicio (032) y la aceptación expresa (033) de una factura que te emitieron, y la aceptación tácita (034) de una factura tuya a crédito. ## Los cinco eventos | `event_code` | Evento | Sobre | Cómo se identifica la factura | | ------------ | -------------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | | `030` | Acuse de recibo | Una factura **recibida** | `invoice` (CUFE, número, emisor) | | `031` | Reclamo | Una factura recibida | `invoice` y `claim` (obligatorio) | | `032` | Recibo del bien o servicio | Una factura recibida | `invoice` | | `033` | Aceptación expresa | Una factura recibida | `invoice` | | `034` | Aceptación tácita | Una factura **propia** a crédito, validada y no de consumidor final | `invoice_id` (`inv_…`, de Trevol) o `invoice: {cufe}` (también las que emitiste con otro software) | Los eventos 035 a 051 (aval, endosos, pago, cesión…) necesitan un participante RADIAN habilitado y **no están disponibles**: `validation_error` en `event_code`. ## Antes de emitir - Los eventos `030` y `032` los firma el **representante legal** de la empresa: se registra en la app. Sin él, `422 company_not_enabled` con `reason: "missing_legal_representative"`. `GET /v1/company` → `capabilities.radian_events` dice qué tienes. - Se comprueba **antes de numerar**. El NIT del emisor de una factura recibida no puede ser el de tu empresa (`invoice.issuer.nit`). ## Enviar un evento `POST /v1/radian_events` (`createRadianEvent`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/radian_events" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "ERP-ACUSE-OJ1234", "event_code": "030", "invoice": { "cufe": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "number": "OJ1234", "type": "01", "issuer": { "nit": "890330031", "check_digit": 3, "document_type": "31", "name": "OSPINA Y ASOCIADOS SAS" } } }' ``` #### Respuesta 201 ```json { "id": "rev_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "radian_event", "environment": "test", "status": "accepted", "event_code": "030", "number": "AR994000123", "prefix": "AR", "external_id": "ERP-ACUSE-OJ1234", "origin": "api", "cude": "abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "invoice": { "cufe": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "number": "OJ1234", "type": "01", "issuer": { "nit": "890330031", "check_digit": 3, "document_type": "31", "name": "OSPINA Y ASOCIADOS SAS" } }, "claim": null, "software": "own", "provider": "company", "dian": { "status_code": "02", "message": "Documento validado por la DIAN", "rules": [] }, "already_registered": false, "already_registered_elsewhere": false, "tacit": false, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createRadianEvent&ejemplo=acuse) El reclamo (031) lleva su concepto: `POST /v1/radian_events` (`createRadianEvent`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/radian_events" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "ERP-RECLAMO-OJ1234", "event_code": "031", "invoice": { "cufe": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "number": "OJ1234", "type": "01", "issuer": { "nit": "890330031", "check_digit": 3, "document_type": "31", "name": "OSPINA Y ASOCIADOS SAS" } }, "claim": { "code": "02", "note": "Faltaron dos cajas" } }' ``` #### Respuesta 201 ```json { "id": "rev_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "radian_event", "environment": "test", "status": "accepted", "event_code": "030", "number": "AR994000123", "prefix": "AR", "external_id": "ERP-ACUSE-OJ1234", "origin": "api", "cude": "abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "invoice": { "cufe": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "number": "OJ1234", "type": "01", "issuer": { "nit": "890330031", "check_digit": 3, "document_type": "31", "name": "OSPINA Y ASOCIADOS SAS" } }, "claim": null, "software": "own", "provider": "company", "dian": { "status_code": "02", "message": "Documento validado por la DIAN", "rules": [] }, "already_registered": false, "already_registered_elsewhere": false, "tacit": false, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createRadianEvent&ejemplo=reclamo) ## La aceptación tácita (034) Procede cuando pasan **3 días hábiles** desde el recibo del bien o servicio (032) sin que el comprador reclame ni acepte. Para no gastar un evento en vano, pregunta primero: `GET /v1/invoices/{id}/tacit_acceptance` (`retrieveInvoiceTacitAcceptance`) #### cURL ```bash curl "https://api.trevol.si/v1/invoices/inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/tacit_acceptance" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "object": "tacit_acceptance", "invoice_id": "inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "cufe": "abababababababababababababababababababababababababababababababababababababababababababababababab", "number": "FE1234", "allowed": false, "available_from": "2026-10-27", "rules": [ { "code": "LGC14", "severity": "rejection", "local": true, "message": "Faltan días hábiles para la aceptación tácita.", "explanation": "Pasan 3 días hábiles desde el recibo del bien o servicio (evento 032)." } ] } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveInvoiceTacitAcceptance) Solo lee: consulta a la DIAN con el certificado de la empresa y no envía nada; usa el mismo historial que el envío, y tiene su propio límite de 30 consultas por minuto por llave. Cuando procede, se pide con `invoice_id` en lugar de `invoice`: `POST /v1/radian_events` (`createRadianEvent`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/radian_events" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "ERP-TACITA-FE1234", "event_code": "034", "invoice_id": "inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }' ``` #### Respuesta 201 ```json { "id": "rev_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "radian_event", "environment": "test", "status": "accepted", "event_code": "030", "number": "AR994000123", "prefix": "AR", "external_id": "ERP-ACUSE-OJ1234", "origin": "api", "cude": "abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "invoice": { "cufe": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "number": "OJ1234", "type": "01", "issuer": { "nit": "890330031", "check_digit": 3, "document_type": "31", "name": "OSPINA Y ASOCIADOS SAS" } }, "claim": null, "software": "own", "provider": "company", "dian": { "status_code": "02", "message": "Documento validado por la DIAN", "rules": [] }, "already_registered": false, "already_registered_elsewhere": false, "tacit": false, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createRadianEvent&ejemplo=tacita) Si todavía no procede, el envío responde `422 tacit_acceptance_not_due` con la fecha (`available_from`) y las reglas; el evento queda en `error` con su número y se reintenta con `POST /v1/radian_events/{id}/send` cuando proceda. ## La aceptación tácita de una factura que no emitiste con Trevol Las facturas que emitiste **antes**, con otro software, también admiten el 034: Trevol las conoce por la sincronización con la DIAN. En lugar de `invoice_id`, identifica la factura por su **CUFE** en `invoice` (exactamente uno de los dos). `number` es opcional: si lo mandas, tiene que ser el de esa factura. `POST /v1/radian_events` (`createRadianEvent`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/radian_events" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "ERP-TACITA-FE4722", "event_code": "034", "invoice": { "cufe": "abababababababababababababababababababababababababababababababababababababababababababababababab", "number": "FE4722" } }' ``` #### Respuesta 201 ```json { "id": "rev_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "radian_event", "environment": "test", "status": "accepted", "event_code": "030", "number": "AR994000123", "prefix": "AR", "external_id": "ERP-ACUSE-OJ1234", "origin": "api", "cude": "abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "invoice": { "cufe": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "number": "OJ1234", "type": "01", "issuer": { "nit": "890330031", "check_digit": 3, "document_type": "31", "name": "OSPINA Y ASOCIADOS SAS" } }, "claim": null, "software": "own", "provider": "company", "dian": { "status_code": "02", "message": "Documento validado por la DIAN", "rules": [] }, "already_registered": false, "already_registered_elsewhere": false, "tacit": false, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createRadianEvent&ejemplo=tacita_cufe) La consulta previa también va por CUFE, con la misma respuesta y el mismo límite (las dos consultas comparten las 30 por minuto): `GET /v1/radian_events/tacit_acceptance` (`retrieveTacitAcceptanceByCufe`) #### cURL ```bash curl "https://api.trevol.si/v1/radian_events/tacit_acceptance?cufe=abababababababababababababababababababababababababababababababababababababababababababababababab" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "object": "tacit_acceptance", "cufe": "abababababababababababababababababababababababababababababababababababababababababababababababab", "number": "FE4722", "allowed": true, "available_from": "2025-05-14", "rules": [] } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveTacitAcceptanceByCufe) - La factura tiene que ser **tuya** (de la empresa y el ambiente de la llave), una factura electrónica de venta (`01`), aprobada por la DIAN, **a crédito** y no de consumidor final, con su XML ya leído por la sincronización. Si no, `422 validation_error` en `invoice.cufe` con el motivo; no se gasta ningún número. Un CUFE que no es de tu empresa es «no existe», sin decir si es de otra. - La sincronización trae las facturas de **producción**: con una llave de pruebas, el 034 va sobre una factura de Trevol (`invoice_id`). - Si el CUFE es de una factura de Trevol, es lo mismo que mandar su `invoice_id`: la respuesta trae `invoice.id`. Si no, `invoice` trae solo `cufe` y `number`. - Las mismas reglas de siempre: el 030 y el 032 del comprador en la DIAN, sin reclamo, aceptación expresa ni otro 034, y 3 días hábiles desde el 032. Un 034 vivo sobre esa factura (por la API, por la app o de antes) es `409 duplicate_document`. ## Lo que hay que saber - **Un rechazo es final.** El siguiente intento es otro evento con otro número (un `POST` nuevo, con otra `Idempotency-Key`). No hay `PUT`. - **Un evento vivo por factura y código.** Otro `POST` del mismo código sobre la misma factura es `409 duplicate_document` con el `existing_id` (`rev_…`), lo haya creado la API o la app. Para reintentar ese evento: `POST /v1/radian_events/{id}/send`. - **El orden de la DIAN.** La DIAN pide el evento anterior antes del siguiente. Si todavía no lo muestra, en la versión 2026-10-21 es `409 previous_event_pending` (no se envió nada; reintenta con `POST …/send`); en la 2026-10-22 es un `202 processing` y Trevol lo manda cuando aparezca. - **El 98 de la DIAN** («en proceso de validación») es `202` con `status: "validating"` en las dos versiones: Trevol solo consulta; **no reenvíes**. - `already_registered: true` (con `201`) significa que la DIAN ya mostraba el CUDE de **este** evento. Si la factura ya tenía ese evento por otro camino, es `422 dian_rejected` con `document.already_registered_elsewhere: true`. - Solo se descarga el `…/xml`. Se cobra con la lista de eventos del canal API. - El `external_id` es único entre los eventos (aparte de las facturas y las nóminas). - Los avisos (webhooks) son un resumen sin nombres ni NIT de quien emitió la factura. Los avisos «todavía no procede» y «falta el evento anterior» no emiten `radian_event.error`. Referencia: [Eventos RADIAN](/docs/api/radian-events). --- # Operaciones especiales > Facturas de exportación, en moneda extranjera, con AIU, mandatos, transporte de carga, notarios, divisas y sector salud por la API de Trevol. Fuente: https://platform.trevol.si/docs/operaciones-especiales La misma `POST /v1/invoices` emite también la factura de exportación, en moneda extranjera, y las operaciones con tipo propio: AIU, mandatos, transporte de carga, notarios, compra y venta de divisas y sector salud. ## Qué se puede emitir | Operación | Cómo se pide | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Exportación | `document_type: "02"`, con `customer` en el exterior (`country` distinto de `"CO"`, sin `municipality_code`) y, si se factura en otra moneda, `currency`. `delivery_terms` lleva el incoterm. | | Moneda extranjera | `currency: {"code", "exchange_rate", "rate_date"}` y, opcional, `alternative`. **Los valores del pedido van en esa moneda**; la factura y el CUFE van en pesos y `totals.currency` trae el equivalente. | | AIU | `operation_type: "09"`; cada línea lleva `aiu` (`"A"`, `"I"` o `"U"`) y la línea A, el `aiu_contract`. | | Mandatos | `operation_type: "11"`; la línea de un ingreso para terceros lleva `income: "third_party"` y su `mandator`. | | Transporte de carga | `operation_type: "12"`; cada línea lleva su `consignment` (remesa del RNDC). | | Notarios | `operation_type: "14"`; la línea lleva `notary` (matrícula y valor comercial). | | Divisas | `operation_type: "15"` (compra) o `"16"` (venta), con `due_diligence` (`"01"` a `"04"`, obligatoria en esas dos). | | Sector salud | `operation_type: "SS-CUFE"` con `interoperability` (obligatoria). | Un campo que no corresponde a la operación (un `aiu` fuera del 09, un `consignment` fuera del 12…) es `validation_error` con el campo exacto, antes de numerar. ## Una exportación El ejemplo de la especificación es una exportación en dólares: `POST /v1/invoices` (`createInvoice`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/invoices" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "EXP-77", "document_type": "02", "currency": { "code": "USD", "exchange_rate": "3950.12", "rate_date": "2026-10-21" }, "delivery_terms": { "incoterm": "FOB", "conditions": "Puerto de Cartagena" }, "customer": { "document_type": "50", "document_number": "US123456789", "name": "ACME INC", "country": "US", "city": "Miami", "region": "Florida", "postal_code": "33131", "address": "200 S Biscayne Blvd" }, "items": [ { "description": "Consultoría", "quantity": "1", "unit_price": "1000", "taxes": [ { "code": "01", "excluded": true } ] } ], "payment": { "form": "credit", "term_days": 30 } }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createInvoice&ejemplo=exportacion) La respuesta es la de toda factura ([Facturas](/docs/facturas)), con `document_type`, `currency`, `delivery_terms` y, si se pidieron, `shipment` e `interoperability`; `totals` siempre en pesos, y con moneda extranjera `totals.currency` con `total_currency` y `total_cop`. ## Lo que hay que saber - **Solo facturas.** Las notas crédito y débito no admiten moneda extranjera, exportación ni tipos de operación: cada campo sale con un mensaje que lo dice. - **`shipment` y `delivery` son distintos.** El envío físico de la mercancía se pide en `shipment` (`date`, `address`, `carrier`). `delivery` es el estado del PDF, el ZIP y el correo en la respuesta y no se pide. - **Contingencia.** `document_type: "03"` y `"04"` y el campo `contingency` siguen sin estar disponibles. - Se valida a fondo antes de numerar, con la misma lógica del editor de Trevol: la fecha de la tasa de cambio no puede ser posterior a la factura, y las reglas de cada operación del Anexo técnico 1.9 se revisan antes de gastar el número. - Se cobra como cualquier factura. Los campos de cada operación están en [Objetos](/docs/api/objetos): `Currency`, `DeliveryTerms`, `Shipment`, `Carrier`, `Interoperability`, `ItemMandator`, `ItemConsignment` e `ItemNotary`. --- # Saldo y recargas > Consultar el saldo prepago de Trevol, ver sus movimientos y preparar una recarga con Wompi por la API. Fuente: https://platform.trevol.si/docs/saldo El saldo prepago es de la **cuenta**, no de la empresa: todas las empresas de la cuenta lo comparten (`shared: true`). Solo lo que se emite en **producción** (llaves `tvl_live_…`) lo consume; la habilitación no cobra. Con `available` por debajo del precio de un documento, emitir en producción responde [`insufficient_balance`](/docs/errores#insufficient_balance) (402). ## El saldo Cualquier llave lo lee, de lectura o de escritura, de Pruebas o de Producción: es uno solo. `GET /v1/balance` (`retrieveBalance`) #### cURL ```bash curl "https://api.trevol.si/v1/balance" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "object": "balance", "currency": "COP", "available": 48600, "held": 400, "overdraft": 0, "low_threshold": 10000, "level": "ok", "updated_at": "2026-10-31T09:20:05-05:00", "shared": true } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveBalance) | Campo | Qué es | | --------------- | ----------------------------------------------------------------------------------- | | `available` | Lo que se puede gastar, en pesos (puede ser negativo por un cobro tardío). | | `held` | Lo apartado por documentos que la DIAN todavía valida. | | `overdraft` | Cuánto puede bajar `available` de cero al apartar un documento. | | `low_threshold` | El umbral del aviso de saldo bajo (20 % de la última recarga); `null` sin recargas. | | `level` | `empty` (`available` ≤ 0), `low` (por debajo del umbral) u `ok`. | El evento `balance.low` avisa por webhook cuando el saldo baja del umbral (solo en producción). ## Los movimientos Lo mismo que «Saldo y recargas» en la app: un documento es **una** fila; lo demás, una fila por movimiento. Filtra con `type` (`all`, `documents`, `funding` o `top_ups`) y pagina con `limit` y `starting_after` ([Paginación](/docs/paginacion)). `GET /v1/balance/transactions` (`listBalanceTransactions`) #### cURL ```bash curl "https://api.trevol.si/v1/balance/transactions?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "object": "list", "has_more": true, "next_cursor": "btx_9300", "data": [ { "id": "btx_9341", "object": "balance_transaction", "type": "document", "status": "pending", "amount": -400, "available_after": 48600, "description": "Factura FE1290 · apartado mientras la DIAN lo valida", "document": { "type": "invoice", "number": "FE1290" }, "company": { "nit": "900373115", "name": "TOSTADORA MONTEVERDE SAS" }, "reference": null, "created_at": "2026-10-31T09:20:05-05:00" }, { "id": "btx_9338", "object": "balance_transaction", "type": "document", "status": "charged", "amount": -400, "available_after": 49000, "description": "Nómina NE15", "document": { "type": "payroll", "number": "NE15" }, "company": { "nit": "900373591", "name": "CAFÉ DEL VALLE SAS" }, "reference": null, "created_at": "2026-10-31T08:02:41-05:00" }, { "id": "btx_9301", "object": "balance_transaction", "type": "document", "status": "released", "amount": 0, "available_after": 49000, "description": "Factura FE1289 · rechazado por la DIAN: no se cobró", "document": { "type": "invoice", "number": "FE1289" }, "company": { "nit": "900373115", "name": "TOSTADORA MONTEVERDE SAS" }, "reference": null, "created_at": "2026-10-30T17:10:00-05:00" }, { "id": "btx_9300", "object": "balance_transaction", "type": "top_up", "status": "completed", "amount": 50000, "available_after": 49400, "description": "Recarga TRV-REC-20261030-8F2A61C0D9B3", "document": null, "company": null, "reference": "TRV-REC-20261030-8F2A61C0D9B3", "created_at": "2026-10-30T16:58:12-05:00" } ] } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listBalanceTransactions) ## Recargar `POST /v1/balance/top_ups` **prepara** una recarga y devuelve lo que necesita el widget de Wompi (`WidgetCheckout`): la referencia, el monto, la firma de integridad y su vencimiento. El pago se hace en el widget; cuando Wompi lo aprueba, Trevol abona el saldo. `POST /v1/balance/top_ups` (`createBalanceTopUp`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/balance/top_ups" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "amount_cop": 50000 }' ``` #### Respuesta 201 ```json { "object": "top_up", "reference": "TRV-REC-20261031-4C1D2E3F4A5B", "amount_cop": 50000, "status": "prepared", "wompi": { "public_key": "pub_prod_Ejemplo0000000000000000000000", "reference": "TRV-REC-20261031-4C1D2E3F4A5B", "amount_in_cents": 5000000, "currency": "COP", "signature": "5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d", "expiration_time": "2026-10-31T15:20:05.000Z" } } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createBalanceTopUp&ejemplo=empresa) - **Es dinero real con cualquier llave**, también `tvl_test_`: no hay un saldo de pruebas. - Solo llaves de escritura; exige `Idempotency-Key` (repetir con la misma clave devuelve la **misma** recarga). La firma vence en una hora: si el widget venció, prepara otra. - El monto va entre la recarga mínima y la máxima ([`top_up_below_minimum`](/docs/errores#top_up_below_minimum) y [`top_up_above_maximum`](/docs/errores#top_up_above_maximum), con `minimum_cop` y `maximum_cop`). Si Wompi no está disponible: [`top_ups_unavailable`](/docs/errores#top_ups_unavailable). - La factura de la recarga sale a nombre de `buyer`; sin él, a nombre de la empresa de la llave con sus datos. Si no alcanzan, `422` en `buyer.*`. - Hasta 20 por llave cada 10 minutos. Referencia: [Saldo](/docs/api/balance). --- # Webhooks > Avisos firmados (Standard Webhooks) cuando un documento se valida, se rechaza, queda en error o se entrega; cómo crearlos, verificarlos, reintentos y rotación del secreto. Fuente: https://platform.trevol.si/docs/webhooks Un webhook es una dirección de tu servidor a la que Trevol le avisa cuando un documento se valida, se rechaza, queda en error o se entrega. Evita que tengas que preguntar, y es la forma de enterarte del resultado de un `202 processing`. - **Cada webhook es de una sola empresa y un solo ambiente**, como las llaves. Hasta 5 por empresa y ambiente. - Créalo en la [consola](https://platform.trevol.si/webhooks) o por API (llave de escritura). El **secreto** (`whsec_…`) se muestra **una sola vez**: guárdalo en tu servidor. - La URL debe ser `https://` con un nombre de dominio (no una IP), sin usuario ni clave y con puerto 443 o 1024–65535. Trevol resuelve el nombre en cada envío y **nunca** manda a direcciones privadas o locales. - Prueba el endpoint con `POST /v1/webhook_endpoints/{id}/test`: llega un evento `webhook.test`. ## Crear un endpoint `POST /v1/webhook_endpoints` (`createWebhookEndpoint`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/webhook_endpoints" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://tu-servidor.co/webhooks/trevol", "description": "Producción: facturación", "events": [ "invoice.accepted", "invoice.rejected", "credit_note.accepted" ] }' ``` [Se administra en Webhooks](https://platform.trevol.si/webhooks) La respuesta trae `secret` (`whsec_…`) solo en esta llamada y al rotarlo. `POST /v1/webhook_endpoints/{id}/test` (`testWebhookEndpoint`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/webhook_endpoints/we_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/test" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=testWebhookEndpoint) ## Los eventos Hay **50 tipos** (con `webhook.test`): los de la factura y las notas, el documento soporte, el documento equivalente (con sus notas de ajuste), la nómina y los eventos RADIAN, y los de la cuenta. Están todos en [Tipos de evento](/docs/webhooks/eventos). Una lista explícita admite hasta 60. Si suscribes `["*"]` recibes todos los que existan. > **Webhooks creados antes:** Un webhook creado antes de que existieran los de documento soporte, equivalente, nómina y RADIAN con `["*"]` sigue recibiendo solo los 22 de entonces (su `catalog_version` es `1`) hasta que guardes de nuevo su lista de eventos —con `PATCH` y `events`, o desde la consola—; los nuevos nacen con `catalog_version: 2`. Así ningún receptor existente recibe tipos que no conoce. `balance.low`, `numbering.low` y `numbering.expiring` solo existen en producción (llaves `tvl_live_…`). ## Qué llega El evento lleva un **resumen** del documento (identificador, estado, número, CUFE o CUDE, total, códigos de la DIAN y entrega), nunca los datos del comprador ni las líneas, y en `….error` un texto fijo por código. Pide el documento completo con `GET /v1/invoices/{id}`. El webhook puede llegar antes que la respuesta HTTP del `POST`: usa `data.id`, que es el mismo. ```http POST /trevol HTTP/1.1 Content-Type: application/json User-Agent: Trevol-Webhooks/1.0 (+https://platform.trevol.si/docs/webhooks) webhook-id: evt_8c1e0f0a7d3b4a6f9e2d1c0b5a4f3e2d webhook-timestamp: 1761163445 webhook-signature: v1,K5oZfzN0ZGxPpoGxC2c0vL1o0U3q6Qh2oA0h1c8l8bE= {"id":"evt_8c1e0f0a7d3b4a6f9e2d1c0b5a4f3e2d","object":"event","type":"invoice.accepted","timestamp":"2026-10-22T20:04:05Z", "environment":"test","api_version":"2026-10-22", "data":{"object":"invoice","id":"inv_08cd6774e31e401c9fa0fbf92b3fbdc0","status":"accepted","environment":"test", "number":"SETP994307051","prefix":"SETP","external_id":"VENTA-2026-000123","origin":"api","cufe":"52cede538652…", "issued_at":"2026-10-22T15:04:05-05:00","total":"178500.00","invoice":null, "dian":{"status_code":"00","rules":[]},"delivery":{"pdf":false,"zip":false,"email":{"status":"pending"}}}} ``` ## Verifica la firma Seguimos [Standard Webhooks](https://www.standardwebhooks.com). Cada entrega trae: - `webhook-id`: el mismo en cada reintento y en un reenvío manual. **Úsalo para no procesar dos veces el mismo evento**: la verificación no evita repeticiones dentro de los 5 minutos ni los reintentos de entrega. - `webhook-timestamp`: segundos Unix del intento. - `webhook-signature`: `v1,` y el HMAC-SHA256 en base64 de `webhook-id.webhook-timestamp.cuerpo`. La clave son los 32 bytes que siguen a `whsec_` (en base64). Durante la gracia de una rotación llegan dos firmas separadas por un espacio: basta con que una coincida. Calcúlala sobre el **cuerpo tal como llegó** (los bytes exactos, nunca el JSON ya decodificado), compárala en tiempo constante y rechaza los avisos con más de 5 minutos de diferencia. Hasta que los SDK se publiquen, verifica con este código (Node.js, sin dependencias) o con una biblioteca abierta de [Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries) de tu lenguaje: ```javascript import { createHmac, timingSafeEqual } from 'node:crypto'; // cuerpo: el texto crudo; cabeceras: un objeto con los nombres en minúsculas (Express: req.headers). export function verificar(cuerpo, cabeceras, secreto) { const id = cabeceras['webhook-id']; const marca = cabeceras['webhook-timestamp']; const firmas = cabeceras['webhook-signature']; if (!id || !/^\d+$/.test(marca ?? '') || !firmas) return false; if (Math.abs(Date.now() / 1000 - Number(marca)) > 300) return false; // más de 5 minutos const clave = Buffer.from(secreto.replace(/^whsec_/, ''), 'base64'); const esperada = createHmac('sha256', clave).update(`${id}.${marca}.${cuerpo}`).digest(); return firmas.split(' ').some((f) => { const [version, valor] = f.split(','); if (version !== 'v1' || !valor) return false; const recibida = Buffer.from(valor, 'base64'); return recibida.length === esperada.length && timingSafeEqual(recibida, esperada); }); } ``` Deduplica por `webhook-id`: esta verificación no evita repeticiones dentro de los 5 minutos ni los reintentos de entrega. Nunca compares las firmas con `==`: usa una comparación en tiempo constante. ## Entrega y reintentos - Cualquier `2xx` en 10 segundos cuenta como entregado. Un 3xx (no seguimos redirecciones), un 4xx, un 5xx, un tiempo agotado o un error de TLS cuentan como fallo. No guardamos la respuesta de tu servidor. - Reintentos con espera creciente: 1 min, 5 min, 30 min, 2 h, 5 h, 10 h y 14 h (8 intentos en unas 32 horas). Después la entrega queda `failed`; reenvíala con `POST /v1/events/{id}/resend` (3 intentos). - Si el endpoint falla sin parar durante 5 días, Trevol lo **desactiva** (`status: disabled`) y le avisa por correo al titular. Se reactiva con `PATCH` `{"status": "enabled"}`. - `GET /v1/webhook_endpoints/{id}/deliveries` muestra cada entrega y su error (`timeout`, `http_5xx`, `blocked_address`…). `GET /v1/events` guarda los eventos 30 días: sirve para recuperar lo que se perdió mientras tu servidor estaba caído. `GET /v1/webhook_endpoints/{id}/deliveries` (`listWebhookEndpointDeliveries`) #### cURL ```bash curl "https://api.trevol.si/v1/webhook_endpoints/we_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/deliveries?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listWebhookEndpointDeliveries) `POST /v1/events/{id}/resend` (`resendEvent`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/events/evt_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/resend" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "endpoint_id": "we_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=resendEvent&ejemplo=reenviar) ## Rotar el secreto Si pierdes el secreto, rótalo: el anterior sigue firmando hasta 72 horas para que no pierdas avisos (durante ese tiempo llegan dos firmas en `webhook-signature`). `POST /v1/webhook_endpoints/{id}/rotate_secret` (`rotateWebhookEndpointSecret`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/webhook_endpoints/we_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/rotate_secret" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "grace_period_hours": 24 }' ``` [Se administra en Webhooks](https://platform.trevol.si/webhooks) --- # Tipos de evento > Los 50 tipos de evento que Trevol manda a tus webhooks, agrupados por documento, con cuándo se emite cada uno. Fuente: https://platform.trevol.si/docs/webhooks/eventos Cada evento tiene la forma `{id, object: "event", type, timestamp, environment, api_version, data}`. El `data` es un resumen: nunca los datos del comprador, del vendedor ni del trabajador. Cómo recibirlos y verificarlos: [Webhooks](/docs/webhooks). La referencia de los objetos (`Event`, `DocumentEventData`…) está en [Objetos](/docs/api/objetos#Event). ## invoice | Tipo | Cuándo | | --- | --- | | `invoice.accepted` | La DIAN validó la factura. Pasa por cualquier camino (API, app, conector o verificación de fondo). | | `invoice.rejected` | La DIAN rechazó la factura. Corrígela con PUT y se reenvía con el mismo número. | | `invoice.error` | La factura quedó en error: la DIAN no confirmó y Trevol dejó de reintentar. `data.error` lleva un `code` y un texto fijo (nunca el mensaje de la DIAN). | | `invoice.delivered` | La factura ya tiene PDF, ZIP y un correo en estado final (la primera vez). | | `invoice.email_bounced` | El correo de la factura rebotó o el comprador lo marcó como queja (`delivery.email.status`: `bounced` o `complained`). | | `invoice.email_failed` | El correo de la factura falló de forma definitiva (`delivery.email.status`: `failed`). | ## credit_note | Tipo | Cuándo | | --- | --- | | `credit_note.accepted` | La DIAN validó la nota crédito. Pasa por cualquier camino (API, app, conector o verificación de fondo). | | `credit_note.rejected` | La DIAN rechazó la nota crédito. Corrígela con PUT y se reenvía con el mismo número. | | `credit_note.error` | La nota crédito quedó en error: la DIAN no confirmó y Trevol dejó de reintentar. `data.error` lleva un `code` y un texto fijo (nunca el mensaje de la DIAN). | | `credit_note.delivered` | La nota crédito ya tiene PDF, ZIP y un correo en estado final (la primera vez). | | `credit_note.email_bounced` | El correo de la nota crédito rebotó o el comprador lo marcó como queja (`delivery.email.status`: `bounced` o `complained`). | | `credit_note.email_failed` | El correo de la nota crédito falló de forma definitiva (`delivery.email.status`: `failed`). | ## debit_note | Tipo | Cuándo | | --- | --- | | `debit_note.accepted` | La DIAN validó la nota débito. Pasa por cualquier camino (API, app, conector o verificación de fondo). | | `debit_note.rejected` | La DIAN rechazó la nota débito. Corrígela con PUT y se reenvía con el mismo número. | | `debit_note.error` | La nota débito quedó en error: la DIAN no confirmó y Trevol dejó de reintentar. `data.error` lleva un `code` y un texto fijo (nunca el mensaje de la DIAN). | | `debit_note.delivered` | La nota débito ya tiene PDF, ZIP y un correo en estado final (la primera vez). | | `debit_note.email_bounced` | El correo de la nota débito rebotó o el comprador lo marcó como queja (`delivery.email.status`: `bounced` o `complained`). | | `debit_note.email_failed` | El correo de la nota débito falló de forma definitiva (`delivery.email.status`: `failed`). | ## balance | Tipo | Cuándo | | --- | --- | | `balance.low` | El saldo de la cuenta quedó bajo o se agotó (`data.level`). Solo producción: te llega a cada empresa de la cuenta. | ## numbering | Tipo | Cuándo | | --- | --- | | `numbering.low` | A una resolución de numeración le quedan pocos números. Solo producción. | | `numbering.expiring` | Una resolución de numeración vence en 30 y en 7 días. Solo producción. | ## certificate | Tipo | Cuándo | | --- | --- | | `certificate.expiring` | El certificado de firma de la empresa vence en 30, 15, 7 y 1 día. Los dos ambientes. | ## support_document | Tipo | Cuándo | | --- | --- | | `support_document.accepted` | La DIAN validó el documento soporte. Pasa por cualquier camino (API, app, conector o verificación de fondo). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `support_document.rejected` | La DIAN rechazó el documento soporte. Corrígela con PUT y se reenvía con el mismo número. (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `support_document.error` | El documento soporte quedó en error: la DIAN no confirmó y Trevol dejó de reintentar. `data.error` lleva un `code` y un texto fijo (nunca el mensaje de la DIAN). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | ## support_document_adjustment | Tipo | Cuándo | | --- | --- | | `support_document_adjustment.accepted` | La DIAN validó la nota de ajuste del documento soporte. Pasa por cualquier camino (API, app, conector o verificación de fondo). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `support_document_adjustment.rejected` | La DIAN rechazó la nota de ajuste del documento soporte. Corrígela con PUT y se reenvía con el mismo número. (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `support_document_adjustment.error` | La nota de ajuste del documento soporte quedó en error: la DIAN no confirmó y Trevol dejó de reintentar. `data.error` lleva un `code` y un texto fijo (nunca el mensaje de la DIAN). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | ## equivalent_document | Tipo | Cuándo | | --- | --- | | `equivalent_document.accepted` | La DIAN validó el documento equivalente. Pasa por cualquier camino (API, app, conector o verificación de fondo). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `equivalent_document.rejected` | La DIAN rechazó el documento equivalente. Corrígela con PUT y se reenvía con el mismo número. (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `equivalent_document.error` | El documento equivalente quedó en error: la DIAN no confirmó y Trevol dejó de reintentar. `data.error` lleva un `code` y un texto fijo (nunca el mensaje de la DIAN). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `equivalent_document.delivered` | El documento equivalente ya tiene PDF, ZIP y un correo en estado final (la primera vez). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `equivalent_document.email_bounced` | El correo de el documento equivalente rebotó o el comprador lo marcó como queja (`delivery.email.status`: `bounced` o `complained`). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `equivalent_document.email_failed` | El correo de el documento equivalente falló de forma definitiva (`delivery.email.status`: `failed`). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | ## equivalent_document_adjustment | Tipo | Cuándo | | --- | --- | | `equivalent_document_adjustment.accepted` | La DIAN validó la nota de ajuste del documento equivalente. Pasa por cualquier camino (API, app, conector o verificación de fondo). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `equivalent_document_adjustment.rejected` | La DIAN rechazó la nota de ajuste del documento equivalente. Corrígela con PUT y se reenvía con el mismo número. (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `equivalent_document_adjustment.error` | La nota de ajuste del documento equivalente quedó en error: la DIAN no confirmó y Trevol dejó de reintentar. `data.error` lleva un `code` y un texto fijo (nunca el mensaje de la DIAN). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `equivalent_document_adjustment.delivered` | La nota de ajuste del documento equivalente ya tiene PDF, ZIP y un correo en estado final (la primera vez). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `equivalent_document_adjustment.email_bounced` | El correo de la nota de ajuste del documento equivalente rebotó o el comprador lo marcó como queja (`delivery.email.status`: `bounced` o `complained`). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `equivalent_document_adjustment.email_failed` | El correo de la nota de ajuste del documento equivalente falló de forma definitiva (`delivery.email.status`: `failed`). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | ## payroll | Tipo | Cuándo | | --- | --- | | `payroll.accepted` | La DIAN validó la nómina electrónica. Pasa por cualquier camino (API, app, conector o verificación de fondo). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `payroll.rejected` | La DIAN rechazó la nómina electrónica. Corrígela con PUT y se reenvía con el mismo número. (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `payroll.error` | La nómina electrónica quedó en error: la DIAN no confirmó y Trevol dejó de reintentar. `data.error` lleva un `code` y un texto fijo (nunca el mensaje de la DIAN). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | ## payroll_adjustment | Tipo | Cuándo | | --- | --- | | `payroll_adjustment.accepted` | La DIAN validó la nota de ajuste de nómina. Pasa por cualquier camino (API, app, conector o verificación de fondo). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `payroll_adjustment.rejected` | La DIAN rechazó la nota de ajuste de nómina. Corrígela con PUT y se reenvía con el mismo número. (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `payroll_adjustment.error` | La nota de ajuste de nómina quedó en error: la DIAN no confirmó y Trevol dejó de reintentar. `data.error` lleva un `code` y un texto fijo (nunca el mensaje de la DIAN). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | ## radian_event | Tipo | Cuándo | | --- | --- | | `radian_event.accepted` | La DIAN validó el evento RADIAN. Pasa por cualquier camino (API, app, conector o verificación de fondo). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `radian_event.rejected` | La DIAN rechazó el evento. Un rechazo es final: el siguiente intento es otro `POST /v1/radian_events` (otro número). (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | | `radian_event.error` | El evento quedó en error: la DIAN no confirmó y Trevol dejó de reintentar. `data.error` lleva un `code` y un texto fijo. Reenvíalo con `POST …/send`. (Catálogo 2: un `*` de un endpoint creado antes de la fase 3 no lo recibe hasta que se guarde de nuevo su lista de eventos.) | ## webhook | Tipo | Cuándo | | --- | --- | | `webhook.test` | Lo manda POST …/test (o «Probar» en la app) solo a ese endpoint. No se suscribe. | --- # Backend intermediario > Cómo usar Trevol desde una aplicación web (Next.js, Nuxt, SvelteKit, Angular) sin exponer la llave en el navegador. Fuente: https://platform.trevol.si/docs/backend-intermediario **No hay SDK para el navegador**, porque la llave de la API nunca debe estar en el navegador: quien la vea puede emitir documentos a nombre de tu empresa. El patrón es el de **backend intermediario**: 1. Tu frontend llama a **tu servidor** (una ruta de tu aplicación), con la sesión de tu usuario. 2. Tu servidor comprueba quién es y qué puede hacer, arma el documento **desde tus propios datos** (no reenvía el cuerpo que llegó del navegador), y llama a Trevol. 3. Tu servidor responde al frontend lo que el usuario necesita (el estado y el número, no la llave). > **Qué no hacer:** - No pongas la llave en una variable pública (`NEXT_PUBLIC_…`, `VITE_…`, `PUBLIC_…`, `environment.ts` de Angular): esas variables viajan al navegador. > - No hagas un «proxy abierto» que reenvíe cualquier ruta y cuerpo a Trevol: cualquiera podría emitir lo que quiera. > - Deriva la `Idempotency-Key` de tu venta (`VENTA-2026-000123`), no de un valor que mande el navegador. Los ejemplos con el SDK de Node.js (Next.js, Nuxt, SvelteKit y Angular) llegan cuando los SDK se publiquen. El patrón es el mismo con cualquier cliente HTTP: la ruta de tu servidor lee `TREVOL_API_KEY` del entorno, arma el documento desde tus datos y llama a `POST https://api.trevol.si/v1/invoices` con `Authorization: Bearer`, un `Idempotency-Key` derivado de tu venta y el cuerpo que describe la [referencia](/docs/api/invoices). ## Y los webhooks Los avisos de Trevol también llegan a tu servidor, nunca al navegador. Recíbelos en una ruta tuya, verifica la firma con el cuerpo crudo y avisa a tu frontend por tu propio canal (WebSocket, SSE o sondeo de tu base). Ver [Webhooks](/docs/webhooks#firma). --- # Para IA > Cada página de la documentación en Markdown, llms.txt, llms-full.txt, la especificación OpenAPI y un servidor MCP de solo lectura. Fuente: https://platform.trevol.si/docs/ia Esta documentación está pensada también para asistentes de IA y agentes de código. Nada de lo que sigue necesita una cuenta ni una llave. ## Cada página en Markdown - Agrega `.md` a la dirección de cualquier página: `/docs/facturas.md` (el inicio es `/docs.md`). Es la misma página, en Markdown limpio, con sus ejemplos de código. - En cada página, el botón **«Copiar como Markdown»** copia ese archivo y **«Ver como Markdown»** lo abre. ## `llms.txt` y `llms-full.txt` - [`/llms.txt`](/llms.txt): el índice, en el formato de [llmstxt.org](https://llmstxt.org), con un enlace al `.md` de cada página. - [`/llms-full.txt`](/llms-full.txt): toda la documentación junta, en el orden del menú. Para no pasar de tamaño, cada operación lleva solo cURL (con los SDK publicados, también Node y Python); el `.md` de cada página lleva todos los lenguajes disponibles. - [`/openapi.json`](/openapi.json): la especificación OpenAPI 3.1 completa. ## Servidor MCP de la documentación `https://platform.trevol.si/mcp` es un servidor [MCP](https://modelcontextprotocol.io) **de solo lectura** (Streamable HTTP, sin estado y sin autenticación) con dos herramientas: | Herramienta | Qué hace | | ------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `buscar_docs` | Busca en la documentación (`consulta`, y `limite` de 1 a 10) y devuelve título, dirección y un fragmento. | | `leer_pagina` | Devuelve el Markdown de una página (`ruta`, como `/docs/facturas` o `facturas`) o solo de una sección (`seccion`, por ancla). | No lee datos de nadie, no emite documentos y no necesita llaves. Un servidor MCP que emita es otra cosa: necesitaría llaves y queda para después. ### Cómo agregarlo a un cliente Claude Code: ```bash claude mcp add --transport http trevol-docs https://platform.trevol.si/mcp ``` Cursor (`.cursor/mcp.json`): ```json { "mcpServers": { "trevol-docs": { "url": "https://platform.trevol.si/mcp" } } } ``` VS Code (`.vscode/mcp.json`; usa `servers` y pide el tipo de transporte): ```json { "servers": { "trevol-docs": { "type": "http", "url": "https://platform.trevol.si/mcp" } } } ``` Con otro cliente, agrega un servidor MCP remoto de tipo HTTP (Streamable HTTP) con esa dirección y consulta su documentación para el formato exacto del archivo. Para probarlo a mano: ```bash curl -s -X POST https://platform.trevol.si/mcp \ -H 'content-type: application/json' \ -H 'accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` > **No pegues llaves en una consulta:** `buscar_docs` no guarda ni registra la consulta, pero una llave pegada en un asistente queda en su historial. Nunca escribas una llave `tvl_…` en una conversación con un asistente. --- # Referencia de la API > Todas las operaciones de la API de Trevol, por recurso, con ejemplos de código en cURL y siete lenguajes. Fuente: https://platform.trevol.si/docs/api La API de Trevol es REST con JSON, en `https://api.trevol.si/v1`. Esta referencia sale de la [especificación OpenAPI 3.1](/openapi.json) (versión 2026-10-22): una página por recurso y, en [Objetos](/docs/api/objetos), cada esquema con sus campos. Antes de empezar, lee [Llaves y entornos](/docs/llaves), [Convenciones](/docs/convenciones) e [Idempotencia](/docs/idempotencia). ## General [Ver la página de General](/docs/api/general) | Método | Ruta | Qué hace | | --- | --- | --- | | `GET` | `/v1/health` | [Salud](/docs/api/general#getHealth) | | `GET` | `/v1/company` | [La empresa de la llave](/docs/api/general#retrieveCompany) | | `GET` | `/v1/dian/acquirer` | [Datos del adquiriente en la DIAN](/docs/api/general#retrieveDianAcquirer) | ## Facturas [Ver la página de Facturas](/docs/api/invoices) | Método | Ruta | Qué hace | | --- | --- | --- | | `POST` | `/v1/invoices` | [Emitir una factura](/docs/api/invoices#createInvoice) | | `GET` | `/v1/invoices` | [Listar (factura)](/docs/api/invoices#listInvoices) | | `POST` | `/v1/invoices/preview` | [Calcular una factura sin emitirla](/docs/api/invoices#previewInvoice) | | `GET` | `/v1/invoices/{id}` | [Una factura](/docs/api/invoices#retrieveInvoice) | | `PUT` | `/v1/invoices/{id}` | [Corregir una factura rechazada (mismo número) y reenviarla](/docs/api/invoices#correctInvoice) | | `POST` | `/v1/invoices/{id}/send` | [Reenviar una pendiente o en error](/docs/api/invoices#sendInvoice) | | `GET` | `/v1/invoices/{id}/pdf` | [Archivo pdf](/docs/api/invoices#downloadInvoicePdf) | | `GET` | `/v1/invoices/{id}/xml` | [Archivo xml](/docs/api/invoices#downloadInvoiceXml) | | `GET` | `/v1/invoices/{id}/zip` | [Archivo zip](/docs/api/invoices#downloadInvoiceZip) | | `GET` | `/v1/invoices/{id}/dian_response` | [Archivo dian_response](/docs/api/invoices#downloadInvoiceDianResponse) | | `GET` | `/v1/invoices/{id}/tacit_acceptance` | [¿Procede ya la aceptación tácita (034) de esta factura?](/docs/api/invoices#retrieveInvoiceTacitAcceptance) | ## Notas crédito [Ver la página de Notas crédito](/docs/api/credit-notes) | Método | Ruta | Qué hace | | --- | --- | --- | | `POST` | `/v1/credit_notes` | [Emitir una nota crédito](/docs/api/credit-notes#createCreditNote) | | `GET` | `/v1/credit_notes` | [Listar (nota crédito)](/docs/api/credit-notes#listCreditNotes) | | `POST` | `/v1/credit_notes/preview` | [Calcular una nota crédito sin emitirla](/docs/api/credit-notes#previewCreditNote) | | `GET` | `/v1/credit_notes/{id}` | [Una nota crédito](/docs/api/credit-notes#retrieveCreditNote) | | `PUT` | `/v1/credit_notes/{id}` | [Corregir una nota crédito rechazada (mismo número) y reenviarla](/docs/api/credit-notes#correctCreditNote) | | `POST` | `/v1/credit_notes/{id}/send` | [Reenviar una pendiente o en error](/docs/api/credit-notes#sendCreditNote) | | `GET` | `/v1/credit_notes/{id}/pdf` | [Archivo pdf](/docs/api/credit-notes#downloadCreditNotePdf) | | `GET` | `/v1/credit_notes/{id}/xml` | [Archivo xml](/docs/api/credit-notes#downloadCreditNoteXml) | | `GET` | `/v1/credit_notes/{id}/zip` | [Archivo zip](/docs/api/credit-notes#downloadCreditNoteZip) | | `GET` | `/v1/credit_notes/{id}/dian_response` | [Archivo dian_response](/docs/api/credit-notes#downloadCreditNoteDianResponse) | ## Notas débito [Ver la página de Notas débito](/docs/api/debit-notes) | Método | Ruta | Qué hace | | --- | --- | --- | | `POST` | `/v1/debit_notes` | [Emitir una nota débito](/docs/api/debit-notes#createDebitNote) | | `GET` | `/v1/debit_notes` | [Listar (nota débito)](/docs/api/debit-notes#listDebitNotes) | | `POST` | `/v1/debit_notes/preview` | [Calcular una nota débito sin emitirla](/docs/api/debit-notes#previewDebitNote) | | `GET` | `/v1/debit_notes/{id}` | [Una nota débito](/docs/api/debit-notes#retrieveDebitNote) | | `PUT` | `/v1/debit_notes/{id}` | [Corregir una nota débito rechazada (mismo número) y reenviarla](/docs/api/debit-notes#correctDebitNote) | | `POST` | `/v1/debit_notes/{id}/send` | [Reenviar una pendiente o en error](/docs/api/debit-notes#sendDebitNote) | | `GET` | `/v1/debit_notes/{id}/pdf` | [Archivo pdf](/docs/api/debit-notes#downloadDebitNotePdf) | | `GET` | `/v1/debit_notes/{id}/xml` | [Archivo xml](/docs/api/debit-notes#downloadDebitNoteXml) | | `GET` | `/v1/debit_notes/{id}/zip` | [Archivo zip](/docs/api/debit-notes#downloadDebitNoteZip) | | `GET` | `/v1/debit_notes/{id}/dian_response` | [Archivo dian_response](/docs/api/debit-notes#downloadDebitNoteDianResponse) | ## Documento soporte [Ver la página de Documento soporte](/docs/api/support-documents) | Método | Ruta | Qué hace | | --- | --- | --- | | `POST` | `/v1/support_documents` | [Emitir un documento soporte](/docs/api/support-documents#createSupportDocument) | | `GET` | `/v1/support_documents` | [Listar (documento soporte)](/docs/api/support-documents#listSupportDocuments) | | `POST` | `/v1/support_documents/preview` | [Calcular un documento soporte sin emitirlo](/docs/api/support-documents#previewSupportDocument) | | `GET` | `/v1/support_documents/{id}` | [Un documento soporte](/docs/api/support-documents#retrieveSupportDocument) | | `PUT` | `/v1/support_documents/{id}` | [Corregir un documento soporte rechazado (mismo número) y reenviarlo](/docs/api/support-documents#correctSupportDocument) | | `POST` | `/v1/support_documents/{id}/send` | [Reenviar una pendiente o en error](/docs/api/support-documents#sendSupportDocument) | | `GET` | `/v1/support_documents/{id}/xml` | [Archivo xml](/docs/api/support-documents#downloadSupportDocumentXml) | | `GET` | `/v1/support_documents/{id}/dian_response` | [Archivo dian_response](/docs/api/support-documents#downloadSupportDocumentDianResponse) | ## Notas de ajuste del documento soporte [Ver la página de Notas de ajuste del documento soporte](/docs/api/support-document-adjustments) | Método | Ruta | Qué hace | | --- | --- | --- | | `POST` | `/v1/support_document_adjustments` | [Emitir una nota de ajuste del documento soporte](/docs/api/support-document-adjustments#createSupportDocumentAdjustment) | | `GET` | `/v1/support_document_adjustments` | [Listar (nota de ajuste del documento soporte)](/docs/api/support-document-adjustments#listSupportDocumentAdjustments) | | `POST` | `/v1/support_document_adjustments/preview` | [Calcular una nota de ajuste del documento soporte sin emitirla](/docs/api/support-document-adjustments#previewSupportDocumentAdjustment) | | `GET` | `/v1/support_document_adjustments/{id}` | [Una nota de ajuste del documento soporte](/docs/api/support-document-adjustments#retrieveSupportDocumentAdjustment) | | `PUT` | `/v1/support_document_adjustments/{id}` | [Corregir una nota de ajuste del documento soporte rechazada (mismo número) y reenviarla](/docs/api/support-document-adjustments#correctSupportDocumentAdjustment) | | `POST` | `/v1/support_document_adjustments/{id}/send` | [Reenviar una pendiente o en error](/docs/api/support-document-adjustments#sendSupportDocumentAdjustment) | | `GET` | `/v1/support_document_adjustments/{id}/xml` | [Archivo xml](/docs/api/support-document-adjustments#downloadSupportDocumentAdjustmentXml) | | `GET` | `/v1/support_document_adjustments/{id}/dian_response` | [Archivo dian_response](/docs/api/support-document-adjustments#downloadSupportDocumentAdjustmentDianResponse) | ## Documento equivalente [Ver la página de Documento equivalente](/docs/api/equivalent-documents) | Método | Ruta | Qué hace | | --- | --- | --- | | `POST` | `/v1/equivalent_documents` | [Emitir un documento equivalente](/docs/api/equivalent-documents#createEquivalentDocument) | | `GET` | `/v1/equivalent_documents` | [Listar (documento equivalente)](/docs/api/equivalent-documents#listEquivalentDocuments) | | `POST` | `/v1/equivalent_documents/preview` | [Calcular un documento equivalente sin emitirlo](/docs/api/equivalent-documents#previewEquivalentDocument) | | `GET` | `/v1/equivalent_documents/{id}` | [Un documento equivalente](/docs/api/equivalent-documents#retrieveEquivalentDocument) | | `PUT` | `/v1/equivalent_documents/{id}` | [Corregir un documento equivalente rechazado (mismo número) y reenviarlo](/docs/api/equivalent-documents#correctEquivalentDocument) | | `POST` | `/v1/equivalent_documents/{id}/send` | [Reenviar una pendiente o en error](/docs/api/equivalent-documents#sendEquivalentDocument) | | `GET` | `/v1/equivalent_documents/{id}/pdf` | [Archivo pdf](/docs/api/equivalent-documents#downloadEquivalentDocumentPdf) | | `GET` | `/v1/equivalent_documents/{id}/xml` | [Archivo xml](/docs/api/equivalent-documents#downloadEquivalentDocumentXml) | | `GET` | `/v1/equivalent_documents/{id}/zip` | [Archivo zip](/docs/api/equivalent-documents#downloadEquivalentDocumentZip) | | `GET` | `/v1/equivalent_documents/{id}/dian_response` | [Archivo dian_response](/docs/api/equivalent-documents#downloadEquivalentDocumentDianResponse) | ## Notas de ajuste del documento equivalente [Ver la página de Notas de ajuste del documento equivalente](/docs/api/equivalent-document-adjustments) | Método | Ruta | Qué hace | | --- | --- | --- | | `POST` | `/v1/equivalent_document_adjustments` | [Emitir una nota de ajuste del documento equivalente](/docs/api/equivalent-document-adjustments#createEquivalentDocumentAdjustment) | | `GET` | `/v1/equivalent_document_adjustments` | [Listar (nota de ajuste del documento equivalente)](/docs/api/equivalent-document-adjustments#listEquivalentDocumentAdjustments) | | `POST` | `/v1/equivalent_document_adjustments/preview` | [Calcular una nota de ajuste del documento equivalente sin emitirla](/docs/api/equivalent-document-adjustments#previewEquivalentDocumentAdjustment) | | `GET` | `/v1/equivalent_document_adjustments/{id}` | [Una nota de ajuste del documento equivalente](/docs/api/equivalent-document-adjustments#retrieveEquivalentDocumentAdjustment) | | `PUT` | `/v1/equivalent_document_adjustments/{id}` | [Corregir una nota de ajuste del documento equivalente rechazada (mismo número) y reenviarla](/docs/api/equivalent-document-adjustments#correctEquivalentDocumentAdjustment) | | `POST` | `/v1/equivalent_document_adjustments/{id}/send` | [Reenviar una pendiente o en error](/docs/api/equivalent-document-adjustments#sendEquivalentDocumentAdjustment) | | `GET` | `/v1/equivalent_document_adjustments/{id}/pdf` | [Archivo pdf](/docs/api/equivalent-document-adjustments#downloadEquivalentDocumentAdjustmentPdf) | | `GET` | `/v1/equivalent_document_adjustments/{id}/xml` | [Archivo xml](/docs/api/equivalent-document-adjustments#downloadEquivalentDocumentAdjustmentXml) | | `GET` | `/v1/equivalent_document_adjustments/{id}/zip` | [Archivo zip](/docs/api/equivalent-document-adjustments#downloadEquivalentDocumentAdjustmentZip) | | `GET` | `/v1/equivalent_document_adjustments/{id}/dian_response` | [Archivo dian_response](/docs/api/equivalent-document-adjustments#downloadEquivalentDocumentAdjustmentDianResponse) | ## Nómina electrónica [Ver la página de Nómina electrónica](/docs/api/payrolls) | Método | Ruta | Qué hace | | --- | --- | --- | | `POST` | `/v1/payrolls` | [Emitir una nómina electrónica](/docs/api/payrolls#createPayroll) | | `GET` | `/v1/payrolls` | [Listar (nómina electrónica)](/docs/api/payrolls#listPayrolls) | | `POST` | `/v1/payrolls/preview` | [Calcular una nómina electrónica sin emitirla](/docs/api/payrolls#previewPayroll) | | `GET` | `/v1/payrolls/{id}` | [Una nómina electrónica](/docs/api/payrolls#retrievePayroll) | | `PUT` | `/v1/payrolls/{id}` | [Corregir una nómina electrónica rechazada (mismo número) y reenviarla](/docs/api/payrolls#correctPayroll) | | `POST` | `/v1/payrolls/{id}/send` | [Reenviar una pendiente o en error](/docs/api/payrolls#sendPayroll) | | `GET` | `/v1/payrolls/{id}/xml` | [Archivo xml](/docs/api/payrolls#downloadPayrollXml) | | `GET` | `/v1/payrolls/{id}/zip` | [Archivo zip](/docs/api/payrolls#downloadPayrollZip) | | `GET` | `/v1/payrolls/{id}/dian_response` | [Archivo dian_response](/docs/api/payrolls#downloadPayrollDianResponse) | ## Notas de ajuste de nómina [Ver la página de Notas de ajuste de nómina](/docs/api/payroll-adjustments) | Método | Ruta | Qué hace | | --- | --- | --- | | `POST` | `/v1/payroll_adjustments` | [Emitir una nota de ajuste de nómina](/docs/api/payroll-adjustments#createPayrollAdjustment) | | `GET` | `/v1/payroll_adjustments` | [Listar (nota de ajuste de nómina)](/docs/api/payroll-adjustments#listPayrollAdjustments) | | `POST` | `/v1/payroll_adjustments/preview` | [Calcular una nota de ajuste de nómina sin emitirla](/docs/api/payroll-adjustments#previewPayrollAdjustment) | | `GET` | `/v1/payroll_adjustments/{id}` | [Una nota de ajuste de nómina](/docs/api/payroll-adjustments#retrievePayrollAdjustment) | | `PUT` | `/v1/payroll_adjustments/{id}` | [Corregir una nota de ajuste de nómina rechazada (mismo número) y reenviarla](/docs/api/payroll-adjustments#correctPayrollAdjustment) | | `POST` | `/v1/payroll_adjustments/{id}/send` | [Reenviar una pendiente o en error](/docs/api/payroll-adjustments#sendPayrollAdjustment) | | `GET` | `/v1/payroll_adjustments/{id}/xml` | [Archivo xml](/docs/api/payroll-adjustments#downloadPayrollAdjustmentXml) | | `GET` | `/v1/payroll_adjustments/{id}/zip` | [Archivo zip](/docs/api/payroll-adjustments#downloadPayrollAdjustmentZip) | | `GET` | `/v1/payroll_adjustments/{id}/dian_response` | [Archivo dian_response](/docs/api/payroll-adjustments#downloadPayrollAdjustmentDianResponse) | ## Eventos RADIAN [Ver la página de Eventos RADIAN](/docs/api/radian-events) | Método | Ruta | Qué hace | | --- | --- | --- | | `POST` | `/v1/radian_events` | [Emitir un evento RADIAN](/docs/api/radian-events#createRadianEvent) | | `GET` | `/v1/radian_events` | [Listar (evento RADIAN)](/docs/api/radian-events#listRadianEvents) | | `GET` | `/v1/radian_events/{id}` | [Un evento radian](/docs/api/radian-events#retrieveRadianEvent) | | `POST` | `/v1/radian_events/{id}/send` | [Reenviar una pendiente o en error](/docs/api/radian-events#sendRadianEvent) | | `GET` | `/v1/radian_events/{id}/xml` | [Archivo xml](/docs/api/radian-events#downloadRadianEventXml) | | `GET` | `/v1/radian_events/tacit_acceptance` | [¿Procede ya la aceptación tácita (034) de esta factura? (por CUFE)](/docs/api/radian-events#retrieveTacitAcceptanceByCufe) | ## Endpoints de webhooks [Ver la página de Endpoints de webhooks](/docs/api/webhook-endpoints) | Método | Ruta | Qué hace | | --- | --- | --- | | `GET` | `/v1/webhook_endpoints` | [Los endpoints de webhooks de la empresa y el ambiente](/docs/api/webhook-endpoints#listWebhookEndpoints) | | `POST` | `/v1/webhook_endpoints` | [Crear un endpoint de webhooks (máximo 5 por empresa y ambiente)](/docs/api/webhook-endpoints#createWebhookEndpoint) | | `GET` | `/v1/webhook_endpoints/{id}` | [Un endpoint](/docs/api/webhook-endpoints#retrieveWebhookEndpoint) | | `PATCH` | `/v1/webhook_endpoints/{id}` | [Cambiar la URL, la descripción, los eventos o pausar y reactivar](/docs/api/webhook-endpoints#updateWebhookEndpoint) | | `DELETE` | `/v1/webhook_endpoints/{id}` | [Borrar el endpoint (con sus entregas)](/docs/api/webhook-endpoints#deleteWebhookEndpoint) | | `POST` | `/v1/webhook_endpoints/{id}/rotate_secret` | [Rotar el secreto](/docs/api/webhook-endpoints#rotateWebhookEndpointSecret) | | `POST` | `/v1/webhook_endpoints/{id}/test` | [Mandar un evento `webhook.test` solo a este endpoint (debe estar activo)](/docs/api/webhook-endpoints#testWebhookEndpoint) | | `GET` | `/v1/webhook_endpoints/{id}/deliveries` | [Las entregas de este endpoint, de la más nueva a la más vieja](/docs/api/webhook-endpoints#listWebhookEndpointDeliveries) | ## Eventos [Ver la página de Eventos](/docs/api/events) | Método | Ruta | Qué hace | | --- | --- | --- | | `GET` | `/v1/events` | [Los eventos de 30 días de la empresa y el ambiente](/docs/api/events#listEvents) | | `GET` | `/v1/events/{id}` | [Un evento](/docs/api/events#retrieveEvent) | | `POST` | `/v1/events/{id}/resend` | [Reenviar un evento a un endpoint (entrega manual nueva; el `webhook-id` es el mismo)](/docs/api/events#resendEvent) | ## Registro de peticiones [Ver la página de Registro de peticiones](/docs/api/requests) | Método | Ruta | Qué hace | | --- | --- | --- | | `GET` | `/v1/requests` | [El registro de peticiones de la empresa y el ambiente de la llave (30 días)](/docs/api/requests#listRequests) | ## Saldo [Ver la página de Saldo](/docs/api/balance) | Método | Ruta | Qué hace | | --- | --- | --- | | `GET` | `/v1/balance` | [El saldo de la cuenta (lo comparten sus empresas)](/docs/api/balance#retrieveBalance) | | `GET` | `/v1/balance/transactions` | [Los movimientos del saldo de la cuenta](/docs/api/balance#listBalanceTransactions) | | `POST` | `/v1/balance/top_ups` | [Preparar una recarga del saldo (para el widget de Wompi)](/docs/api/balance#createBalanceTopUp) | --- # General > Las operaciones de General de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/general Salud, la empresa de la llave y la consulta de adquirientes. Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## Salud `GET /v1/health` · `getHealth` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | ok | `GET /v1/health` (`getHealth`) #### cURL ```bash curl "https://api.trevol.si/v1/health" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` ## La empresa de la llave `GET /v1/company` · `retrieveCompany` La empresa de la llave: NIT, razón social, ambiente y numeraciones activas. Desde la fase 3 trae `capabilities`, un resumen de lo que ESTA llave puede emitir ahora: documento soporte (con su numeración), documento equivalente (con los prefijos POS), nómina (`status`: `enabled`, `testing`, `not_registered` o `not_enabled`, según el ambiente de la llave) y eventos RADIAN (representante legal). Es informativo: si una lectura falla, esa clave no aparece. **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | La empresa, su ambiente y sus numeraciones activas ([Company](/docs/api/objetos#Company)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/company` (`retrieveCompany`) #### cURL ```bash curl "https://api.trevol.si/v1/company" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveCompany) ## Datos del adquiriente en la DIAN `GET /v1/dian/acquirer` · `retrieveDianAcquirer` **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `document_type` | consulta | texto | sí | | | `document_number` | consulta | texto | sí | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {found, name, email} ([Acquirer](/docs/api/objetos#Acquirer)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/dian/acquirer` (`retrieveDianAcquirer`) #### cURL ```bash curl "https://api.trevol.si/v1/dian/acquirer?document_type=31&document_number=900373591" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveDianAcquirer) --- # Facturas > Las operaciones de Facturas de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/invoices Emitir, consultar, corregir y reenviar facturas electrónicas, incluidas la exportación, la moneda extranjera, el AIU, los mandatos, el transporte, los notarios, las divisas y el sector salud. Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## Emitir una factura `POST /v1/invoices` · `createInvoice` Valida, numera, firma y envía una factura a la DIAN. Además de la factura nacional acepta la exportación (`document_type: "02"`), la moneda extranjera (`currency`), los términos de entrega (`delivery_terms`), el AIU, los mandatos, el transporte de carga, los notarios, las divisas y el sector salud (`operation_type`). El envío físico de la mercancía va en `shipment` (no en `delivery`: ese es el estado del PDF, el ZIP y el correo en la respuesta). Las notas crédito y débito no admiten estos campos. **Respuestas.** `201` la DIAN lo validó (`status: accepted`). `202` solo en la versión 2026-10-22: la DIAN no contestó (`processing`), otra petición lo está enviando (`sending`) o, en habilitación, el set de pruebas todavía lo valida (`validating`, en las dos versiones); trae `Location` y `Retry-After`, y el resultado llega por GET y por webhook: **no crees otro documento para la misma operación**. `422` `validation_error` (se revisó a fondo ANTES de reservar: no se gastó el número, y `errors[]` dice el campo), `dian_rejected` (con `document` y las reglas traducidas; corrige con `PUT` y el mismo número), `company_not_enabled` o `numbering_unavailable` (con `reason`). `402` `insufficient_balance` (producción sin saldo: queda `pending` con su número). `409` `external_id_in_use` o `duplicate_document` (con `existing_id`). `503` `dian_unavailable` en la 2026-10-21. Pide `Idempotency-Key`: repetir la misma devuelve lo mismo sin enviar nada. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([InvoiceInput](/docs/api/objetos#InvoiceInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | | | `items` | lista de [Item](/docs/api/objetos#Item) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `document_type` | texto | | Tipo de factura (tabla 13.1.3): "01" venta (por defecto) o "02" exportación. La contingencia (03/04) todavía no está disponible en la API. | | `operation_type` | texto | | Tipo de operación (tabla 13.1.5.1): "10" estándar (por defecto), "09" AIU, "11" mandatos, "12" transporte de carga, "14" notarios, "15" compra o "16" venta de divisas, o "SS-CUFE" (sector salud, con `interoperability`). | | `currency` | [Currency](/docs/api/objetos#Currency) | | Moneda extranjera: los valores del pedido van en ESA moneda; la factura y el CUFE van en pesos (× la tasa). Sin `currency`, la factura es en pesos. | | `cop_totals` | booleano | | Con `currency`: informa la moneda y la tasa en la interoperabilidad («Exportación» / «Factura de Venta»). | | `delivery_terms` | [DeliveryTerms](/docs/api/objetos#DeliveryTerms) | | Condiciones de entrega de la exportación (tabla 13.3.7: INCOTERM). | | `shipment` | [Shipment](/docs/api/objetos#Shipment) | | El envío físico de la mercancía (fecha, dirección y transportador). No se llama `delivery`: esa clave de la respuesta es el estado de los archivos y del correo. | | `due_diligence` | texto | | Debida diligencia (tabla 18.3: "01"–"04"). Obligatoria en la compra y venta de divisas (15/16) y prohibida en los demás tipos de operación. | | `interoperability` | [Interoperability](/docs/api/objetos#Interoperability) | | Grupo de interoperabilidad (Suplemento H). Obligatorio con `operation_type` "SS-CUFE". | **Respuestas** | Estado | Qué pasó | | --- | --- | | 201 | El documento ([Document](/docs/api/objetos#Document)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([Document](/docs/api/objetos#Document)) | | 402 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 429 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 503 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/invoices` (`createInvoice`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/invoices" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "VENTA-2026-000123", "customer": { "document_type": "31", "document_number": "900123456", "name": "COMERCIALIZADORA ANDINA SAS", "person_type": "legal", "email": "facturacion@comercializadoraandina.example", "address": "Calle 72 # 10-34, Oficina 501", "municipality_code": "11001" }, "items": [ { "code": "LIC-ANUAL", "description": "Licencia anual de software", "quantity": "2", "unit_price": "150000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "code": "SOP-MES", "description": "Soporte técnico mensual", "quantity": "1", "unit_price": "80000", "taxes": [ { "code": "01", "rate": "19" } ] } ], "payment": { "form": "credit", "term_days": 30 }, "notes": [ "Gracias por su compra" ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createInvoice&ejemplo=venta) ## Calcular una factura sin emitirla `POST /v1/invoices/preview` · `previewInvoice` **Cuerpo** ([InvoiceInput](/docs/api/objetos#InvoiceInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | | | `items` | lista de [Item](/docs/api/objetos#Item) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `document_type` | texto | | Tipo de factura (tabla 13.1.3): "01" venta (por defecto) o "02" exportación. La contingencia (03/04) todavía no está disponible en la API. | | `operation_type` | texto | | Tipo de operación (tabla 13.1.5.1): "10" estándar (por defecto), "09" AIU, "11" mandatos, "12" transporte de carga, "14" notarios, "15" compra o "16" venta de divisas, o "SS-CUFE" (sector salud, con `interoperability`). | | `currency` | [Currency](/docs/api/objetos#Currency) | | Moneda extranjera: los valores del pedido van en ESA moneda; la factura y el CUFE van en pesos (× la tasa). Sin `currency`, la factura es en pesos. | | `cop_totals` | booleano | | Con `currency`: informa la moneda y la tasa en la interoperabilidad («Exportación» / «Factura de Venta»). | | `delivery_terms` | [DeliveryTerms](/docs/api/objetos#DeliveryTerms) | | Condiciones de entrega de la exportación (tabla 13.3.7: INCOTERM). | | `shipment` | [Shipment](/docs/api/objetos#Shipment) | | El envío físico de la mercancía (fecha, dirección y transportador). No se llama `delivery`: esa clave de la respuesta es el estado de los archivos y del correo. | | `due_diligence` | texto | | Debida diligencia (tabla 18.3: "01"–"04"). Obligatoria en la compra y venta de divisas (15/16) y prohibida en los demás tipos de operación. | | `interoperability` | [Interoperability](/docs/api/objetos#Interoperability) | | Grupo de interoperabilidad (Suplemento H). Obligatorio con `operation_type` "SS-CUFE". | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | Totales ([Preview](/docs/api/objetos#Preview)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/invoices/preview` (`previewInvoice`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/invoices/preview" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "VENTA-2026-000123", "customer": { "document_type": "31", "document_number": "900123456", "name": "COMERCIALIZADORA ANDINA SAS", "person_type": "legal", "email": "facturacion@comercializadoraandina.example", "address": "Calle 72 # 10-34, Oficina 501", "municipality_code": "11001" }, "items": [ { "code": "LIC-ANUAL", "description": "Licencia anual de software", "quantity": "2", "unit_price": "150000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "code": "SOP-MES", "description": "Soporte técnico mensual", "quantity": "1", "unit_price": "80000", "taxes": [ { "code": "01", "rate": "19" } ] } ], "payment": { "form": "credit", "term_days": 30 }, "notes": [ "Gracias por su compra" ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=previewInvoice&ejemplo=venta) ## Reenviar una pendiente o en error `POST /v1/invoices/{id}/send` · `sendInvoice` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([Document](/docs/api/objetos#Document)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([Document](/docs/api/objetos#Document)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/invoices/{id}/send` (`sendInvoice`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/invoices/inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/send" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=sendInvoice) ## Listar (factura) `GET /v1/invoices` · `listInvoices` **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `limit` | consulta | entero | | | | `starting_after` | consulta | texto | | | | `status` | consulta | texto | | | | `external_id` | consulta | texto | | | | `created_from` | consulta | texto | | | | `created_to` | consulta | texto | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data, has_more, next_cursor} (objeto) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/invoices` (`listInvoices`) #### cURL ```bash curl "https://api.trevol.si/v1/invoices?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listInvoices) ## Una factura `GET /v1/invoices/{id}` · `retrieveInvoice` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([Document](/docs/api/objetos#Document)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/invoices/{id}` (`retrieveInvoice`) #### cURL ```bash curl "https://api.trevol.si/v1/invoices/inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveInvoice) ## Archivo pdf `GET /v1/invoices/{id}/pdf` · `downloadInvoicePdf` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | pdf (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/invoices/{id}/pdf` (`downloadInvoicePdf`) #### cURL ```bash curl "https://api.trevol.si/v1/invoices/inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/pdf" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o invoice_pdf.pdf ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadInvoicePdf) ## Archivo xml `GET /v1/invoices/{id}/xml` · `downloadInvoiceXml` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | xml (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/invoices/{id}/xml` (`downloadInvoiceXml`) #### cURL ```bash curl "https://api.trevol.si/v1/invoices/inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/xml" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o invoice_xml.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadInvoiceXml) ## Archivo zip `GET /v1/invoices/{id}/zip` · `downloadInvoiceZip` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | zip (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/invoices/{id}/zip` (`downloadInvoiceZip`) #### cURL ```bash curl "https://api.trevol.si/v1/invoices/inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/zip" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o invoice_zip.zip ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadInvoiceZip) ## Archivo dian_response `GET /v1/invoices/{id}/dian_response` · `downloadInvoiceDianResponse` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | dian_response (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/invoices/{id}/dian_response` (`downloadInvoiceDianResponse`) #### cURL ```bash curl "https://api.trevol.si/v1/invoices/inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/dian_response" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o invoice_dian_response.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadInvoiceDianResponse) ## ¿Procede ya la aceptación tácita (034) de esta factura? `GET /v1/invoices/{id}/tacit_acceptance` · `retrieveInvoiceTacitAcceptance` Consulta GetStatusEvent (con el certificado de la empresa) y aplica la regla de los 3 días hábiles; no envía nada. Usa el mismo historial que el envío del 034 (`POST /v1/radian_events`), así que la consulta y el envío no se contradicen. 404 si la factura no es de la empresa y el ambiente de la llave; 422 si no está validada, no es a crédito o es de consumidor final; 429 con más de 30 consultas por minuto por llave; 503 si la DIAN no contesta (reintentable). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El resultado ([TacitAcceptance](/docs/api/objetos#TacitAcceptance)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/invoices/{id}/tacit_acceptance` (`retrieveInvoiceTacitAcceptance`) #### cURL ```bash curl "https://api.trevol.si/v1/invoices/inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/tacit_acceptance" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "object": "tacit_acceptance", "invoice_id": "inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "cufe": "abababababababababababababababababababababababababababababababababababababababababababababababab", "number": "FE1234", "allowed": false, "available_from": "2026-10-27", "rules": [ { "code": "LGC14", "severity": "rejection", "local": true, "message": "Faltan días hábiles para la aceptación tácita.", "explanation": "Pasan 3 días hábiles desde el recibo del bien o servicio (evento 032)." } ] } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveInvoiceTacitAcceptance) ## Corregir una factura rechazada (mismo número) y reenviarla `PUT /v1/invoices/{id}` · `correctInvoice` **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([InvoiceInput](/docs/api/objetos#InvoiceInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | | | `items` | lista de [Item](/docs/api/objetos#Item) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `document_type` | texto | | Tipo de factura (tabla 13.1.3): "01" venta (por defecto) o "02" exportación. La contingencia (03/04) todavía no está disponible en la API. | | `operation_type` | texto | | Tipo de operación (tabla 13.1.5.1): "10" estándar (por defecto), "09" AIU, "11" mandatos, "12" transporte de carga, "14" notarios, "15" compra o "16" venta de divisas, o "SS-CUFE" (sector salud, con `interoperability`). | | `currency` | [Currency](/docs/api/objetos#Currency) | | Moneda extranjera: los valores del pedido van en ESA moneda; la factura y el CUFE van en pesos (× la tasa). Sin `currency`, la factura es en pesos. | | `cop_totals` | booleano | | Con `currency`: informa la moneda y la tasa en la interoperabilidad («Exportación» / «Factura de Venta»). | | `delivery_terms` | [DeliveryTerms](/docs/api/objetos#DeliveryTerms) | | Condiciones de entrega de la exportación (tabla 13.3.7: INCOTERM). | | `shipment` | [Shipment](/docs/api/objetos#Shipment) | | El envío físico de la mercancía (fecha, dirección y transportador). No se llama `delivery`: esa clave de la respuesta es el estado de los archivos y del correo. | | `due_diligence` | texto | | Debida diligencia (tabla 18.3: "01"–"04"). Obligatoria en la compra y venta de divisas (15/16) y prohibida en los demás tipos de operación. | | `interoperability` | [Interoperability](/docs/api/objetos#Interoperability) | | Grupo de interoperabilidad (Suplemento H). Obligatorio con `operation_type` "SS-CUFE". | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([Document](/docs/api/objetos#Document)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([Document](/docs/api/objetos#Document)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `PUT /v1/invoices/{id}` (`correctInvoice`) #### cURL ```bash curl -X PUT "https://api.trevol.si/v1/invoices/inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "VENTA-2026-000123", "customer": { "document_type": "31", "document_number": "900123456", "name": "COMERCIALIZADORA ANDINA SAS", "person_type": "legal", "email": "facturacion@comercializadoraandina.example", "address": "Calle 72 # 10-34, Oficina 501", "municipality_code": "11001" }, "items": [ { "code": "LIC-ANUAL", "description": "Licencia anual de software", "quantity": "2", "unit_price": "150000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "code": "SOP-MES", "description": "Soporte técnico mensual", "quantity": "1", "unit_price": "80000", "taxes": [ { "code": "01", "rate": "19" } ] } ], "payment": { "form": "credit", "term_days": 30 }, "notes": [ "Gracias por su compra" ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=correctInvoice&ejemplo=venta) --- # Notas crédito > Las operaciones de Notas crédito de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/credit-notes Notas crédito electrónicas. Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## Emitir una nota crédito `POST /v1/credit_notes` · `createCreditNote` **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([NoteInput](/docs/api/objetos#NoteInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | | | `items` | lista de [Item](/docs/api/objetos#Item) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `concept` | texto | sí | Concepto de corrección: NC 1–6, ND 1–4. | | `description` | texto | | | | `invoice` | [InvoiceReference](/docs/api/objetos#InvoiceReference) | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 201 | El documento ([Document](/docs/api/objetos#Document)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([Document](/docs/api/objetos#Document)) | | 402 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 429 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 503 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/credit_notes` (`createCreditNote`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/credit_notes" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "DEV-2026-000045", "concept": "1", "description": "Devolución de una unidad por empaque dañado", "invoice": { "id": "inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Café especial 500 g", "code": "CAF-500", "unit": "94", "quantity": "1", "unit_price": "32000", "taxes": [ { "code": "01", "rate": "19" } ] } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createCreditNote&ejemplo=devolucion) ## Calcular una nota crédito sin emitirla `POST /v1/credit_notes/preview` · `previewCreditNote` **Cuerpo** ([NoteInput](/docs/api/objetos#NoteInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | | | `items` | lista de [Item](/docs/api/objetos#Item) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `concept` | texto | sí | Concepto de corrección: NC 1–6, ND 1–4. | | `description` | texto | | | | `invoice` | [InvoiceReference](/docs/api/objetos#InvoiceReference) | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | Totales ([Preview](/docs/api/objetos#Preview)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/credit_notes/preview` (`previewCreditNote`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/credit_notes/preview" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "DEV-2026-000045", "concept": "1", "description": "Devolución de una unidad por empaque dañado", "invoice": { "id": "inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Café especial 500 g", "code": "CAF-500", "unit": "94", "quantity": "1", "unit_price": "32000", "taxes": [ { "code": "01", "rate": "19" } ] } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=previewCreditNote&ejemplo=devolucion) ## Reenviar una pendiente o en error `POST /v1/credit_notes/{id}/send` · `sendCreditNote` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([Document](/docs/api/objetos#Document)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([Document](/docs/api/objetos#Document)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/credit_notes/{id}/send` (`sendCreditNote`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/credit_notes/cn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/send" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=sendCreditNote) ## Listar (nota crédito) `GET /v1/credit_notes` · `listCreditNotes` **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `limit` | consulta | entero | | | | `starting_after` | consulta | texto | | | | `status` | consulta | texto | | | | `external_id` | consulta | texto | | | | `created_from` | consulta | texto | | | | `created_to` | consulta | texto | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data, has_more, next_cursor} (objeto) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/credit_notes` (`listCreditNotes`) #### cURL ```bash curl "https://api.trevol.si/v1/credit_notes?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listCreditNotes) ## Una nota crédito `GET /v1/credit_notes/{id}` · `retrieveCreditNote` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([Document](/docs/api/objetos#Document)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/credit_notes/{id}` (`retrieveCreditNote`) #### cURL ```bash curl "https://api.trevol.si/v1/credit_notes/cn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveCreditNote) ## Archivo pdf `GET /v1/credit_notes/{id}/pdf` · `downloadCreditNotePdf` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | pdf (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/credit_notes/{id}/pdf` (`downloadCreditNotePdf`) #### cURL ```bash curl "https://api.trevol.si/v1/credit_notes/cn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/pdf" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o credit_note_pdf.pdf ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadCreditNotePdf) ## Archivo xml `GET /v1/credit_notes/{id}/xml` · `downloadCreditNoteXml` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | xml (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/credit_notes/{id}/xml` (`downloadCreditNoteXml`) #### cURL ```bash curl "https://api.trevol.si/v1/credit_notes/cn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/xml" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o credit_note_xml.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadCreditNoteXml) ## Archivo zip `GET /v1/credit_notes/{id}/zip` · `downloadCreditNoteZip` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | zip (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/credit_notes/{id}/zip` (`downloadCreditNoteZip`) #### cURL ```bash curl "https://api.trevol.si/v1/credit_notes/cn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/zip" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o credit_note_zip.zip ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadCreditNoteZip) ## Archivo dian_response `GET /v1/credit_notes/{id}/dian_response` · `downloadCreditNoteDianResponse` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | dian_response (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/credit_notes/{id}/dian_response` (`downloadCreditNoteDianResponse`) #### cURL ```bash curl "https://api.trevol.si/v1/credit_notes/cn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/dian_response" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o credit_note_dian_response.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadCreditNoteDianResponse) ## Corregir una nota crédito rechazada (mismo número) y reenviarla `PUT /v1/credit_notes/{id}` · `correctCreditNote` **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([NoteInput](/docs/api/objetos#NoteInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | | | `items` | lista de [Item](/docs/api/objetos#Item) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `concept` | texto | sí | Concepto de corrección: NC 1–6, ND 1–4. | | `description` | texto | | | | `invoice` | [InvoiceReference](/docs/api/objetos#InvoiceReference) | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([Document](/docs/api/objetos#Document)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([Document](/docs/api/objetos#Document)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `PUT /v1/credit_notes/{id}` (`correctCreditNote`) #### cURL ```bash curl -X PUT "https://api.trevol.si/v1/credit_notes/cn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "DEV-2026-000045", "concept": "1", "description": "Devolución de una unidad por empaque dañado", "invoice": { "id": "inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Café especial 500 g", "code": "CAF-500", "unit": "94", "quantity": "1", "unit_price": "32000", "taxes": [ { "code": "01", "rate": "19" } ] } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=correctCreditNote&ejemplo=devolucion) --- # Notas débito > Las operaciones de Notas débito de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/debit-notes Notas débito electrónicas. Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## Emitir una nota débito `POST /v1/debit_notes` · `createDebitNote` **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([NoteInput](/docs/api/objetos#NoteInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | | | `items` | lista de [Item](/docs/api/objetos#Item) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `concept` | texto | sí | Concepto de corrección: NC 1–6, ND 1–4. | | `description` | texto | | | | `invoice` | [InvoiceReference](/docs/api/objetos#InvoiceReference) | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 201 | El documento ([Document](/docs/api/objetos#Document)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([Document](/docs/api/objetos#Document)) | | 402 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 429 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 503 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/debit_notes` (`createDebitNote`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/debit_notes" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "INT-2026-000012", "concept": "1", "description": "Intereses de mora de la factura de septiembre", "invoice": { "id": "inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Intereses de mora", "unit": "94", "quantity": "1", "unit_price": "45000" } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createDebitNote&ejemplo=intereses) ## Calcular una nota débito sin emitirla `POST /v1/debit_notes/preview` · `previewDebitNote` **Cuerpo** ([NoteInput](/docs/api/objetos#NoteInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | | | `items` | lista de [Item](/docs/api/objetos#Item) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `concept` | texto | sí | Concepto de corrección: NC 1–6, ND 1–4. | | `description` | texto | | | | `invoice` | [InvoiceReference](/docs/api/objetos#InvoiceReference) | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | Totales ([Preview](/docs/api/objetos#Preview)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/debit_notes/preview` (`previewDebitNote`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/debit_notes/preview" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "INT-2026-000012", "concept": "1", "description": "Intereses de mora de la factura de septiembre", "invoice": { "id": "inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Intereses de mora", "unit": "94", "quantity": "1", "unit_price": "45000" } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=previewDebitNote&ejemplo=intereses) ## Reenviar una pendiente o en error `POST /v1/debit_notes/{id}/send` · `sendDebitNote` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([Document](/docs/api/objetos#Document)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([Document](/docs/api/objetos#Document)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/debit_notes/{id}/send` (`sendDebitNote`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/debit_notes/dn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/send" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=sendDebitNote) ## Listar (nota débito) `GET /v1/debit_notes` · `listDebitNotes` **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `limit` | consulta | entero | | | | `starting_after` | consulta | texto | | | | `status` | consulta | texto | | | | `external_id` | consulta | texto | | | | `created_from` | consulta | texto | | | | `created_to` | consulta | texto | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data, has_more, next_cursor} (objeto) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/debit_notes` (`listDebitNotes`) #### cURL ```bash curl "https://api.trevol.si/v1/debit_notes?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listDebitNotes) ## Una nota débito `GET /v1/debit_notes/{id}` · `retrieveDebitNote` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([Document](/docs/api/objetos#Document)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/debit_notes/{id}` (`retrieveDebitNote`) #### cURL ```bash curl "https://api.trevol.si/v1/debit_notes/dn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveDebitNote) ## Archivo pdf `GET /v1/debit_notes/{id}/pdf` · `downloadDebitNotePdf` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | pdf (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/debit_notes/{id}/pdf` (`downloadDebitNotePdf`) #### cURL ```bash curl "https://api.trevol.si/v1/debit_notes/dn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/pdf" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o debit_note_pdf.pdf ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadDebitNotePdf) ## Archivo xml `GET /v1/debit_notes/{id}/xml` · `downloadDebitNoteXml` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | xml (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/debit_notes/{id}/xml` (`downloadDebitNoteXml`) #### cURL ```bash curl "https://api.trevol.si/v1/debit_notes/dn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/xml" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o debit_note_xml.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadDebitNoteXml) ## Archivo zip `GET /v1/debit_notes/{id}/zip` · `downloadDebitNoteZip` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | zip (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/debit_notes/{id}/zip` (`downloadDebitNoteZip`) #### cURL ```bash curl "https://api.trevol.si/v1/debit_notes/dn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/zip" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o debit_note_zip.zip ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadDebitNoteZip) ## Archivo dian_response `GET /v1/debit_notes/{id}/dian_response` · `downloadDebitNoteDianResponse` **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | dian_response (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/debit_notes/{id}/dian_response` (`downloadDebitNoteDianResponse`) #### cURL ```bash curl "https://api.trevol.si/v1/debit_notes/dn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/dian_response" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o debit_note_dian_response.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadDebitNoteDianResponse) ## Corregir una nota débito rechazada (mismo número) y reenviarla `PUT /v1/debit_notes/{id}` · `correctDebitNote` **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([NoteInput](/docs/api/objetos#NoteInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | | | `items` | lista de [Item](/docs/api/objetos#Item) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `concept` | texto | sí | Concepto de corrección: NC 1–6, ND 1–4. | | `description` | texto | | | | `invoice` | [InvoiceReference](/docs/api/objetos#InvoiceReference) | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([Document](/docs/api/objetos#Document)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([Document](/docs/api/objetos#Document)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `PUT /v1/debit_notes/{id}` (`correctDebitNote`) #### cURL ```bash curl -X PUT "https://api.trevol.si/v1/debit_notes/dn_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "INT-2026-000012", "concept": "1", "description": "Intereses de mora de la factura de septiembre", "invoice": { "id": "inv_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Intereses de mora", "unit": "94", "quantity": "1", "unit_price": "45000" } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=correctDebitNote&ejemplo=intereses) --- # Documento soporte > Las operaciones de Documento soporte de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/support-documents Documento soporte en adquisiciones a no obligados a facturar (05) y su nota de ajuste (95). Se generan, no se expiden: sin PDF, ZIP ni correo. Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## Emitir un documento soporte `POST /v1/support_documents` · `createSupportDocument` Genera un **documento soporte** (tipo 05): el respaldo de una compra a quien no está obligado a facturar (una persona natural, un campesino, un proveedor del exterior). Trevol valida a fondo ANTES de reservar un número, numera con el rango de documento soporte vigente más nuevo (o el del prefijo de `numbering`), firma con el certificado de la empresa y lo manda a la DIAN. **Se genera, no se expide**: no hay PDF, ZIP ni correo; el XML y la respuesta de la DIAN se descargan en las dos versiones. La empresa necesita su software registrado ante la DIAN (`company_not_enabled` con `reason: missing_software`) y un rango vigente con números libres (`numbering_unavailable`, `no_resolution`). La `purchase_date` de cada línea no puede ser posterior a hoy (DSFC02b/c). Cuenta como documento de facturación en el cobro. **Respuestas.** `201` la DIAN lo validó (`status: accepted`). `202` solo en la versión 2026-10-22: la DIAN no contestó (`processing`), otra petición lo está enviando (`sending`) o, en habilitación, el set de pruebas todavía lo valida (`validating`, en las dos versiones); trae `Location` y `Retry-After`, y el resultado llega por GET y por webhook: **no crees otro documento para la misma operación**. `422` `validation_error` (se revisó a fondo ANTES de reservar: no se gastó el número, y `errors[]` dice el campo), `dian_rejected` (con `document` y las reglas traducidas; corrige con `PUT` y el mismo número), `company_not_enabled` o `numbering_unavailable` (con `reason`). `402` `insufficient_balance` (producción sin saldo: queda `pending` con su número). `409` `external_id_in_use` o `duplicate_document` (con `existing_id`). `503` `dian_unavailable` en la 2026-10-21. Pide `Idempotency-Key`: repetir la misma devuelve lo mismo sin enviar nada. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([SupportDocumentInput](/docs/api/objetos#SupportDocumentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `seller` | [Seller](/docs/api/objetos#Seller) | sí | Quien vende y no está obligado a facturar (el SNO). La empresa de la llave es la compradora: sus datos salen de la empresa. No hay `customer`. | | `items` | lista de [SupportItem](/docs/api/objetos#SupportItem) | sí | Las líneas (1 a 500). Retenciones solo por línea (`withholdings`: 05 ReteIVA, 06 ReteRenta); las del documento no se publican. Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `currency` | [SupportCurrency](/docs/api/objetos#SupportCurrency) | | | | `transmission` | `per_operation` \| `weekly` | | `per_operation` (por defecto, un documento por compra) o `weekly` (acumulado semanal con un mismo vendedor: cada línea lleva su `purchase_date`). | | `adjustment_note` | [SupportAdjustmentReference](/docs/api/objetos#SupportAdjustmentReference) | | La nota de ajuste que originó este documento (opcional): {"id": "sda_…"} o {number, cuds, issue_date}. | | `numbering` | [Numbering](/docs/api/objetos#Numbering) | | Opcional: el prefijo de la resolución de documento soporte que numera (una empresa con varios rangos). Sin él, la más nueva vigente. Si no existe, `numbering_unavailable` (`no_resolution`). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 201 | El documento ([SupportDocument](/docs/api/objetos#SupportDocument)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([SupportDocument](/docs/api/objetos#SupportDocument)) | | 402 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 429 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 503 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/support_documents` (`createSupportDocument`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/support_documents" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "OC-991", "seller": { "resident": true, "person_type": "natural", "document_type": "31", "document_number": "1020304050", "name": "PEREZ GOMEZ ANA MARIA", "address": "Vereda El Silencio, finca La Esperanza", "municipality_code": "25053", "postal_code": "252007", "tax_responsibilities": [ "R-99-PN" ] }, "items": [ { "description": "Leche cruda", "unit": "LTR", "quantity": "120", "unit_price": "1800" } ], "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "notes": [ "Compra de la semana 42 de octubre" ] }' ``` #### Respuesta 201 ```json { "id": "sd_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "support_document", "environment": "test", "status": "accepted", "number": "DS994001", "prefix": "DS", "external_id": "OC-991", "origin": "api", "cuds": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe-hab.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "seller": { "resident": true, "person_type": "natural", "document_type": "31", "document_number": "1020304050", "name": "PEREZ GOMEZ ANA MARIA", "address": "Vereda El Silencio, finca La Esperanza", "municipality_code": "25053", "postal_code": "252007", "tax_responsibilities": [ "R-99-PN" ] }, "items": [ { "description": "Leche cruda", "unit": "LTR", "quantity": "120", "unit_price": "1800" } ], "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "transmission": "per_operation", "adjustment_note": null, "totals": { "subtotal": "216000.00", "discounts": "0.00", "line_extension": "216000.00", "tax_exclusive": "0.00", "taxes": [], "allowances": "0.00", "charges": "0.00", "rounding": "0.00", "total": "216000.00", "withholdings": [], "prepayments": "0.00", "net_payable": null, "items": [ { "gross": "216000.00", "discount": "0.00", "line_extension": "216000.00" } ] }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "delivery": { "status": null, "pdf": false, "zip": false, "xml": true, "email": null }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createSupportDocument&ejemplo=compra) ## Calcular un documento soporte sin emitirlo `POST /v1/support_documents/preview` · `previewSupportDocument` Valida y calcula los totales del documento soporte **sin** reservar un número ni enviar nada. No pide `Idempotency-Key` y no se cobra. **Cuerpo** ([SupportDocumentInput](/docs/api/objetos#SupportDocumentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `seller` | [Seller](/docs/api/objetos#Seller) | sí | Quien vende y no está obligado a facturar (el SNO). La empresa de la llave es la compradora: sus datos salen de la empresa. No hay `customer`. | | `items` | lista de [SupportItem](/docs/api/objetos#SupportItem) | sí | Las líneas (1 a 500). Retenciones solo por línea (`withholdings`: 05 ReteIVA, 06 ReteRenta); las del documento no se publican. Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `currency` | [SupportCurrency](/docs/api/objetos#SupportCurrency) | | | | `transmission` | `per_operation` \| `weekly` | | `per_operation` (por defecto, un documento por compra) o `weekly` (acumulado semanal con un mismo vendedor: cada línea lleva su `purchase_date`). | | `adjustment_note` | [SupportAdjustmentReference](/docs/api/objetos#SupportAdjustmentReference) | | La nota de ajuste que originó este documento (opcional): {"id": "sda_…"} o {number, cuds, issue_date}. | | `numbering` | [Numbering](/docs/api/objetos#Numbering) | | Opcional: el prefijo de la resolución de documento soporte que numera (una empresa con varios rangos). Sin él, la más nueva vigente. Si no existe, `numbering_unavailable` (`no_resolution`). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | Totales ([Preview](/docs/api/objetos#Preview)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/support_documents/preview` (`previewSupportDocument`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/support_documents/preview" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "OC-991", "seller": { "resident": true, "person_type": "natural", "document_type": "31", "document_number": "1020304050", "name": "PEREZ GOMEZ ANA MARIA", "address": "Vereda El Silencio, finca La Esperanza", "municipality_code": "25053", "postal_code": "252007", "tax_responsibilities": [ "R-99-PN" ] }, "items": [ { "description": "Leche cruda", "unit": "LTR", "quantity": "120", "unit_price": "1800" } ], "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "notes": [ "Compra de la semana 42 de octubre" ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=previewSupportDocument&ejemplo=compra) ## Reenviar una pendiente o en error `POST /v1/support_documents/{id}/send` · `sendSupportDocument` Vuelve a enviar un documento soporte `pending` o `error` (por ejemplo, después de recargar saldo). Sin cuerpo. Con un documento `accepted` responde `200` con el documento tal cual; con uno que la DIAN todavía verifica no reenvía nada (`202`); con uno `rejected` es `409 invalid_state` (se corrige con `PUT`). No pide `Idempotency-Key` (reintentarlo es seguro). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([SupportDocument](/docs/api/objetos#SupportDocument)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([SupportDocument](/docs/api/objetos#SupportDocument)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/support_documents/{id}/send` (`sendSupportDocument`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/support_documents/sd_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/send" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "id": "sd_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "support_document", "environment": "test", "status": "accepted", "number": "DS994001", "prefix": "DS", "external_id": "OC-991", "origin": "api", "cuds": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe-hab.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "seller": { "resident": true, "person_type": "natural", "document_type": "31", "document_number": "1020304050", "name": "PEREZ GOMEZ ANA MARIA", "address": "Vereda El Silencio, finca La Esperanza", "municipality_code": "25053", "postal_code": "252007", "tax_responsibilities": [ "R-99-PN" ] }, "items": [ { "description": "Leche cruda", "unit": "LTR", "quantity": "120", "unit_price": "1800" } ], "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "transmission": "per_operation", "adjustment_note": null, "totals": { "subtotal": "216000.00", "discounts": "0.00", "line_extension": "216000.00", "tax_exclusive": "0.00", "taxes": [], "allowances": "0.00", "charges": "0.00", "rounding": "0.00", "total": "216000.00", "withholdings": [], "prepayments": "0.00", "net_payable": null, "items": [ { "gross": "216000.00", "discount": "0.00", "line_extension": "216000.00" } ] }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "delivery": { "status": null, "pdf": false, "zip": false, "xml": true, "email": null }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=sendSupportDocument) ## Listar (documento soporte) `GET /v1/support_documents` · `listSupportDocuments` Paginación por cursor (`limit` hasta 100, `starting_after`) y filtros `status`, `external_id`, `created_from` y `created_to`. `status=error` trae también los que están en verificación (`processing`). **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `limit` | consulta | entero | | | | `starting_after` | consulta | texto | | | | `status` | consulta | texto | | | | `external_id` | consulta | texto | | | | `created_from` | consulta | texto | | | | `created_to` | consulta | texto | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data, has_more, next_cursor} (objeto) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/support_documents` (`listSupportDocuments`) #### cURL ```bash curl "https://api.trevol.si/v1/support_documents?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listSupportDocuments) ## Un documento soporte `GET /v1/support_documents/{id}` · `retrieveSupportDocument` El documento soporte tal como se emitió: lo que mandaste, los totales, el CUDS y la respuesta de la DIAN. `delivery` no aplica (`status: null`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([SupportDocument](/docs/api/objetos#SupportDocument)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/support_documents/{id}` (`retrieveSupportDocument`) #### cURL ```bash curl "https://api.trevol.si/v1/support_documents/sd_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "id": "sd_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "support_document", "environment": "test", "status": "accepted", "number": "DS994001", "prefix": "DS", "external_id": "OC-991", "origin": "api", "cuds": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe-hab.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "seller": { "resident": true, "person_type": "natural", "document_type": "31", "document_number": "1020304050", "name": "PEREZ GOMEZ ANA MARIA", "address": "Vereda El Silencio, finca La Esperanza", "municipality_code": "25053", "postal_code": "252007", "tax_responsibilities": [ "R-99-PN" ] }, "items": [ { "description": "Leche cruda", "unit": "LTR", "quantity": "120", "unit_price": "1800" } ], "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "transmission": "per_operation", "adjustment_note": null, "totals": { "subtotal": "216000.00", "discounts": "0.00", "line_extension": "216000.00", "tax_exclusive": "0.00", "taxes": [], "allowances": "0.00", "charges": "0.00", "rounding": "0.00", "total": "216000.00", "withholdings": [], "prepayments": "0.00", "net_payable": null, "items": [ { "gross": "216000.00", "discount": "0.00", "line_extension": "216000.00" } ] }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "delivery": { "status": null, "pdf": false, "zip": false, "xml": true, "email": null }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveSupportDocument) ## Archivo xml `GET /v1/support_documents/{id}/xml` · `downloadSupportDocumentXml` El XML firmado que se mandó a la DIAN (`application/xml`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | xml (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/support_documents/{id}/xml` (`downloadSupportDocumentXml`) #### cURL ```bash curl "https://api.trevol.si/v1/support_documents/sd_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/xml" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o support_document_xml.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadSupportDocumentXml) ## Archivo dian_response `GET /v1/support_documents/{id}/dian_response` · `downloadSupportDocumentDianResponse` El `ApplicationResponse` con que la DIAN contestó (`application/xml`), en las dos versiones. **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | dian_response (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/support_documents/{id}/dian_response` (`downloadSupportDocumentDianResponse`) #### cURL ```bash curl "https://api.trevol.si/v1/support_documents/sd_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/dian_response" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o support_document_dian_response.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadSupportDocumentDianResponse) ## Corregir un documento soporte rechazado (mismo número) y reenviarlo `PUT /v1/support_documents/{id}` · `correctSupportDocument` Corrige un documento soporte rechazado con el **mismo número** y lo vuelve a enviar. Solo si el documento está `rejected`, `error` o `pending`. Uno `accepted` no se corrige (se ajusta con una nota) y uno que la DIAN todavía verifica es `409 invalid_state`: espera el resultado. El `external_id` y la numeración no se cambian. Pide `Idempotency-Key`. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([SupportDocumentInput](/docs/api/objetos#SupportDocumentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `seller` | [Seller](/docs/api/objetos#Seller) | sí | Quien vende y no está obligado a facturar (el SNO). La empresa de la llave es la compradora: sus datos salen de la empresa. No hay `customer`. | | `items` | lista de [SupportItem](/docs/api/objetos#SupportItem) | sí | Las líneas (1 a 500). Retenciones solo por línea (`withholdings`: 05 ReteIVA, 06 ReteRenta); las del documento no se publican. Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `currency` | [SupportCurrency](/docs/api/objetos#SupportCurrency) | | | | `transmission` | `per_operation` \| `weekly` | | `per_operation` (por defecto, un documento por compra) o `weekly` (acumulado semanal con un mismo vendedor: cada línea lleva su `purchase_date`). | | `adjustment_note` | [SupportAdjustmentReference](/docs/api/objetos#SupportAdjustmentReference) | | La nota de ajuste que originó este documento (opcional): {"id": "sda_…"} o {number, cuds, issue_date}. | | `numbering` | [Numbering](/docs/api/objetos#Numbering) | | Opcional: el prefijo de la resolución de documento soporte que numera (una empresa con varios rangos). Sin él, la más nueva vigente. Si no existe, `numbering_unavailable` (`no_resolution`). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([SupportDocument](/docs/api/objetos#SupportDocument)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([SupportDocument](/docs/api/objetos#SupportDocument)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `PUT /v1/support_documents/{id}` (`correctSupportDocument`) #### cURL ```bash curl -X PUT "https://api.trevol.si/v1/support_documents/sd_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "OC-991", "seller": { "resident": true, "person_type": "natural", "document_type": "31", "document_number": "1020304050", "name": "PEREZ GOMEZ ANA MARIA", "address": "Vereda El Silencio, finca La Esperanza", "municipality_code": "25053", "postal_code": "252007", "tax_responsibilities": [ "R-99-PN" ] }, "items": [ { "description": "Leche cruda", "unit": "LTR", "quantity": "120", "unit_price": "1800" } ], "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "notes": [ "Compra de la semana 42 de octubre" ] }' ``` #### Respuesta 200 ```json { "id": "sd_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "support_document", "environment": "test", "status": "accepted", "number": "DS994001", "prefix": "DS", "external_id": "OC-991", "origin": "api", "cuds": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe-hab.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "seller": { "resident": true, "person_type": "natural", "document_type": "31", "document_number": "1020304050", "name": "PEREZ GOMEZ ANA MARIA", "address": "Vereda El Silencio, finca La Esperanza", "municipality_code": "25053", "postal_code": "252007", "tax_responsibilities": [ "R-99-PN" ] }, "items": [ { "description": "Leche cruda", "unit": "LTR", "quantity": "120", "unit_price": "1800" } ], "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "transmission": "per_operation", "adjustment_note": null, "totals": { "subtotal": "216000.00", "discounts": "0.00", "line_extension": "216000.00", "tax_exclusive": "0.00", "taxes": [], "allowances": "0.00", "charges": "0.00", "rounding": "0.00", "total": "216000.00", "withholdings": [], "prepayments": "0.00", "net_payable": null, "items": [ { "gross": "216000.00", "discount": "0.00", "line_extension": "216000.00" } ] }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "delivery": { "status": null, "pdf": false, "zip": false, "xml": true, "email": null }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=correctSupportDocument&ejemplo=compra) --- # Notas de ajuste del documento soporte > Las operaciones de Notas de ajuste del documento soporte de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/support-document-adjustments Documento soporte en adquisiciones a no obligados a facturar (05) y su nota de ajuste (95). Se generan, no se expiden: sin PDF, ZIP ni correo. Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## Emitir una nota de ajuste del documento soporte `POST /v1/support_document_adjustments` · `createSupportDocumentAdjustment` Genera la **nota de ajuste de un documento soporte** (95): corrige o anula una compra ya validada. El documento que ajusta es de Trevol (`support_document.id`) o de otra plataforma (`number`, `cuds`, `issue_date` y `total`). `concept` 1 a 5; el 2 anula y lleva exactamente los valores del documento. Sin `seller` hereda el del documento de Trevol. La suma de las notas no puede pasar del saldo del documento. Un documento soporte rechazado no se ajusta: se corrige con `PUT`. **Respuestas.** `201` la DIAN lo validó (`status: accepted`). `202` solo en la versión 2026-10-22: la DIAN no contestó (`processing`), otra petición lo está enviando (`sending`) o, en habilitación, el set de pruebas todavía lo valida (`validating`, en las dos versiones); trae `Location` y `Retry-After`, y el resultado llega por GET y por webhook: **no crees otro documento para la misma operación**. `422` `validation_error` (se revisó a fondo ANTES de reservar: no se gastó el número, y `errors[]` dice el campo), `dian_rejected` (con `document` y las reglas traducidas; corrige con `PUT` y el mismo número), `company_not_enabled` o `numbering_unavailable` (con `reason`). `402` `insufficient_balance` (producción sin saldo: queda `pending` con su número). `409` `external_id_in_use` o `duplicate_document` (con `existing_id`). `503` `dian_unavailable` en la 2026-10-21. Pide `Idempotency-Key`: repetir la misma devuelve lo mismo sin enviar nada. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([SupportDocumentAdjustmentInput](/docs/api/objetos#SupportDocumentAdjustmentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `seller` | [Seller](/docs/api/objetos#Seller) | | Opcional con `support_document.id`: se toma el vendedor del documento soporte (si viene, tiene que ser el mismo). | | `items` | lista de [SupportAdjustmentItem](/docs/api/objetos#SupportAdjustmentItem) | sí | Las líneas de la nota (1 a 500): sin retenciones ni fecha de compra. Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `currency` | [SupportCurrency](/docs/api/objetos#SupportCurrency) | | | | `concept` | texto | sí | Concepto de la nota de ajuste (tabla 16.2.4): "1" devolución parcial, "2" anulación del documento soporte (lleva exactamente sus valores), "3" rebaja o descuento, "4" ajuste de precio, "5" otros. | | `description` | texto | sí | Qué se corrige y por qué (de 20 a 5000 caracteres, NSBF04). | | `support_document` | [SupportDocumentReference](/docs/api/objetos#SupportDocumentReference) | sí | El documento soporte que ajusta: {"id": "sd_…"} (de Trevol: validado, de la empresa y el ambiente de la llave) o {number, cuds, issue_date, total?} si es de otra plataforma (`total` hace falta para anularlo). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 201 | El documento ([SupportDocumentAdjustment](/docs/api/objetos#SupportDocumentAdjustment)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([SupportDocumentAdjustment](/docs/api/objetos#SupportDocumentAdjustment)) | | 402 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 429 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 503 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/support_document_adjustments` (`createSupportDocumentAdjustment`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/support_document_adjustments" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "OC-991-ANULA", "concept": "2", "description": "Anulación: la compra de leche se registró dos veces en el ERP", "support_document": { "id": "sd_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Leche cruda", "unit": "LTR", "quantity": "120", "unit_price": "1800" } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createSupportDocumentAdjustment&ejemplo=anulacion) ## Calcular una nota de ajuste del documento soporte sin emitirla `POST /v1/support_document_adjustments/preview` · `previewSupportDocumentAdjustment` Valida y calcula la nota de ajuste **sin** reservar un número ni enviar nada. **Cuerpo** ([SupportDocumentAdjustmentInput](/docs/api/objetos#SupportDocumentAdjustmentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `seller` | [Seller](/docs/api/objetos#Seller) | | Opcional con `support_document.id`: se toma el vendedor del documento soporte (si viene, tiene que ser el mismo). | | `items` | lista de [SupportAdjustmentItem](/docs/api/objetos#SupportAdjustmentItem) | sí | Las líneas de la nota (1 a 500): sin retenciones ni fecha de compra. Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `currency` | [SupportCurrency](/docs/api/objetos#SupportCurrency) | | | | `concept` | texto | sí | Concepto de la nota de ajuste (tabla 16.2.4): "1" devolución parcial, "2" anulación del documento soporte (lleva exactamente sus valores), "3" rebaja o descuento, "4" ajuste de precio, "5" otros. | | `description` | texto | sí | Qué se corrige y por qué (de 20 a 5000 caracteres, NSBF04). | | `support_document` | [SupportDocumentReference](/docs/api/objetos#SupportDocumentReference) | sí | El documento soporte que ajusta: {"id": "sd_…"} (de Trevol: validado, de la empresa y el ambiente de la llave) o {number, cuds, issue_date, total?} si es de otra plataforma (`total` hace falta para anularlo). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | Totales ([Preview](/docs/api/objetos#Preview)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/support_document_adjustments/preview` (`previewSupportDocumentAdjustment`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/support_document_adjustments/preview" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "OC-991-ANULA", "concept": "2", "description": "Anulación: la compra de leche se registró dos veces en el ERP", "support_document": { "id": "sd_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Leche cruda", "unit": "LTR", "quantity": "120", "unit_price": "1800" } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=previewSupportDocumentAdjustment&ejemplo=anulacion) ## Reenviar una pendiente o en error `POST /v1/support_document_adjustments/{id}/send` · `sendSupportDocumentAdjustment` Vuelve a enviar una nota de ajuste `pending` o `error`. Sin cuerpo. Con un documento `accepted` responde `200` con el documento tal cual; con uno que la DIAN todavía verifica no reenvía nada (`202`); con uno `rejected` es `409 invalid_state` (se corrige con `PUT`). No pide `Idempotency-Key` (reintentarlo es seguro). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([SupportDocumentAdjustment](/docs/api/objetos#SupportDocumentAdjustment)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([SupportDocumentAdjustment](/docs/api/objetos#SupportDocumentAdjustment)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/support_document_adjustments/{id}/send` (`sendSupportDocumentAdjustment`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/support_document_adjustments/sda_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/send" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=sendSupportDocumentAdjustment) ## Listar (nota de ajuste del documento soporte) `GET /v1/support_document_adjustments` · `listSupportDocumentAdjustments` Paginación por cursor (`limit` hasta 100, `starting_after`) y filtros `status`, `external_id`, `created_from` y `created_to`. `status=error` trae también los que están en verificación (`processing`). **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `limit` | consulta | entero | | | | `starting_after` | consulta | texto | | | | `status` | consulta | texto | | | | `external_id` | consulta | texto | | | | `created_from` | consulta | texto | | | | `created_to` | consulta | texto | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data, has_more, next_cursor} (objeto) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/support_document_adjustments` (`listSupportDocumentAdjustments`) #### cURL ```bash curl "https://api.trevol.si/v1/support_document_adjustments?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listSupportDocumentAdjustments) ## Una nota de ajuste del documento soporte `GET /v1/support_document_adjustments/{id}` · `retrieveSupportDocumentAdjustment` La nota de ajuste del documento soporte, con `support_document` (el documento que ajusta). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([SupportDocumentAdjustment](/docs/api/objetos#SupportDocumentAdjustment)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/support_document_adjustments/{id}` (`retrieveSupportDocumentAdjustment`) #### cURL ```bash curl "https://api.trevol.si/v1/support_document_adjustments/sda_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveSupportDocumentAdjustment) ## Archivo xml `GET /v1/support_document_adjustments/{id}/xml` · `downloadSupportDocumentAdjustmentXml` El XML firmado que se mandó a la DIAN (`application/xml`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | xml (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/support_document_adjustments/{id}/xml` (`downloadSupportDocumentAdjustmentXml`) #### cURL ```bash curl "https://api.trevol.si/v1/support_document_adjustments/sda_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/xml" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o support_document_adjustment_xml.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadSupportDocumentAdjustmentXml) ## Archivo dian_response `GET /v1/support_document_adjustments/{id}/dian_response` · `downloadSupportDocumentAdjustmentDianResponse` El `ApplicationResponse` con que la DIAN contestó (`application/xml`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | dian_response (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/support_document_adjustments/{id}/dian_response` (`downloadSupportDocumentAdjustmentDianResponse`) #### cURL ```bash curl "https://api.trevol.si/v1/support_document_adjustments/sda_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/dian_response" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o support_document_adjustment_dian_response.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadSupportDocumentAdjustmentDianResponse) ## Corregir una nota de ajuste del documento soporte rechazada (mismo número) y reenviarla `PUT /v1/support_document_adjustments/{id}` · `correctSupportDocumentAdjustment` Corrige una nota de ajuste rechazada con el **mismo número** y la vuelve a enviar. Solo si el documento está `rejected`, `error` o `pending`. Uno `accepted` no se corrige (se ajusta con una nota) y uno que la DIAN todavía verifica es `409 invalid_state`: espera el resultado. El `external_id` y la numeración no se cambian. Pide `Idempotency-Key`. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([SupportDocumentAdjustmentInput](/docs/api/objetos#SupportDocumentAdjustmentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `seller` | [Seller](/docs/api/objetos#Seller) | | Opcional con `support_document.id`: se toma el vendedor del documento soporte (si viene, tiene que ser el mismo). | | `items` | lista de [SupportAdjustmentItem](/docs/api/objetos#SupportAdjustmentItem) | sí | Las líneas de la nota (1 a 500): sin retenciones ni fecha de compra. Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `currency` | [SupportCurrency](/docs/api/objetos#SupportCurrency) | | | | `concept` | texto | sí | Concepto de la nota de ajuste (tabla 16.2.4): "1" devolución parcial, "2" anulación del documento soporte (lleva exactamente sus valores), "3" rebaja o descuento, "4" ajuste de precio, "5" otros. | | `description` | texto | sí | Qué se corrige y por qué (de 20 a 5000 caracteres, NSBF04). | | `support_document` | [SupportDocumentReference](/docs/api/objetos#SupportDocumentReference) | sí | El documento soporte que ajusta: {"id": "sd_…"} (de Trevol: validado, de la empresa y el ambiente de la llave) o {number, cuds, issue_date, total?} si es de otra plataforma (`total` hace falta para anularlo). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([SupportDocumentAdjustment](/docs/api/objetos#SupportDocumentAdjustment)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([SupportDocumentAdjustment](/docs/api/objetos#SupportDocumentAdjustment)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `PUT /v1/support_document_adjustments/{id}` (`correctSupportDocumentAdjustment`) #### cURL ```bash curl -X PUT "https://api.trevol.si/v1/support_document_adjustments/sda_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "OC-991-ANULA", "concept": "2", "description": "Anulación: la compra de leche se registró dos veces en el ERP", "support_document": { "id": "sd_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Leche cruda", "unit": "LTR", "quantity": "120", "unit_price": "1800" } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=correctSupportDocumentAdjustment&ejemplo=anulacion) --- # Documento equivalente > Las operaciones de Documento equivalente de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/equivalent-documents Documento equivalente electrónico (tiquete POS 20 y los tipos 25, 27, 30, 35, 40, 45, 50, 55 y 60) y su nota de ajuste (crédito 94 y débito 93). Se expiden como la factura: PDF, ZIP y correo. Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## Emitir un documento equivalente `POST /v1/equivalent_documents` · `createEquivalentDocument` Genera un **documento equivalente electrónico**: el tiquete POS (20, por defecto) o cualquiera de los otros nueve tipos de la tabla 16.3 (`document_type`: 25 cine, 27 espectáculos, 30 juegos, 35 transporte, 40 peajes, 45 extracto, 50 tiquete aéreo, 55 bolsa de valores, 60 servicios públicos; cada uno con su información propia en `extension`). El POS lleva su caja en `pos.register` y, si la empresa tiene varias cajas, el prefijo de su resolución en `numbering.prefix`. Se expide como la factura: PDF, ZIP y correo después del 201 (espera `equivalent_document.delivered` o consulta `delivery.status`). La contingencia 07 (documento físico) se pide con `contingency`; la 08 (la DIAN no contestó) la decide Trevol y responde `202` con `status: contingency`. En habilitación, el set de pruebas por tipo es automático: mientras la DIAN lo valida responde `202 validating` en las dos versiones y reintentar no vuelve a mandarlo. Cómo se calcula el CUDE del POS depende del despliegue del servicio, no de la empresa. **Respuestas.** `201` la DIAN lo validó (`status: accepted`). `202` solo en la versión 2026-10-22: la DIAN no contestó (`processing`), otra petición lo está enviando (`sending`) o, en habilitación, el set de pruebas todavía lo valida (`validating`, en las dos versiones); trae `Location` y `Retry-After`, y el resultado llega por GET y por webhook: **no crees otro documento para la misma operación**. `422` `validation_error` (se revisó a fondo ANTES de reservar: no se gastó el número, y `errors[]` dice el campo), `dian_rejected` (con `document` y las reglas traducidas; corrige con `PUT` y el mismo número), `company_not_enabled` o `numbering_unavailable` (con `reason`). `402` `insufficient_balance` (producción sin saldo: queda `pending` con su número). `409` `external_id_in_use` o `duplicate_document` (con `existing_id`). `503` `dian_unavailable` en la 2026-10-21. Pide `Idempotency-Key`: repetir la misma devuelve lo mismo sin enviar nada. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([EquivalentDocumentInput](/docs/api/objetos#EquivalentDocumentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | El adquirente. Para consumidor final: {"final_consumer": true}. La relación de juegos (30) no lleva `customer`: el adquirente es la DIAN. | | `items` | lista de [EquivalentItem](/docs/api/objetos#EquivalentItem) | sí | Las líneas (1 a 500). Retenciones solo como autorretenciones del emisor (`self_withholding: true`). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `document_type` | texto | | Tipo de documento equivalente (tabla 16.3): "20" tiquete POS (por defecto), "25" boleta de cine, "27" espectáculos públicos, "30" juegos localizados y no localizados (se acepta "32"), "35" transporte de pasajeros, "40" peajes, "45" extracto, "50" tiquete aéreo, "55" bolsa de valores o "60" servicios públicos. | | `operation_type` | texto | | Tipo de operación (CustomizationID, tabla 16.4.1) del tipo de documento. Por defecto, el primero del tipo ("10" en el POS). | | `pos` | [Pos](/docs/api/objetos#Pos) | | Obligatorio en el tiquete POS (20) y prohibido en los demás: la caja que expide el tiquete y, opcional, los beneficios del comprador. | | `extension` | [Extension](/docs/api/objetos#Extension) | | La información propia del tipo, con UNA clave, la de su `document_type`: `cinema` (25), `show` (27), `games` (30), `transport` (35), `air` (50), `stock_exchange` (55) o `public_services` (60). Prohibida en el POS (20), peajes (40) y extracto (45). | | `prefix` | texto | | Solo en los tipos sin resolución (todos menos el POS): el prefijo del consecutivo propio (1 a 4 letras o dígitos). Por defecto, el del tipo (DEC, DEEP, DEJL, DETT, DEP, EXT, DETA, DEBV, SPD). | | `origin_note` | [OriginNote](/docs/api/objetos#OriginNote) | | La nota de ajuste que dio origen a este documento (opcional). | | `contingency` | [Contingency](/docs/api/objetos#Contingency) | | Solo la contingencia 07 (el emisor transcribe un documento físico): `physical_document` con number, date y type. La 08 (la DIAN no contestó) no se pide: la decide Trevol y el documento responde `202` con `status: contingency`. | | `numbering` | [Numbering](/docs/api/objetos#Numbering) | | Solo en el POS: el prefijo de la resolución POS que numera (una empresa con varias cajas o establecimientos tiene varias). Sin él, la más nueva vigente. Si no existe, `numbering_unavailable` (`no_resolution`). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 201 | El documento ([EquivalentDocument](/docs/api/objetos#EquivalentDocument)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([EquivalentDocument](/docs/api/objetos#EquivalentDocument)) | | 402 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 429 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 503 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/equivalent_documents` (`createEquivalentDocument`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/equivalent_documents" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "TIQ-1", "customer": { "final_consumer": true }, "pos": { "register": { "plate": "CJ-01", "location": "Local 1, Jamundí", "cashier": "PEREZ ANA", "type": "Caja principal" } }, "numbering": { "prefix": "POS2" }, "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "items": [ { "description": "Gaseosa 400 ml", "code": "GAS-1", "unit": "94", "quantity": "2", "unit_price": "3000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "description": "Almuerzo", "unit": "94", "quantity": "1", "unit_price": "18000" } ] }' ``` #### Respuesta 201 ```json { "id": "pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "equivalent_document", "environment": "test", "status": "accepted", "number": "POS2994001", "prefix": "POS2", "external_id": "TIQ-1", "origin": "api", "cude": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe-hab.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "customer": { "final_consumer": true }, "items": [ { "description": "Gaseosa 400 ml", "code": "GAS-1", "unit": "94", "quantity": "2", "unit_price": "3000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "description": "Almuerzo", "unit": "94", "quantity": "1", "unit_price": "18000" } ], "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "document_type": "20", "operation_type": "22", "pos": { "register": { "plate": "CJ-01", "location": "Local 1, Jamundí", "cashier": "PEREZ ANA", "type": "Caja principal" } }, "contingency": null, "totals": { "subtotal": "24000.00", "discounts": "0.00", "line_extension": "24000.00", "tax_exclusive": "6000.00", "taxes": [ { "code": "01", "name": "IVA", "rate": "19.00", "base": "6000.00", "amount": "1140.00" } ], "allowances": "0.00", "charges": "0.00", "rounding": "0.00", "total": "25140.00", "withholdings": [], "prepayments": "0.00", "net_payable": null, "items": [ { "gross": "6000.00", "discount": "0.00", "line_extension": "6000.00" }, { "gross": "18000.00", "discount": "0.00", "line_extension": "18000.00" } ] }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "delivery": { "status": "pending", "pdf": false, "zip": false, "xml": true, "email": { "status": "pending", "to": null, "detail": null } }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createEquivalentDocument&ejemplo=pos) ## Calcular un documento equivalente sin emitirlo `POST /v1/equivalent_documents/preview` · `previewEquivalentDocument` Valida y calcula los totales **sin** reservar un número ni enviar nada. No pide `Idempotency-Key`. **Cuerpo** ([EquivalentDocumentInput](/docs/api/objetos#EquivalentDocumentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | El adquirente. Para consumidor final: {"final_consumer": true}. La relación de juegos (30) no lleva `customer`: el adquirente es la DIAN. | | `items` | lista de [EquivalentItem](/docs/api/objetos#EquivalentItem) | sí | Las líneas (1 a 500). Retenciones solo como autorretenciones del emisor (`self_withholding: true`). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `document_type` | texto | | Tipo de documento equivalente (tabla 16.3): "20" tiquete POS (por defecto), "25" boleta de cine, "27" espectáculos públicos, "30" juegos localizados y no localizados (se acepta "32"), "35" transporte de pasajeros, "40" peajes, "45" extracto, "50" tiquete aéreo, "55" bolsa de valores o "60" servicios públicos. | | `operation_type` | texto | | Tipo de operación (CustomizationID, tabla 16.4.1) del tipo de documento. Por defecto, el primero del tipo ("10" en el POS). | | `pos` | [Pos](/docs/api/objetos#Pos) | | Obligatorio en el tiquete POS (20) y prohibido en los demás: la caja que expide el tiquete y, opcional, los beneficios del comprador. | | `extension` | [Extension](/docs/api/objetos#Extension) | | La información propia del tipo, con UNA clave, la de su `document_type`: `cinema` (25), `show` (27), `games` (30), `transport` (35), `air` (50), `stock_exchange` (55) o `public_services` (60). Prohibida en el POS (20), peajes (40) y extracto (45). | | `prefix` | texto | | Solo en los tipos sin resolución (todos menos el POS): el prefijo del consecutivo propio (1 a 4 letras o dígitos). Por defecto, el del tipo (DEC, DEEP, DEJL, DETT, DEP, EXT, DETA, DEBV, SPD). | | `origin_note` | [OriginNote](/docs/api/objetos#OriginNote) | | La nota de ajuste que dio origen a este documento (opcional). | | `contingency` | [Contingency](/docs/api/objetos#Contingency) | | Solo la contingencia 07 (el emisor transcribe un documento físico): `physical_document` con number, date y type. La 08 (la DIAN no contestó) no se pide: la decide Trevol y el documento responde `202` con `status: contingency`. | | `numbering` | [Numbering](/docs/api/objetos#Numbering) | | Solo en el POS: el prefijo de la resolución POS que numera (una empresa con varias cajas o establecimientos tiene varias). Sin él, la más nueva vigente. Si no existe, `numbering_unavailable` (`no_resolution`). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | Totales ([Preview](/docs/api/objetos#Preview)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/equivalent_documents/preview` (`previewEquivalentDocument`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/equivalent_documents/preview" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "TIQ-1", "customer": { "final_consumer": true }, "pos": { "register": { "plate": "CJ-01", "location": "Local 1, Jamundí", "cashier": "PEREZ ANA", "type": "Caja principal" } }, "numbering": { "prefix": "POS2" }, "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "items": [ { "description": "Gaseosa 400 ml", "code": "GAS-1", "unit": "94", "quantity": "2", "unit_price": "3000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "description": "Almuerzo", "unit": "94", "quantity": "1", "unit_price": "18000" } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=previewEquivalentDocument&ejemplo=pos) ## Reenviar una pendiente o en error `POST /v1/equivalent_documents/{id}/send` · `sendEquivalentDocument` Vuelve a enviar un documento equivalente `pending` o `error`. En contingencia devuelve el documento (lo reenvía Trevol). Con el set de pruebas en curso (`validating`) solo consulta: no lo vuelve a mandar. Sin cuerpo. Con un documento `accepted` responde `200` con el documento tal cual; con uno que la DIAN todavía verifica no reenvía nada (`202`); con uno `rejected` es `409 invalid_state` (se corrige con `PUT`). No pide `Idempotency-Key` (reintentarlo es seguro). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([EquivalentDocument](/docs/api/objetos#EquivalentDocument)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([EquivalentDocument](/docs/api/objetos#EquivalentDocument)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/equivalent_documents/{id}/send` (`sendEquivalentDocument`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/equivalent_documents/pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/send" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "id": "pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "equivalent_document", "environment": "test", "status": "accepted", "number": "POS2994001", "prefix": "POS2", "external_id": "TIQ-1", "origin": "api", "cude": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe-hab.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "customer": { "final_consumer": true }, "items": [ { "description": "Gaseosa 400 ml", "code": "GAS-1", "unit": "94", "quantity": "2", "unit_price": "3000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "description": "Almuerzo", "unit": "94", "quantity": "1", "unit_price": "18000" } ], "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "document_type": "20", "operation_type": "22", "pos": { "register": { "plate": "CJ-01", "location": "Local 1, Jamundí", "cashier": "PEREZ ANA", "type": "Caja principal" } }, "contingency": null, "totals": { "subtotal": "24000.00", "discounts": "0.00", "line_extension": "24000.00", "tax_exclusive": "6000.00", "taxes": [ { "code": "01", "name": "IVA", "rate": "19.00", "base": "6000.00", "amount": "1140.00" } ], "allowances": "0.00", "charges": "0.00", "rounding": "0.00", "total": "25140.00", "withholdings": [], "prepayments": "0.00", "net_payable": null, "items": [ { "gross": "6000.00", "discount": "0.00", "line_extension": "6000.00" }, { "gross": "18000.00", "discount": "0.00", "line_extension": "18000.00" } ] }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "delivery": { "status": "pending", "pdf": false, "zip": false, "xml": true, "email": { "status": "pending", "to": null, "detail": null } }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=sendEquivalentDocument) ## Listar (documento equivalente) `GET /v1/equivalent_documents` · `listEquivalentDocuments` Paginación por cursor (`limit` hasta 100, `starting_after`) y filtros `status`, `external_id`, `created_from` y `created_to`. `status=error` trae también los que están en verificación (`processing`). Además, `document_type` filtra por tipo. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `limit` | consulta | entero | | | | `starting_after` | consulta | texto | | | | `status` | consulta | texto | | | | `external_id` | consulta | texto | | | | `created_from` | consulta | texto | | | | `created_to` | consulta | texto | | | | `document_type` | consulta | `20` \| `25` \| `27` \| `30` \| `35` \| `40` \| `45` \| `50` \| `55` \| `60` | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data, has_more, next_cursor} (objeto) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/equivalent_documents` (`listEquivalentDocuments`) #### cURL ```bash curl "https://api.trevol.si/v1/equivalent_documents?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listEquivalentDocuments) ## Un documento equivalente `GET /v1/equivalent_documents/{id}` · `retrieveEquivalentDocument` El documento equivalente tal como se emitió. `contingency` dice si salió en contingencia (07 u 08) y, en la 08, cuándo se mandó el respaldo POS. **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([EquivalentDocument](/docs/api/objetos#EquivalentDocument)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/equivalent_documents/{id}` (`retrieveEquivalentDocument`) #### cURL ```bash curl "https://api.trevol.si/v1/equivalent_documents/pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "id": "pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "equivalent_document", "environment": "test", "status": "accepted", "number": "POS2994001", "prefix": "POS2", "external_id": "TIQ-1", "origin": "api", "cude": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe-hab.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "customer": { "final_consumer": true }, "items": [ { "description": "Gaseosa 400 ml", "code": "GAS-1", "unit": "94", "quantity": "2", "unit_price": "3000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "description": "Almuerzo", "unit": "94", "quantity": "1", "unit_price": "18000" } ], "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "document_type": "20", "operation_type": "22", "pos": { "register": { "plate": "CJ-01", "location": "Local 1, Jamundí", "cashier": "PEREZ ANA", "type": "Caja principal" } }, "contingency": null, "totals": { "subtotal": "24000.00", "discounts": "0.00", "line_extension": "24000.00", "tax_exclusive": "6000.00", "taxes": [ { "code": "01", "name": "IVA", "rate": "19.00", "base": "6000.00", "amount": "1140.00" } ], "allowances": "0.00", "charges": "0.00", "rounding": "0.00", "total": "25140.00", "withholdings": [], "prepayments": "0.00", "net_payable": null, "items": [ { "gross": "6000.00", "discount": "0.00", "line_extension": "6000.00" }, { "gross": "18000.00", "discount": "0.00", "line_extension": "18000.00" } ] }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "delivery": { "status": "pending", "pdf": false, "zip": false, "xml": true, "email": { "status": "pending", "to": null, "detail": null } }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveEquivalentDocument) ## Archivo pdf `GET /v1/equivalent_documents/{id}/pdf` · `downloadEquivalentDocumentPdf` La representación gráfica (`application/pdf`). Si todavía no se hizo, se genera en el momento. **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | pdf (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/equivalent_documents/{id}/pdf` (`downloadEquivalentDocumentPdf`) #### cURL ```bash curl "https://api.trevol.si/v1/equivalent_documents/pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/pdf" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o equivalent_document_pdf.pdf ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadEquivalentDocumentPdf) ## Archivo xml `GET /v1/equivalent_documents/{id}/xml` · `downloadEquivalentDocumentXml` El XML firmado que se mandó a la DIAN (`application/xml`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | xml (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/equivalent_documents/{id}/xml` (`downloadEquivalentDocumentXml`) #### cURL ```bash curl "https://api.trevol.si/v1/equivalent_documents/pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/xml" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o equivalent_document_xml.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadEquivalentDocumentXml) ## Archivo zip `GET /v1/equivalent_documents/{id}/zip` · `downloadEquivalentDocumentZip` El ZIP de entrega con el XML firmado y el PDF (`application/zip`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | zip (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/equivalent_documents/{id}/zip` (`downloadEquivalentDocumentZip`) #### cURL ```bash curl "https://api.trevol.si/v1/equivalent_documents/pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/zip" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o equivalent_document_zip.zip ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadEquivalentDocumentZip) ## Archivo dian_response `GET /v1/equivalent_documents/{id}/dian_response` · `downloadEquivalentDocumentDianResponse` El `ApplicationResponse` con que la DIAN contestó (`application/xml`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | dian_response (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/equivalent_documents/{id}/dian_response` (`downloadEquivalentDocumentDianResponse`) #### cURL ```bash curl "https://api.trevol.si/v1/equivalent_documents/pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/dian_response" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o equivalent_document_dian_response.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadEquivalentDocumentDianResponse) ## Corregir un documento equivalente rechazado (mismo número) y reenviarlo `PUT /v1/equivalent_documents/{id}` · `correctEquivalentDocument` Corrige un documento equivalente rechazado con el **mismo número** y lo vuelve a enviar. Con el set de pruebas en curso responde `409 invalid_state`. Solo si el documento está `rejected`, `error` o `pending`. Uno `accepted` no se corrige (se ajusta con una nota) y uno que la DIAN todavía verifica es `409 invalid_state`: espera el resultado. El `external_id` y la numeración no se cambian. Pide `Idempotency-Key`. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([EquivalentDocumentInput](/docs/api/objetos#EquivalentDocumentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | El adquirente. Para consumidor final: {"final_consumer": true}. La relación de juegos (30) no lleva `customer`: el adquirente es la DIAN. | | `items` | lista de [EquivalentItem](/docs/api/objetos#EquivalentItem) | sí | Las líneas (1 a 500). Retenciones solo como autorretenciones del emisor (`self_withholding: true`). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `document_type` | texto | | Tipo de documento equivalente (tabla 16.3): "20" tiquete POS (por defecto), "25" boleta de cine, "27" espectáculos públicos, "30" juegos localizados y no localizados (se acepta "32"), "35" transporte de pasajeros, "40" peajes, "45" extracto, "50" tiquete aéreo, "55" bolsa de valores o "60" servicios públicos. | | `operation_type` | texto | | Tipo de operación (CustomizationID, tabla 16.4.1) del tipo de documento. Por defecto, el primero del tipo ("10" en el POS). | | `pos` | [Pos](/docs/api/objetos#Pos) | | Obligatorio en el tiquete POS (20) y prohibido en los demás: la caja que expide el tiquete y, opcional, los beneficios del comprador. | | `extension` | [Extension](/docs/api/objetos#Extension) | | La información propia del tipo, con UNA clave, la de su `document_type`: `cinema` (25), `show` (27), `games` (30), `transport` (35), `air` (50), `stock_exchange` (55) o `public_services` (60). Prohibida en el POS (20), peajes (40) y extracto (45). | | `prefix` | texto | | Solo en los tipos sin resolución (todos menos el POS): el prefijo del consecutivo propio (1 a 4 letras o dígitos). Por defecto, el del tipo (DEC, DEEP, DEJL, DETT, DEP, EXT, DETA, DEBV, SPD). | | `origin_note` | [OriginNote](/docs/api/objetos#OriginNote) | | La nota de ajuste que dio origen a este documento (opcional). | | `contingency` | [Contingency](/docs/api/objetos#Contingency) | | Solo la contingencia 07 (el emisor transcribe un documento físico): `physical_document` con number, date y type. La 08 (la DIAN no contestó) no se pide: la decide Trevol y el documento responde `202` con `status: contingency`. | | `numbering` | [Numbering](/docs/api/objetos#Numbering) | | Solo en el POS: el prefijo de la resolución POS que numera (una empresa con varias cajas o establecimientos tiene varias). Sin él, la más nueva vigente. Si no existe, `numbering_unavailable` (`no_resolution`). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([EquivalentDocument](/docs/api/objetos#EquivalentDocument)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([EquivalentDocument](/docs/api/objetos#EquivalentDocument)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `PUT /v1/equivalent_documents/{id}` (`correctEquivalentDocument`) #### cURL ```bash curl -X PUT "https://api.trevol.si/v1/equivalent_documents/pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "TIQ-1", "customer": { "final_consumer": true }, "pos": { "register": { "plate": "CJ-01", "location": "Local 1, Jamundí", "cashier": "PEREZ ANA", "type": "Caja principal" } }, "numbering": { "prefix": "POS2" }, "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "items": [ { "description": "Gaseosa 400 ml", "code": "GAS-1", "unit": "94", "quantity": "2", "unit_price": "3000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "description": "Almuerzo", "unit": "94", "quantity": "1", "unit_price": "18000" } ] }' ``` #### Respuesta 200 ```json { "id": "pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "equivalent_document", "environment": "test", "status": "accepted", "number": "POS2994001", "prefix": "POS2", "external_id": "TIQ-1", "origin": "api", "cude": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe-hab.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "customer": { "final_consumer": true }, "items": [ { "description": "Gaseosa 400 ml", "code": "GAS-1", "unit": "94", "quantity": "2", "unit_price": "3000", "taxes": [ { "code": "01", "rate": "19" } ] }, { "description": "Almuerzo", "unit": "94", "quantity": "1", "unit_price": "18000" } ], "payment": { "form": "cash", "methods": [ { "code": "10" } ] }, "document_type": "20", "operation_type": "22", "pos": { "register": { "plate": "CJ-01", "location": "Local 1, Jamundí", "cashier": "PEREZ ANA", "type": "Caja principal" } }, "contingency": null, "totals": { "subtotal": "24000.00", "discounts": "0.00", "line_extension": "24000.00", "tax_exclusive": "6000.00", "taxes": [ { "code": "01", "name": "IVA", "rate": "19.00", "base": "6000.00", "amount": "1140.00" } ], "allowances": "0.00", "charges": "0.00", "rounding": "0.00", "total": "25140.00", "withholdings": [], "prepayments": "0.00", "net_payable": null, "items": [ { "gross": "6000.00", "discount": "0.00", "line_extension": "6000.00" }, { "gross": "18000.00", "discount": "0.00", "line_extension": "18000.00" } ] }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "delivery": { "status": "pending", "pdf": false, "zip": false, "xml": true, "email": { "status": "pending", "to": null, "detail": null } }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=correctEquivalentDocument&ejemplo=pos) --- # Notas de ajuste del documento equivalente > Las operaciones de Notas de ajuste del documento equivalente de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/equivalent-document-adjustments Documento equivalente electrónico (tiquete POS 20 y los tipos 25, 27, 30, 35, 40, 45, 50, 55 y 60) y su nota de ajuste (crédito 94 y débito 93). Se expiden como la factura: PDF, ZIP y correo. Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## Emitir una nota de ajuste del documento equivalente `POST /v1/equivalent_document_adjustments` · `createEquivalentDocumentAdjustment` Genera la **nota de ajuste de un documento equivalente**: crédito (94) o débito (93) según `note_type`. El documento que ajusta es de Trevol (`equivalent_document.id`) o de otra plataforma. `concept` 1 a 5 (el 2 anula y lleva exactamente los valores del documento). Sin `customer` toma el del documento; en juegos el adquirente es la DIAN. Se expide como el documento: PDF, ZIP y correo. **Respuestas.** `201` la DIAN lo validó (`status: accepted`). `202` solo en la versión 2026-10-22: la DIAN no contestó (`processing`), otra petición lo está enviando (`sending`) o, en habilitación, el set de pruebas todavía lo valida (`validating`, en las dos versiones); trae `Location` y `Retry-After`, y el resultado llega por GET y por webhook: **no crees otro documento para la misma operación**. `422` `validation_error` (se revisó a fondo ANTES de reservar: no se gastó el número, y `errors[]` dice el campo), `dian_rejected` (con `document` y las reglas traducidas; corrige con `PUT` y el mismo número), `company_not_enabled` o `numbering_unavailable` (con `reason`). `402` `insufficient_balance` (producción sin saldo: queda `pending` con su número). `409` `external_id_in_use` o `duplicate_document` (con `existing_id`). `503` `dian_unavailable` en la 2026-10-21. Pide `Idempotency-Key`: repetir la misma devuelve lo mismo sin enviar nada. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([EquivalentDocumentAdjustmentInput](/docs/api/objetos#EquivalentDocumentAdjustmentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | Opcional con `equivalent_document.id`: se toma el adquirente del documento (si viene, tiene que ser el mismo). | | `items` | lista de [EquivalentAdjustmentItem](/docs/api/objetos#EquivalentAdjustmentItem) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `note_type` | `credit` \| `debit` | sí | "credit" (nota de ajuste crédito, 94) o "debit" (débito, 93). | | `concept` | texto | sí | Concepto de la nota (tabla 16.6): "1" devolución parcial, "2" anulación del documento (lleva exactamente sus valores), "3" rebaja o descuento, "4" ajuste de precio, "5" otros. | | `description` | texto | | Opcional (20 a 5000 caracteres). Sin ella, Trevol pone el texto de la tabla (el EXACTO en la nota débito: NADBF04). | | `equivalent_document` | [EquivalentDocumentReference](/docs/api/objetos#EquivalentDocumentReference) | sí | El documento que ajusta: {"id": "pos_…"} (de Trevol: validado, de la empresa y el ambiente de la llave) o {number, cude, issue_date, document_type, total?} si es de otra plataforma (`total` hace falta para anularlo). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 201 | El documento ([EquivalentDocumentAdjustment](/docs/api/objetos#EquivalentDocumentAdjustment)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([EquivalentDocumentAdjustment](/docs/api/objetos#EquivalentDocumentAdjustment)) | | 402 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 429 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 503 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/equivalent_document_adjustments` (`createEquivalentDocumentAdjustment`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/equivalent_document_adjustments" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "TIQ-1-DEV", "note_type": "credit", "concept": "1", "equivalent_document": { "id": "pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Devolución gaseosa", "unit": "94", "quantity": "1", "unit_price": "3000", "taxes": [ { "code": "01", "rate": "19" } ] } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createEquivalentDocumentAdjustment&ejemplo=devolucion) ## Calcular una nota de ajuste del documento equivalente sin emitirla `POST /v1/equivalent_document_adjustments/preview` · `previewEquivalentDocumentAdjustment` Valida y calcula la nota de ajuste **sin** reservar un número ni enviar nada. **Cuerpo** ([EquivalentDocumentAdjustmentInput](/docs/api/objetos#EquivalentDocumentAdjustmentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | Opcional con `equivalent_document.id`: se toma el adquirente del documento (si viene, tiene que ser el mismo). | | `items` | lista de [EquivalentAdjustmentItem](/docs/api/objetos#EquivalentAdjustmentItem) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `note_type` | `credit` \| `debit` | sí | "credit" (nota de ajuste crédito, 94) o "debit" (débito, 93). | | `concept` | texto | sí | Concepto de la nota (tabla 16.6): "1" devolución parcial, "2" anulación del documento (lleva exactamente sus valores), "3" rebaja o descuento, "4" ajuste de precio, "5" otros. | | `description` | texto | | Opcional (20 a 5000 caracteres). Sin ella, Trevol pone el texto de la tabla (el EXACTO en la nota débito: NADBF04). | | `equivalent_document` | [EquivalentDocumentReference](/docs/api/objetos#EquivalentDocumentReference) | sí | El documento que ajusta: {"id": "pos_…"} (de Trevol: validado, de la empresa y el ambiente de la llave) o {number, cude, issue_date, document_type, total?} si es de otra plataforma (`total` hace falta para anularlo). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | Totales ([Preview](/docs/api/objetos#Preview)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/equivalent_document_adjustments/preview` (`previewEquivalentDocumentAdjustment`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/equivalent_document_adjustments/preview" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "TIQ-1-DEV", "note_type": "credit", "concept": "1", "equivalent_document": { "id": "pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Devolución gaseosa", "unit": "94", "quantity": "1", "unit_price": "3000", "taxes": [ { "code": "01", "rate": "19" } ] } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=previewEquivalentDocumentAdjustment&ejemplo=devolucion) ## Reenviar una pendiente o en error `POST /v1/equivalent_document_adjustments/{id}/send` · `sendEquivalentDocumentAdjustment` Vuelve a enviar una nota de ajuste `pending` o `error`. Sin cuerpo. Con un documento `accepted` responde `200` con el documento tal cual; con uno que la DIAN todavía verifica no reenvía nada (`202`); con uno `rejected` es `409 invalid_state` (se corrige con `PUT`). No pide `Idempotency-Key` (reintentarlo es seguro). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([EquivalentDocumentAdjustment](/docs/api/objetos#EquivalentDocumentAdjustment)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([EquivalentDocumentAdjustment](/docs/api/objetos#EquivalentDocumentAdjustment)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/equivalent_document_adjustments/{id}/send` (`sendEquivalentDocumentAdjustment`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/equivalent_document_adjustments/posa_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/send" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=sendEquivalentDocumentAdjustment) ## Listar (nota de ajuste del documento equivalente) `GET /v1/equivalent_document_adjustments` · `listEquivalentDocumentAdjustments` Paginación por cursor (`limit` hasta 100, `starting_after`) y filtros `status`, `external_id`, `created_from` y `created_to`. `status=error` trae también los que están en verificación (`processing`). **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `limit` | consulta | entero | | | | `starting_after` | consulta | texto | | | | `status` | consulta | texto | | | | `external_id` | consulta | texto | | | | `created_from` | consulta | texto | | | | `created_to` | consulta | texto | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data, has_more, next_cursor} (objeto) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/equivalent_document_adjustments` (`listEquivalentDocumentAdjustments`) #### cURL ```bash curl "https://api.trevol.si/v1/equivalent_document_adjustments?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listEquivalentDocumentAdjustments) ## Una nota de ajuste del documento equivalente `GET /v1/equivalent_document_adjustments/{id}` · `retrieveEquivalentDocumentAdjustment` La nota de ajuste del documento equivalente, con `equivalent_document` (el documento que ajusta). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([EquivalentDocumentAdjustment](/docs/api/objetos#EquivalentDocumentAdjustment)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/equivalent_document_adjustments/{id}` (`retrieveEquivalentDocumentAdjustment`) #### cURL ```bash curl "https://api.trevol.si/v1/equivalent_document_adjustments/posa_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveEquivalentDocumentAdjustment) ## Archivo pdf `GET /v1/equivalent_document_adjustments/{id}/pdf` · `downloadEquivalentDocumentAdjustmentPdf` La representación gráfica (`application/pdf`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | pdf (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/equivalent_document_adjustments/{id}/pdf` (`downloadEquivalentDocumentAdjustmentPdf`) #### cURL ```bash curl "https://api.trevol.si/v1/equivalent_document_adjustments/posa_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/pdf" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o equivalent_document_adjustment_pdf.pdf ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadEquivalentDocumentAdjustmentPdf) ## Archivo xml `GET /v1/equivalent_document_adjustments/{id}/xml` · `downloadEquivalentDocumentAdjustmentXml` El XML firmado que se mandó a la DIAN (`application/xml`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | xml (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/equivalent_document_adjustments/{id}/xml` (`downloadEquivalentDocumentAdjustmentXml`) #### cURL ```bash curl "https://api.trevol.si/v1/equivalent_document_adjustments/posa_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/xml" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o equivalent_document_adjustment_xml.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadEquivalentDocumentAdjustmentXml) ## Archivo zip `GET /v1/equivalent_document_adjustments/{id}/zip` · `downloadEquivalentDocumentAdjustmentZip` El ZIP de entrega (`application/zip`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | zip (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/equivalent_document_adjustments/{id}/zip` (`downloadEquivalentDocumentAdjustmentZip`) #### cURL ```bash curl "https://api.trevol.si/v1/equivalent_document_adjustments/posa_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/zip" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o equivalent_document_adjustment_zip.zip ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadEquivalentDocumentAdjustmentZip) ## Archivo dian_response `GET /v1/equivalent_document_adjustments/{id}/dian_response` · `downloadEquivalentDocumentAdjustmentDianResponse` El `ApplicationResponse` con que la DIAN contestó (`application/xml`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | dian_response (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/equivalent_document_adjustments/{id}/dian_response` (`downloadEquivalentDocumentAdjustmentDianResponse`) #### cURL ```bash curl "https://api.trevol.si/v1/equivalent_document_adjustments/posa_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/dian_response" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o equivalent_document_adjustment_dian_response.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadEquivalentDocumentAdjustmentDianResponse) ## Corregir una nota de ajuste del documento equivalente rechazada (mismo número) y reenviarla `PUT /v1/equivalent_document_adjustments/{id}` · `correctEquivalentDocumentAdjustment` Corrige una nota de ajuste rechazada con el **mismo número** y la vuelve a enviar. Solo si el documento está `rejected`, `error` o `pending`. Uno `accepted` no se corrige (se ajusta con una nota) y uno que la DIAN todavía verifica es `409 invalid_state`: espera el resultado. El `external_id` y la numeración no se cambian. Pide `Idempotency-Key`. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([EquivalentDocumentAdjustmentInput](/docs/api/objetos#EquivalentDocumentAdjustmentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | Opcional con `equivalent_document.id`: se toma el adquirente del documento (si viene, tiene que ser el mismo). | | `items` | lista de [EquivalentAdjustmentItem](/docs/api/objetos#EquivalentAdjustmentItem) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `note_type` | `credit` \| `debit` | sí | "credit" (nota de ajuste crédito, 94) o "debit" (débito, 93). | | `concept` | texto | sí | Concepto de la nota (tabla 16.6): "1" devolución parcial, "2" anulación del documento (lleva exactamente sus valores), "3" rebaja o descuento, "4" ajuste de precio, "5" otros. | | `description` | texto | | Opcional (20 a 5000 caracteres). Sin ella, Trevol pone el texto de la tabla (el EXACTO en la nota débito: NADBF04). | | `equivalent_document` | [EquivalentDocumentReference](/docs/api/objetos#EquivalentDocumentReference) | sí | El documento que ajusta: {"id": "pos_…"} (de Trevol: validado, de la empresa y el ambiente de la llave) o {number, cude, issue_date, document_type, total?} si es de otra plataforma (`total` hace falta para anularlo). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([EquivalentDocumentAdjustment](/docs/api/objetos#EquivalentDocumentAdjustment)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. ([EquivalentDocumentAdjustment](/docs/api/objetos#EquivalentDocumentAdjustment)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `PUT /v1/equivalent_document_adjustments/{id}` (`correctEquivalentDocumentAdjustment`) #### cURL ```bash curl -X PUT "https://api.trevol.si/v1/equivalent_document_adjustments/posa_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "TIQ-1-DEV", "note_type": "credit", "concept": "1", "equivalent_document": { "id": "pos_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }, "items": [ { "description": "Devolución gaseosa", "unit": "94", "quantity": "1", "unit_price": "3000", "taxes": [ { "code": "01", "rate": "19" } ] } ] }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=correctEquivalentDocumentAdjustment&ejemplo=devolucion) --- # Nómina electrónica > Las operaciones de Nómina electrónica de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/payrolls Documento soporte de pago de nómina electrónica (102) y su nota de ajuste (103: Reemplazar o Eliminar). Un 102 por trabajador y por mes en producción (y en habilitación cuando la empresa ya pasó el set de pruebas). Sin PDF ni correo: `xml`, `zip` y `dian_response`. El trabajador solo sale al dueño de la llave. Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## Emitir una nómina electrónica `POST /v1/payrolls` · `createPayroll` Emite el documento soporte de pago de **nómina electrónica** (102) de un trabajador para un periodo. Trevol valida todo el contrato (periodo, trabajador, devengados y deducciones) ANTES de reservar, calcula los totales, numera (prefijo de nómina de la empresa), firma y lo manda a la DIAN. **En producción hay un solo 102 por trabajador y por mes** (art. 1.5.3.2.2): otro es `409 duplicate_document` con `existing_id`; para cambiarlo usa `POST /v1/payroll_adjustments`. La empresa debe tener la nómina habilitada ante la DIAN (`company_not_enabled`, `payroll_not_enabled`): con una llave `live`, `habilitada`; con una `test`, también el set de pruebas (`GET /v1/company` → `capabilities.payroll`). En el set de pruebas de habilitación responde `202 validating` en las dos versiones y reintentar no lo reenvía. No hay PDF ni correo: `xml`, `zip` y `dian_response`. Se cobra por empleado. Los datos del trabajador solo salen al dueño de la llave: no van en los webhooks ni en el registro de peticiones, y la respuesta guardada para la idempotencia no los copia. **Respuestas.** `201` la DIAN lo validó (`status: accepted`). `202` solo en la versión 2026-10-22: la DIAN no contestó (`processing`), otra petición lo está enviando (`sending`) o, en habilitación, el set de pruebas todavía lo valida (`validating`, en las dos versiones); trae `Location` y `Retry-After`, y el resultado llega por GET y por webhook: **no crees otro documento para la misma operación**. `422` `validation_error` (se revisó a fondo ANTES de reservar: no se gastó el número, y `errors[]` dice el campo), `dian_rejected` (con `document` y las reglas traducidas; corrige con `PUT` y el mismo número), `company_not_enabled` o `numbering_unavailable` (con `reason`). `402` `insufficient_balance` (producción sin saldo: queda `pending` con su número). `409` `external_id_in_use` o `duplicate_document` (con `existing_id`). `503` `dian_unavailable` en la 2026-10-21. Pide `Idempotency-Key`: repetir la misma devuelve lo mismo sin enviar nada. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([PayrollInput](/docs/api/objetos#PayrollInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. En las nóminas es único entre las nóminas y sus ajustes (aparte de las facturas y los eventos). | | `period` | [PayrollPeriod](/docs/api/objetos#PayrollPeriod) | sí | El periodo que se liquida y el tiempo laborado (`time_worked` se calcula si no se manda: años de 360 días y meses de 30). | | `pay_period` | texto | sí | Periodo de nómina (tabla 5.5.1): "1" semanal, "2" decenal, "3" catorcenal, "4" quincenal, "5" mensual, "6" otro. | | `currency` | texto | | Moneda (ISO 4217). Por defecto COP; con otra va `exchange_rate`. | | `exchange_rate` | decimal | | Tasa de cambio a pesos. Obligatoria con una moneda distinta de COP y prohibida con COP. | | `novelty` | [PayrollNovelty](/docs/api/objetos#PayrollNovelty) | | El CUNE del documento con la novedad contractual, si la hay. Solo en la nómina. | | `notes` | lista de texto | | Máximo 20. | | `employee` | [PayrollEmployee](/docs/api/objetos#PayrollEmployee) | sí | El trabajador. Sus datos solo salen en las respuestas a esta llave (la del empleador): nunca en webhooks, en el registro de peticiones ni en lo que se guarda de la idempotencia. | | `payment` | [PayrollPayment](/docs/api/objetos#PayrollPayment) | sí | | | `payment_dates` | lista de fecha (AAAA-MM-DD) | sí | Las fechas de pago (1 a 31). Máximo 31. | | `earnings` | [PayrollEarnings](/docs/api/objetos#PayrollEarnings) | sí | | | `deductions` | [PayrollDeductions](/docs/api/objetos#PayrollDeductions) | sí | | | `rounding` | decimal | | Redondeo hasta 2.00; no suma a los totales. | | `generation_place` | [PayrollPlace](/docs/api/objetos#PayrollPlace) | | Dónde se generó el documento. Por defecto, el municipio de la empresa. | | `employer` | [PayrollEmployer](/docs/api/objetos#PayrollEmployer) | | Solo si la empresa es persona natural: sus apellidos y nombres (si la razón social no se separa bien en «apellido1 apellido2 nombre1»). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 201 | El documento ([Payroll](/docs/api/objetos#Payroll)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. También `status: validating` (en las dos versiones): el set de pruebas de habilitación de nómina recibió el documento y todavía lo valida; reintentar con la misma Idempotency-Key, `POST …/send` o corregir responde lo mismo SIN reenviar (`PUT` en ese estado es 409 `invalid_state`). ([Payroll](/docs/api/objetos#Payroll)) | | 402 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 429 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 503 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/payrolls` (`createPayroll`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/payrolls" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "NOM-2026-09-001", "period": { "hire_date": "2024-03-01", "start": "2026-09-01", "end": "2026-09-30" }, "pay_period": "5", "notes": [ "Nómina de septiembre de 2026" ], "employee": { "type": "01", "subtype": "00", "high_risk_pension": false, "document_type": "13", "document_number": "1000000101", "first_surname": "PRUEBA", "second_surname": "NOMINA01", "first_name": "TRABAJADOR", "other_names": "DE PRUEBA", "workplace": { "country": "CO", "municipality_code": "76001", "address": "Calle 1 # 1-1" }, "integral_salary": false, "contract_type": "2", "salary": "1423500", "code": "TRAB-01" }, "payment": { "form": "1", "method": "42", "bank": "Banco de Pruebas", "account_type": "Ahorros", "account_number": "0001234501" }, "payment_dates": [ "2026-09-30" ], "earnings": { "basic": { "days": 30, "amount": "1423500" }, "transport": [ { "allowance": "200000" } ] }, "deductions": { "health": { "percent": "4", "amount": "56940" }, "pension": { "percent": "4", "amount": "56940" } } }' ``` #### Respuesta 201 ```json { "id": "pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "payroll", "environment": "live", "status": "accepted", "number": "NE15", "prefix": "NE", "external_id": "NOM-2026-09-001", "origin": "api", "cune": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "submission": "sync", "period": { "hire_date": "2024-03-01", "start": "2026-09-01", "end": "2026-09-30" }, "pay_period": "5", "currency": "COP", "notes": [ "Nómina de septiembre de 2026" ], "employee": { "type": "01", "subtype": "00", "high_risk_pension": false, "document_type": "13", "document_number": "1000000101", "first_surname": "PRUEBA", "second_surname": "NOMINA01", "first_name": "TRABAJADOR", "other_names": "DE PRUEBA", "workplace": { "country": "CO", "municipality_code": "76001", "address": "Calle 1 # 1-1" }, "integral_salary": false, "contract_type": "2", "salary": "1423500", "code": "TRAB-01" }, "payment": { "form": "1", "method": "42", "bank": "Banco de Pruebas", "account_type": "Ahorros", "account_number": "0001234501" }, "payment_dates": [ "2026-09-30" ], "earnings": { "basic": { "days": 30, "amount": "1423500" }, "transport": [ { "allowance": "200000" } ] }, "deductions": { "health": { "percent": "4", "amount": "56940" }, "pension": { "percent": "4", "amount": "56940" } }, "totals": { "earnings": "1623500.00", "deductions": "113880.00", "net": "1509620.00" }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "files": { "xml": true, "zip": true, "dian_response": true }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createPayroll&ejemplo=mensual) ## Calcular una nómina electrónica sin emitirla `POST /v1/payrolls/preview` · `previewPayroll` Valida y calcula los totales de la nómina **sin** reservar un número ni enviar nada. No pide `Idempotency-Key` y no se cobra. El trabajador que mandaste vuelve en la respuesta; Trevol no lo guarda. **Cuerpo** ([PayrollInput](/docs/api/objetos#PayrollInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. En las nóminas es único entre las nóminas y sus ajustes (aparte de las facturas y los eventos). | | `period` | [PayrollPeriod](/docs/api/objetos#PayrollPeriod) | sí | El periodo que se liquida y el tiempo laborado (`time_worked` se calcula si no se manda: años de 360 días y meses de 30). | | `pay_period` | texto | sí | Periodo de nómina (tabla 5.5.1): "1" semanal, "2" decenal, "3" catorcenal, "4" quincenal, "5" mensual, "6" otro. | | `currency` | texto | | Moneda (ISO 4217). Por defecto COP; con otra va `exchange_rate`. | | `exchange_rate` | decimal | | Tasa de cambio a pesos. Obligatoria con una moneda distinta de COP y prohibida con COP. | | `novelty` | [PayrollNovelty](/docs/api/objetos#PayrollNovelty) | | El CUNE del documento con la novedad contractual, si la hay. Solo en la nómina. | | `notes` | lista de texto | | Máximo 20. | | `employee` | [PayrollEmployee](/docs/api/objetos#PayrollEmployee) | sí | El trabajador. Sus datos solo salen en las respuestas a esta llave (la del empleador): nunca en webhooks, en el registro de peticiones ni en lo que se guarda de la idempotencia. | | `payment` | [PayrollPayment](/docs/api/objetos#PayrollPayment) | sí | | | `payment_dates` | lista de fecha (AAAA-MM-DD) | sí | Las fechas de pago (1 a 31). Máximo 31. | | `earnings` | [PayrollEarnings](/docs/api/objetos#PayrollEarnings) | sí | | | `deductions` | [PayrollDeductions](/docs/api/objetos#PayrollDeductions) | sí | | | `rounding` | decimal | | Redondeo hasta 2.00; no suma a los totales. | | `generation_place` | [PayrollPlace](/docs/api/objetos#PayrollPlace) | | Dónde se generó el documento. Por defecto, el municipio de la empresa. | | `employer` | [PayrollEmployer](/docs/api/objetos#PayrollEmployer) | | Solo si la empresa es persona natural: sus apellidos y nombres (si la razón social no se separa bien en «apellido1 apellido2 nombre1»). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | Totales ([Preview](/docs/api/objetos#Preview)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/payrolls/preview` (`previewPayroll`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/payrolls/preview" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "NOM-2026-09-001", "period": { "hire_date": "2024-03-01", "start": "2026-09-01", "end": "2026-09-30" }, "pay_period": "5", "notes": [ "Nómina de septiembre de 2026" ], "employee": { "type": "01", "subtype": "00", "high_risk_pension": false, "document_type": "13", "document_number": "1000000101", "first_surname": "PRUEBA", "second_surname": "NOMINA01", "first_name": "TRABAJADOR", "other_names": "DE PRUEBA", "workplace": { "country": "CO", "municipality_code": "76001", "address": "Calle 1 # 1-1" }, "integral_salary": false, "contract_type": "2", "salary": "1423500", "code": "TRAB-01" }, "payment": { "form": "1", "method": "42", "bank": "Banco de Pruebas", "account_type": "Ahorros", "account_number": "0001234501" }, "payment_dates": [ "2026-09-30" ], "earnings": { "basic": { "days": 30, "amount": "1423500" }, "transport": [ { "allowance": "200000" } ] }, "deductions": { "health": { "percent": "4", "amount": "56940" }, "pension": { "percent": "4", "amount": "56940" } } }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=previewPayroll&ejemplo=mensual) ## Reenviar una pendiente o en error `POST /v1/payrolls/{id}/send` · `sendPayroll` Vuelve a enviar una nómina `pending` o `error`. Si el set de pruebas la valida (`validating`), solo consulta. Sin cuerpo. Con un documento `accepted` responde `200` con el documento tal cual; con uno que la DIAN todavía verifica no reenvía nada (`202`); con uno `rejected` es `409 invalid_state` (se corrige con `PUT`). No pide `Idempotency-Key` (reintentarlo es seguro). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([Payroll](/docs/api/objetos#Payroll)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. También `status: validating` (en las dos versiones): el set de pruebas de habilitación de nómina recibió el documento y todavía lo valida; reintentar con la misma Idempotency-Key, `POST …/send` o corregir responde lo mismo SIN reenviar (`PUT` en ese estado es 409 `invalid_state`). ([Payroll](/docs/api/objetos#Payroll)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/payrolls/{id}/send` (`sendPayroll`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/payrolls/pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/send" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "id": "pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "payroll", "environment": "live", "status": "accepted", "number": "NE15", "prefix": "NE", "external_id": "NOM-2026-09-001", "origin": "api", "cune": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "submission": "sync", "period": { "hire_date": "2024-03-01", "start": "2026-09-01", "end": "2026-09-30" }, "pay_period": "5", "currency": "COP", "notes": [ "Nómina de septiembre de 2026" ], "employee": { "type": "01", "subtype": "00", "high_risk_pension": false, "document_type": "13", "document_number": "1000000101", "first_surname": "PRUEBA", "second_surname": "NOMINA01", "first_name": "TRABAJADOR", "other_names": "DE PRUEBA", "workplace": { "country": "CO", "municipality_code": "76001", "address": "Calle 1 # 1-1" }, "integral_salary": false, "contract_type": "2", "salary": "1423500", "code": "TRAB-01" }, "payment": { "form": "1", "method": "42", "bank": "Banco de Pruebas", "account_type": "Ahorros", "account_number": "0001234501" }, "payment_dates": [ "2026-09-30" ], "earnings": { "basic": { "days": 30, "amount": "1423500" }, "transport": [ { "allowance": "200000" } ] }, "deductions": { "health": { "percent": "4", "amount": "56940" }, "pension": { "percent": "4", "amount": "56940" } }, "totals": { "earnings": "1623500.00", "deductions": "113880.00", "net": "1509620.00" }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "files": { "xml": true, "zip": true, "dian_response": true }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=sendPayroll) ## Listar (nómina electrónica) `GET /v1/payrolls` · `listPayrolls` Paginación por cursor y filtros `status`, `external_id`, `created_from`, `created_to`, `employee_code` (el código interno del empleado) y `period_end_from` / `period_end_to`. **No se filtra por el documento del trabajador**: la cédula en la URL quedaría en los registros del servidor. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `limit` | consulta | entero | | | | `starting_after` | consulta | texto | | | | `status` | consulta | texto | | | | `external_id` | consulta | texto | | | | `created_from` | consulta | texto | | | | `created_to` | consulta | texto | | | | `employee_code` | consulta | texto | | | | `period_end_from` | consulta | fecha (AAAA-MM-DD) | | | | `period_end_to` | consulta | fecha (AAAA-MM-DD) | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data, has_more, next_cursor} (objeto) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/payrolls` (`listPayrolls`) #### cURL ```bash curl "https://api.trevol.si/v1/payrolls?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listPayrolls) ## Una nómina electrónica `GET /v1/payrolls/{id}` · `retrievePayroll` La nómina tal como se emitió, con el trabajador, los devengados, las deducciones y los totales (`earnings`, `deductions`, `net`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([Payroll](/docs/api/objetos#Payroll)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/payrolls/{id}` (`retrievePayroll`) #### cURL ```bash curl "https://api.trevol.si/v1/payrolls/pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "id": "pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "payroll", "environment": "live", "status": "accepted", "number": "NE15", "prefix": "NE", "external_id": "NOM-2026-09-001", "origin": "api", "cune": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "submission": "sync", "period": { "hire_date": "2024-03-01", "start": "2026-09-01", "end": "2026-09-30" }, "pay_period": "5", "currency": "COP", "notes": [ "Nómina de septiembre de 2026" ], "employee": { "type": "01", "subtype": "00", "high_risk_pension": false, "document_type": "13", "document_number": "1000000101", "first_surname": "PRUEBA", "second_surname": "NOMINA01", "first_name": "TRABAJADOR", "other_names": "DE PRUEBA", "workplace": { "country": "CO", "municipality_code": "76001", "address": "Calle 1 # 1-1" }, "integral_salary": false, "contract_type": "2", "salary": "1423500", "code": "TRAB-01" }, "payment": { "form": "1", "method": "42", "bank": "Banco de Pruebas", "account_type": "Ahorros", "account_number": "0001234501" }, "payment_dates": [ "2026-09-30" ], "earnings": { "basic": { "days": 30, "amount": "1423500" }, "transport": [ { "allowance": "200000" } ] }, "deductions": { "health": { "percent": "4", "amount": "56940" }, "pension": { "percent": "4", "amount": "56940" } }, "totals": { "earnings": "1623500.00", "deductions": "113880.00", "net": "1509620.00" }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "files": { "xml": true, "zip": true, "dian_response": true }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrievePayroll) ## Archivo xml `GET /v1/payrolls/{id}/xml` · `downloadPayrollXml` El XML firmado que se mandó a la DIAN (`application/xml`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | xml (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/payrolls/{id}/xml` (`downloadPayrollXml`) #### cURL ```bash curl "https://api.trevol.si/v1/payrolls/pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/xml" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o payroll_xml.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadPayrollXml) ## Archivo zip `GET /v1/payrolls/{id}/zip` · `downloadPayrollZip` El ZIP del documento (`application/zip`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | zip (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/payrolls/{id}/zip` (`downloadPayrollZip`) #### cURL ```bash curl "https://api.trevol.si/v1/payrolls/pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/zip" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o payroll_zip.zip ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadPayrollZip) ## Archivo dian_response `GET /v1/payrolls/{id}/dian_response` · `downloadPayrollDianResponse` El `ApplicationResponse` con que la DIAN contestó (`application/xml`), si la DIAN lo devolvió. **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | dian_response (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/payrolls/{id}/dian_response` (`downloadPayrollDianResponse`) #### cURL ```bash curl "https://api.trevol.si/v1/payrolls/pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/dian_response" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o payroll_dian_response.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadPayrollDianResponse) ## Corregir una nómina electrónica rechazada (mismo número) y reenviarla `PUT /v1/payrolls/{id}` · `correctPayroll` Corrige una nómina rechazada con el **mismo número** y la vuelve a enviar. `409 invalid_state` si el set de pruebas la está validando. Solo si el documento está `rejected`, `error` o `pending`. Uno `accepted` no se corrige (se ajusta con una nota) y uno que la DIAN todavía verifica es `409 invalid_state`: espera el resultado. El `external_id` y la numeración no se cambian. Pide `Idempotency-Key`. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([PayrollInput](/docs/api/objetos#PayrollInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. En las nóminas es único entre las nóminas y sus ajustes (aparte de las facturas y los eventos). | | `period` | [PayrollPeriod](/docs/api/objetos#PayrollPeriod) | sí | El periodo que se liquida y el tiempo laborado (`time_worked` se calcula si no se manda: años de 360 días y meses de 30). | | `pay_period` | texto | sí | Periodo de nómina (tabla 5.5.1): "1" semanal, "2" decenal, "3" catorcenal, "4" quincenal, "5" mensual, "6" otro. | | `currency` | texto | | Moneda (ISO 4217). Por defecto COP; con otra va `exchange_rate`. | | `exchange_rate` | decimal | | Tasa de cambio a pesos. Obligatoria con una moneda distinta de COP y prohibida con COP. | | `novelty` | [PayrollNovelty](/docs/api/objetos#PayrollNovelty) | | El CUNE del documento con la novedad contractual, si la hay. Solo en la nómina. | | `notes` | lista de texto | | Máximo 20. | | `employee` | [PayrollEmployee](/docs/api/objetos#PayrollEmployee) | sí | El trabajador. Sus datos solo salen en las respuestas a esta llave (la del empleador): nunca en webhooks, en el registro de peticiones ni en lo que se guarda de la idempotencia. | | `payment` | [PayrollPayment](/docs/api/objetos#PayrollPayment) | sí | | | `payment_dates` | lista de fecha (AAAA-MM-DD) | sí | Las fechas de pago (1 a 31). Máximo 31. | | `earnings` | [PayrollEarnings](/docs/api/objetos#PayrollEarnings) | sí | | | `deductions` | [PayrollDeductions](/docs/api/objetos#PayrollDeductions) | sí | | | `rounding` | decimal | | Redondeo hasta 2.00; no suma a los totales. | | `generation_place` | [PayrollPlace](/docs/api/objetos#PayrollPlace) | | Dónde se generó el documento. Por defecto, el municipio de la empresa. | | `employer` | [PayrollEmployer](/docs/api/objetos#PayrollEmployer) | | Solo si la empresa es persona natural: sus apellidos y nombres (si la razón social no se separa bien en «apellido1 apellido2 nombre1»). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([Payroll](/docs/api/objetos#Payroll)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. También `status: validating` (en las dos versiones): el set de pruebas de habilitación de nómina recibió el documento y todavía lo valida; reintentar con la misma Idempotency-Key, `POST …/send` o corregir responde lo mismo SIN reenviar (`PUT` en ese estado es 409 `invalid_state`). ([Payroll](/docs/api/objetos#Payroll)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `PUT /v1/payrolls/{id}` (`correctPayroll`) #### cURL ```bash curl -X PUT "https://api.trevol.si/v1/payrolls/pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "NOM-2026-09-001", "period": { "hire_date": "2024-03-01", "start": "2026-09-01", "end": "2026-09-30" }, "pay_period": "5", "notes": [ "Nómina de septiembre de 2026" ], "employee": { "type": "01", "subtype": "00", "high_risk_pension": false, "document_type": "13", "document_number": "1000000101", "first_surname": "PRUEBA", "second_surname": "NOMINA01", "first_name": "TRABAJADOR", "other_names": "DE PRUEBA", "workplace": { "country": "CO", "municipality_code": "76001", "address": "Calle 1 # 1-1" }, "integral_salary": false, "contract_type": "2", "salary": "1423500", "code": "TRAB-01" }, "payment": { "form": "1", "method": "42", "bank": "Banco de Pruebas", "account_type": "Ahorros", "account_number": "0001234501" }, "payment_dates": [ "2026-09-30" ], "earnings": { "basic": { "days": 30, "amount": "1423500" }, "transport": [ { "allowance": "200000" } ] }, "deductions": { "health": { "percent": "4", "amount": "56940" }, "pension": { "percent": "4", "amount": "56940" } } }' ``` #### Respuesta 200 ```json { "id": "pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "payroll", "environment": "live", "status": "accepted", "number": "NE15", "prefix": "NE", "external_id": "NOM-2026-09-001", "origin": "api", "cune": "abababababababababababababababababababababababababababababababababababababababababababababababab", "qr": "https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey=abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "submission": "sync", "period": { "hire_date": "2024-03-01", "start": "2026-09-01", "end": "2026-09-30" }, "pay_period": "5", "currency": "COP", "notes": [ "Nómina de septiembre de 2026" ], "employee": { "type": "01", "subtype": "00", "high_risk_pension": false, "document_type": "13", "document_number": "1000000101", "first_surname": "PRUEBA", "second_surname": "NOMINA01", "first_name": "TRABAJADOR", "other_names": "DE PRUEBA", "workplace": { "country": "CO", "municipality_code": "76001", "address": "Calle 1 # 1-1" }, "integral_salary": false, "contract_type": "2", "salary": "1423500", "code": "TRAB-01" }, "payment": { "form": "1", "method": "42", "bank": "Banco de Pruebas", "account_type": "Ahorros", "account_number": "0001234501" }, "payment_dates": [ "2026-09-30" ], "earnings": { "basic": { "days": 30, "amount": "1423500" }, "transport": [ { "allowance": "200000" } ] }, "deductions": { "health": { "percent": "4", "amount": "56940" }, "pension": { "percent": "4", "amount": "56940" } }, "totals": { "earnings": "1623500.00", "deductions": "113880.00", "net": "1509620.00" }, "dian": { "status_code": "00", "message": "Procesado Correctamente.", "rules": [] }, "files": { "xml": true, "zip": true, "dian_response": true }, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=correctPayroll&ejemplo=mensual) --- # Notas de ajuste de nómina > Las operaciones de Notas de ajuste de nómina de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/payroll-adjustments Documento soporte de pago de nómina electrónica (102) y su nota de ajuste (103: Reemplazar o Eliminar). Un 102 por trabajador y por mes en producción (y en habilitación cuando la empresa ya pasó el set de pruebas). Sin PDF ni correo: `xml`, `zip` y `dian_response`. El trabajador solo sale al dueño de la llave. Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## Emitir una nota de ajuste de nómina `POST /v1/payroll_adjustments` · `createPayrollAdjustment` Emite la **nota de ajuste de nómina electrónica** (103). `type: "replace"` (Reemplazar) lleva la nómina completa corregida; `type: "delete"` (Eliminar) solo el predecesor y las notas. El `predecessor` es una nómina de Trevol (`pr_…` o `pra_…`, validada) o una de otra plataforma (`number`, `cune`, `issue_date`); es de la misma empresa y ambiente de la llave, del mismo trabajador, y el último de su cadena. Una nómina eliminada no se ajusta. **Respuestas.** `201` la DIAN lo validó (`status: accepted`). `202` solo en la versión 2026-10-22: la DIAN no contestó (`processing`), otra petición lo está enviando (`sending`) o, en habilitación, el set de pruebas todavía lo valida (`validating`, en las dos versiones); trae `Location` y `Retry-After`, y el resultado llega por GET y por webhook: **no crees otro documento para la misma operación**. `422` `validation_error` (se revisó a fondo ANTES de reservar: no se gastó el número, y `errors[]` dice el campo), `dian_rejected` (con `document` y las reglas traducidas; corrige con `PUT` y el mismo número), `company_not_enabled` o `numbering_unavailable` (con `reason`). `402` `insufficient_balance` (producción sin saldo: queda `pending` con su número). `409` `external_id_in_use` o `duplicate_document` (con `existing_id`). `503` `dian_unavailable` en la 2026-10-21. Pide `Idempotency-Key`: repetir la misma devuelve lo mismo sin enviar nada. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([PayrollAdjustmentInput](/docs/api/objetos#PayrollAdjustmentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. En las nóminas es único entre las nóminas y sus ajustes (aparte de las facturas y los eventos). | | `type` | `replace` \| `delete` | sí | "replace" (Reemplazar: el ajuste lleva todos los datos de la nómina menos `novelty`) o "delete" (Eliminar: solo `predecessor`, `notes`, `generation_place` y `employer`). | | `predecessor` | [PayrollReference](/docs/api/objetos#PayrollReference) | sí | El documento que se ajusta: {"id": "pr_…"} (una nómina de Trevol) o {"id": "pra_…"} (otro ajuste, el último de la cadena), o sus datos {number, cune, issue_date}. | | `period` | [PayrollPeriod](/docs/api/objetos#PayrollPeriod) | | | | `pay_period` | texto | | | | `currency` | texto | | | | `exchange_rate` | decimal | | Número o texto decimal con punto («150000.50»). | | `notes` | lista de texto | | Máximo 20. | | `employee` | [PayrollEmployee](/docs/api/objetos#PayrollEmployee) | | | | `payment` | [PayrollPayment](/docs/api/objetos#PayrollPayment) | | | | `payment_dates` | lista de fecha (AAAA-MM-DD) | | Máximo 31. | | `earnings` | [PayrollEarnings](/docs/api/objetos#PayrollEarnings) | | | | `deductions` | [PayrollDeductions](/docs/api/objetos#PayrollDeductions) | | | | `rounding` | decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `generation_place` | [PayrollPlace](/docs/api/objetos#PayrollPlace) | | | | `employer` | [PayrollEmployer](/docs/api/objetos#PayrollEmployer) | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 201 | El documento ([PayrollAdjustment](/docs/api/objetos#PayrollAdjustment)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. También `status: validating` (en las dos versiones): el set de pruebas de habilitación de nómina recibió el documento y todavía lo valida; reintentar con la misma Idempotency-Key, `POST …/send` o corregir responde lo mismo SIN reenviar (`PUT` en ese estado es 409 `invalid_state`). ([PayrollAdjustment](/docs/api/objetos#PayrollAdjustment)) | | 402 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 429 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 503 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/payroll_adjustments` (`createPayrollAdjustment`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/payroll_adjustments" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "NOM-2026-09-001-R", "period": { "hire_date": "2024-03-01", "start": "2026-09-01", "end": "2026-09-30" }, "pay_period": "5", "notes": [ "Nómina de septiembre de 2026" ], "employee": { "type": "01", "subtype": "00", "high_risk_pension": false, "document_type": "13", "document_number": "1000000101", "first_surname": "PRUEBA", "second_surname": "NOMINA01", "first_name": "TRABAJADOR", "other_names": "DE PRUEBA", "workplace": { "country": "CO", "municipality_code": "76001", "address": "Calle 1 # 1-1" }, "integral_salary": false, "contract_type": "2", "salary": "1423500", "code": "TRAB-01" }, "payment": { "form": "1", "method": "42", "bank": "Banco de Pruebas", "account_type": "Ahorros", "account_number": "0001234501" }, "payment_dates": [ "2026-09-30" ], "earnings": { "basic": { "days": 30, "amount": "1423500" }, "transport": [ { "allowance": "210000" } ] }, "deductions": { "health": { "percent": "4", "amount": "56940" }, "pension": { "percent": "4", "amount": "56940" } }, "type": "replace", "predecessor": { "id": "pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" } }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createPayrollAdjustment&ejemplo=reemplazar) ## Calcular una nota de ajuste de nómina sin emitirla `POST /v1/payroll_adjustments/preview` · `previewPayrollAdjustment` Valida y calcula la nota de ajuste **sin** reservar un número ni enviar nada. **Cuerpo** ([PayrollAdjustmentInput](/docs/api/objetos#PayrollAdjustmentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. En las nóminas es único entre las nóminas y sus ajustes (aparte de las facturas y los eventos). | | `type` | `replace` \| `delete` | sí | "replace" (Reemplazar: el ajuste lleva todos los datos de la nómina menos `novelty`) o "delete" (Eliminar: solo `predecessor`, `notes`, `generation_place` y `employer`). | | `predecessor` | [PayrollReference](/docs/api/objetos#PayrollReference) | sí | El documento que se ajusta: {"id": "pr_…"} (una nómina de Trevol) o {"id": "pra_…"} (otro ajuste, el último de la cadena), o sus datos {number, cune, issue_date}. | | `period` | [PayrollPeriod](/docs/api/objetos#PayrollPeriod) | | | | `pay_period` | texto | | | | `currency` | texto | | | | `exchange_rate` | decimal | | Número o texto decimal con punto («150000.50»). | | `notes` | lista de texto | | Máximo 20. | | `employee` | [PayrollEmployee](/docs/api/objetos#PayrollEmployee) | | | | `payment` | [PayrollPayment](/docs/api/objetos#PayrollPayment) | | | | `payment_dates` | lista de fecha (AAAA-MM-DD) | | Máximo 31. | | `earnings` | [PayrollEarnings](/docs/api/objetos#PayrollEarnings) | | | | `deductions` | [PayrollDeductions](/docs/api/objetos#PayrollDeductions) | | | | `rounding` | decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `generation_place` | [PayrollPlace](/docs/api/objetos#PayrollPlace) | | | | `employer` | [PayrollEmployer](/docs/api/objetos#PayrollEmployer) | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | Totales ([Preview](/docs/api/objetos#Preview)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/payroll_adjustments/preview` (`previewPayrollAdjustment`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/payroll_adjustments/preview" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "NOM-2026-09-001-R", "period": { "hire_date": "2024-03-01", "start": "2026-09-01", "end": "2026-09-30" }, "pay_period": "5", "notes": [ "Nómina de septiembre de 2026" ], "employee": { "type": "01", "subtype": "00", "high_risk_pension": false, "document_type": "13", "document_number": "1000000101", "first_surname": "PRUEBA", "second_surname": "NOMINA01", "first_name": "TRABAJADOR", "other_names": "DE PRUEBA", "workplace": { "country": "CO", "municipality_code": "76001", "address": "Calle 1 # 1-1" }, "integral_salary": false, "contract_type": "2", "salary": "1423500", "code": "TRAB-01" }, "payment": { "form": "1", "method": "42", "bank": "Banco de Pruebas", "account_type": "Ahorros", "account_number": "0001234501" }, "payment_dates": [ "2026-09-30" ], "earnings": { "basic": { "days": 30, "amount": "1423500" }, "transport": [ { "allowance": "210000" } ] }, "deductions": { "health": { "percent": "4", "amount": "56940" }, "pension": { "percent": "4", "amount": "56940" } }, "type": "replace", "predecessor": { "id": "pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" } }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=previewPayrollAdjustment&ejemplo=reemplazar) ## Reenviar una pendiente o en error `POST /v1/payroll_adjustments/{id}/send` · `sendPayrollAdjustment` Vuelve a enviar una nota de ajuste `pending` o `error`. Sin cuerpo. Con un documento `accepted` responde `200` con el documento tal cual; con uno que la DIAN todavía verifica no reenvía nada (`202`); con uno `rejected` es `409 invalid_state` (se corrige con `PUT`). No pide `Idempotency-Key` (reintentarlo es seguro). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([PayrollAdjustment](/docs/api/objetos#PayrollAdjustment)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. También `status: validating` (en las dos versiones): el set de pruebas de habilitación de nómina recibió el documento y todavía lo valida; reintentar con la misma Idempotency-Key, `POST …/send` o corregir responde lo mismo SIN reenviar (`PUT` en ese estado es 409 `invalid_state`). ([PayrollAdjustment](/docs/api/objetos#PayrollAdjustment)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/payroll_adjustments/{id}/send` (`sendPayrollAdjustment`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/payroll_adjustments/pra_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/send" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=sendPayrollAdjustment) ## Listar (nota de ajuste de nómina) `GET /v1/payroll_adjustments` · `listPayrollAdjustments` Paginación por cursor y los mismos filtros de `GET /v1/payrolls` (sin documento del trabajador). **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `limit` | consulta | entero | | | | `starting_after` | consulta | texto | | | | `status` | consulta | texto | | | | `external_id` | consulta | texto | | | | `created_from` | consulta | texto | | | | `created_to` | consulta | texto | | | | `employee_code` | consulta | texto | | | | `period_end_from` | consulta | fecha (AAAA-MM-DD) | | | | `period_end_to` | consulta | fecha (AAAA-MM-DD) | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data, has_more, next_cursor} (objeto) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/payroll_adjustments` (`listPayrollAdjustments`) #### cURL ```bash curl "https://api.trevol.si/v1/payroll_adjustments?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listPayrollAdjustments) ## Una nota de ajuste de nómina `GET /v1/payroll_adjustments/{id}` · `retrievePayrollAdjustment` La nota de ajuste, con `type` y `predecessor` (con el prefijo de la clase de su predecesor). El Eliminar no trae trabajador, devengados ni deducciones. **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([PayrollAdjustment](/docs/api/objetos#PayrollAdjustment)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/payroll_adjustments/{id}` (`retrievePayrollAdjustment`) #### cURL ```bash curl "https://api.trevol.si/v1/payroll_adjustments/pra_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrievePayrollAdjustment) ## Archivo xml `GET /v1/payroll_adjustments/{id}/xml` · `downloadPayrollAdjustmentXml` El XML firmado que se mandó a la DIAN (`application/xml`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | xml (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/payroll_adjustments/{id}/xml` (`downloadPayrollAdjustmentXml`) #### cURL ```bash curl "https://api.trevol.si/v1/payroll_adjustments/pra_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/xml" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o payroll_adjustment_xml.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadPayrollAdjustmentXml) ## Archivo zip `GET /v1/payroll_adjustments/{id}/zip` · `downloadPayrollAdjustmentZip` El ZIP del documento (`application/zip`). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | zip (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/payroll_adjustments/{id}/zip` (`downloadPayrollAdjustmentZip`) #### cURL ```bash curl "https://api.trevol.si/v1/payroll_adjustments/pra_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/zip" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o payroll_adjustment_zip.zip ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadPayrollAdjustmentZip) ## Archivo dian_response `GET /v1/payroll_adjustments/{id}/dian_response` · `downloadPayrollAdjustmentDianResponse` El `ApplicationResponse` con que la DIAN contestó (`application/xml`), si la DIAN lo devolvió. **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | dian_response (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/payroll_adjustments/{id}/dian_response` (`downloadPayrollAdjustmentDianResponse`) #### cURL ```bash curl "https://api.trevol.si/v1/payroll_adjustments/pra_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/dian_response" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o payroll_adjustment_dian_response.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadPayrollAdjustmentDianResponse) ## Corregir una nota de ajuste de nómina rechazada (mismo número) y reenviarla `PUT /v1/payroll_adjustments/{id}` · `correctPayrollAdjustment` Corrige una nota de ajuste rechazada con el **mismo número** y la vuelve a enviar. Solo si el documento está `rejected`, `error` o `pending`. Uno `accepted` no se corrige (se ajusta con una nota) y uno que la DIAN todavía verifica es `409 invalid_state`: espera el resultado. El `external_id` y la numeración no se cambian. Pide `Idempotency-Key`. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([PayrollAdjustmentInput](/docs/api/objetos#PayrollAdjustmentInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. En las nóminas es único entre las nóminas y sus ajustes (aparte de las facturas y los eventos). | | `type` | `replace` \| `delete` | sí | "replace" (Reemplazar: el ajuste lleva todos los datos de la nómina menos `novelty`) o "delete" (Eliminar: solo `predecessor`, `notes`, `generation_place` y `employer`). | | `predecessor` | [PayrollReference](/docs/api/objetos#PayrollReference) | sí | El documento que se ajusta: {"id": "pr_…"} (una nómina de Trevol) o {"id": "pra_…"} (otro ajuste, el último de la cadena), o sus datos {number, cune, issue_date}. | | `period` | [PayrollPeriod](/docs/api/objetos#PayrollPeriod) | | | | `pay_period` | texto | | | | `currency` | texto | | | | `exchange_rate` | decimal | | Número o texto decimal con punto («150000.50»). | | `notes` | lista de texto | | Máximo 20. | | `employee` | [PayrollEmployee](/docs/api/objetos#PayrollEmployee) | | | | `payment` | [PayrollPayment](/docs/api/objetos#PayrollPayment) | | | | `payment_dates` | lista de fecha (AAAA-MM-DD) | | Máximo 31. | | `earnings` | [PayrollEarnings](/docs/api/objetos#PayrollEarnings) | | | | `deductions` | [PayrollDeductions](/docs/api/objetos#PayrollDeductions) | | | | `rounding` | decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `generation_place` | [PayrollPlace](/docs/api/objetos#PayrollPlace) | | | | `employer` | [PayrollEmployer](/docs/api/objetos#PayrollEmployer) | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([PayrollAdjustment](/docs/api/objetos#PayrollAdjustment)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. También `status: validating` (en las dos versiones): el set de pruebas de habilitación de nómina recibió el documento y todavía lo valida; reintentar con la misma Idempotency-Key, `POST …/send` o corregir responde lo mismo SIN reenviar (`PUT` en ese estado es 409 `invalid_state`). ([PayrollAdjustment](/docs/api/objetos#PayrollAdjustment)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `PUT /v1/payroll_adjustments/{id}` (`correctPayrollAdjustment`) #### cURL ```bash curl -X PUT "https://api.trevol.si/v1/payroll_adjustments/pra_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "NOM-2026-09-001-R", "period": { "hire_date": "2024-03-01", "start": "2026-09-01", "end": "2026-09-30" }, "pay_period": "5", "notes": [ "Nómina de septiembre de 2026" ], "employee": { "type": "01", "subtype": "00", "high_risk_pension": false, "document_type": "13", "document_number": "1000000101", "first_surname": "PRUEBA", "second_surname": "NOMINA01", "first_name": "TRABAJADOR", "other_names": "DE PRUEBA", "workplace": { "country": "CO", "municipality_code": "76001", "address": "Calle 1 # 1-1" }, "integral_salary": false, "contract_type": "2", "salary": "1423500", "code": "TRAB-01" }, "payment": { "form": "1", "method": "42", "bank": "Banco de Pruebas", "account_type": "Ahorros", "account_number": "0001234501" }, "payment_dates": [ "2026-09-30" ], "earnings": { "basic": { "days": 30, "amount": "1423500" }, "transport": [ { "allowance": "210000" } ] }, "deductions": { "health": { "percent": "4", "amount": "56940" }, "pension": { "percent": "4", "amount": "56940" } }, "type": "replace", "predecessor": { "id": "pr_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" } }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=correctPayrollAdjustment&ejemplo=reemplazar) --- # Eventos RADIAN > Las operaciones de Eventos RADIAN de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/radian-events Eventos sobre facturas: el acuse (030), el reclamo (031), el recibo del bien (032) y la aceptación expresa (033) de una factura RECIBIDA, y la aceptación tácita (034) de una factura PROPIA a crédito. Un rechazo es final: el siguiente intento es otro evento. Solo `xml`. Los eventos 035 a 051 necesitan un participante RADIAN y no están disponibles. Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## Emitir un evento RADIAN `POST /v1/radian_events` · `createRadianEvent` Envía un **evento RADIAN** sobre una factura: el acuse de recibo (030), el reclamo (031), el recibo del bien o servicio (032) y la aceptación expresa (033) de una factura RECIBIDA (`invoice`), o la aceptación tácita (034) de una factura PROPIA a crédito: de Trevol (`invoice_id`) o emitida antes con otro software y traída por la sincronización con la DIAN (`invoice: {cufe, number?}`; exactamente uno de los dos). Los eventos 035 a 051 necesitan un participante RADIAN y no están disponibles. El 030 y el 032 necesitan el representante legal de la empresa (lo firma): se configura en la app. **Un rechazo es final**: el siguiente intento es otro evento, con otro número (`POST` nuevo). Un evento vivo del mismo código sobre la misma factura es `409 duplicate_document` con `existing_id`: reintenta ese con `POST …/send`. `already_registered: true` (con `201`) solo significa que la DIAN ya mostraba el CUDE de ESTE evento; si la factura ya tenía ese evento por otro camino es `422 dian_rejected` con `already_registered_elsewhere: true`. La DIAN pide el evento anterior antes del siguiente: en la 2026-10-21 es `409 previous_event_pending`; en la 2026-10-22, `202 processing` y Trevol lo manda cuando aparezca. Si la DIAN contesta 98 («en proceso de validación») es `202 validating`: no reenvíes. Para saber si el 034 procede antes de pedirlo, usa `GET /v1/invoices/{id}/tacit_acceptance` (o, por CUFE, `GET /v1/radian_events/tacit_acceptance?cufe=…`). **Respuestas.** `201` la DIAN lo validó (`status: accepted`). `202` solo en la versión 2026-10-22: la DIAN no contestó (`processing`), otra petición lo está enviando (`sending`) o, en habilitación, el set de pruebas todavía lo valida (`validating`, en las dos versiones); trae `Location` y `Retry-After`, y el resultado llega por GET y por webhook: **no crees otro documento para la misma operación**. `422` `validation_error` (se revisó a fondo ANTES de reservar: no se gastó el número, y `errors[]` dice el campo), `dian_rejected` (con `document` y las reglas traducidas; un rechazo es final: el siguiente intento es otro `POST`, con otro número), `company_not_enabled` o `numbering_unavailable` (con `reason`). `402` `insufficient_balance` (producción sin saldo: queda `pending` con su número). `409` `external_id_in_use` o `duplicate_document` (con `existing_id`). `503` `dian_unavailable` en la 2026-10-21. Pide `Idempotency-Key`: repetir la misma devuelve lo mismo sin enviar nada. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([RadianEventInput](/docs/api/objetos#RadianEventInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. En los eventos es único entre los eventos (aparte de las facturas y las nóminas). | | `event_code` | texto | sí | "030" acuse de recibo, "031" reclamo, "032" recibo del bien o servicio, "033" aceptación expresa (las cuatro sobre una factura RECIBIDA) o "034" aceptación tácita (sobre una factura PROPIA a crédito). Los 035 a 051 necesitan un participante RADIAN y no están disponibles. | | `invoice` | [ReceivedInvoice](/docs/api/objetos#ReceivedInvoice) | | 030–033: la factura RECIBIDA sobre la que va el evento (CUFE, número y quién la emitió). 034: la factura PROPIA por su CUFE, `{cufe, number?}` (solo esos dos), en lugar de `invoice_id`: sirve también para las facturas que no se emitieron con Trevol (las trae la sincronización con la DIAN). Si el CUFE es de una factura de Trevol, es lo mismo que mandar su `invoice_id`. | | `invoice_id` | texto | | 034: el id (`inv_…`) de una factura de Trevol: propia, validada, a crédito y que no sea de consumidor final. Va aparte de `invoice` para que cada esquema sea plano en los SDK. Para una factura que no se emitió con Trevol, usa `invoice: {cufe}` (exactamente uno de los dos). | | `claim` | [EventClaim](/docs/api/objetos#EventClaim) | | Solo en el reclamo (031) y obligatorio en él: {code: "01" a "04", note?}. | | `software` | `own` \| `dian_free` | | 030–033: `own` (el Software Propio de la empresa, por defecto) o `dian_free` (el software gratuito de la DIAN). El 034 se firma con el Software Propio. | | `provider` | `dian` \| `company` | | 030–033: quién es el proveedor tecnológico: `dian` (por defecto con `dian_free`) o `company` (por defecto con `own`). | **Respuestas** | Estado | Qué pasó | | --- | --- | | 201 | El documento ([RadianEvent](/docs/api/objetos#RadianEvent)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. También `status: validating` (en las dos versiones): la DIAN respondió 98; reintentar con la misma Idempotency-Key o `POST …/send` responde lo mismo SIN reenviar. En la 2026-10-21, el evento anterior que aún no aparece es 409 `previous_event_pending`. ([RadianEvent](/docs/api/objetos#RadianEvent)) | | 402 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 429 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 503 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/radian_events` (`createRadianEvent`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/radian_events" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "external_id": "ERP-ACUSE-OJ1234", "event_code": "030", "invoice": { "cufe": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "number": "OJ1234", "type": "01", "issuer": { "nit": "890330031", "check_digit": 3, "document_type": "31", "name": "OSPINA Y ASOCIADOS SAS" } } }' ``` #### Respuesta 201 ```json { "id": "rev_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "radian_event", "environment": "test", "status": "accepted", "event_code": "030", "number": "AR994000123", "prefix": "AR", "external_id": "ERP-ACUSE-OJ1234", "origin": "api", "cude": "abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "invoice": { "cufe": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "number": "OJ1234", "type": "01", "issuer": { "nit": "890330031", "check_digit": 3, "document_type": "31", "name": "OSPINA Y ASOCIADOS SAS" } }, "claim": null, "software": "own", "provider": "company", "dian": { "status_code": "02", "message": "Documento validado por la DIAN", "rules": [] }, "already_registered": false, "already_registered_elsewhere": false, "tacit": false, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createRadianEvent&ejemplo=acuse) ## Reenviar una pendiente o en error `POST /v1/radian_events/{id}/send` · `sendRadianEvent` Vuelve a enviar un evento `pending` o `error` con el **mismo número** (por ejemplo, un 034 que todavía no procedía o un 031 que esperaba al evento anterior). Un evento que la DIAN verifica (`validating`) solo consulta; uno `rejected` es final (`409 invalid_state`): crea otro con `POST`. Sin cuerpo. Con un documento `accepted` responde `200` con el documento tal cual; con uno que la DIAN todavía verifica no reenvía nada (`202`); con uno `rejected` es `409 invalid_state` (se corrige con `PUT`). No pide `Idempotency-Key` (reintentarlo es seguro). **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([RadianEvent](/docs/api/objetos#RadianEvent)) | | 202 | Versión 2026-10-22: la DIAN no contestó (`status: processing`; Trevol termina el documento y avisa por webhook; no crees otro documento para la misma venta, y si repites con la misma Idempotency-Key respondemos sin enviar) o el documento se está enviando (`status: sending`); también la contingencia (`status: contingency`). Lleva `Location` y, salvo la contingencia, `Retry-After: 30`. En 2026-10-21 la DIAN sin contestar es un 503 `dian_unavailable`. También `status: validating` (en las dos versiones): la DIAN respondió 98; reintentar con la misma Idempotency-Key o `POST …/send` responde lo mismo SIN reenviar. En la 2026-10-21, el evento anterior que aún no aparece es 409 `previous_event_pending`. ([RadianEvent](/docs/api/objetos#RadianEvent)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/radian_events/{id}/send` (`sendRadianEvent`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/radian_events/rev_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/send" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "id": "rev_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "radian_event", "environment": "test", "status": "accepted", "event_code": "030", "number": "AR994000123", "prefix": "AR", "external_id": "ERP-ACUSE-OJ1234", "origin": "api", "cude": "abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "invoice": { "cufe": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "number": "OJ1234", "type": "01", "issuer": { "nit": "890330031", "check_digit": 3, "document_type": "31", "name": "OSPINA Y ASOCIADOS SAS" } }, "claim": null, "software": "own", "provider": "company", "dian": { "status_code": "02", "message": "Documento validado por la DIAN", "rules": [] }, "already_registered": false, "already_registered_elsewhere": false, "tacit": false, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=sendRadianEvent) ## Listar (evento RADIAN) `GET /v1/radian_events` · `listRadianEvents` Paginación por cursor y filtros `status`, `external_id`, `created_from`, `created_to`, `event_code` y `invoice_cufe` (el CUFE de la factura). **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `limit` | consulta | entero | | | | `starting_after` | consulta | texto | | | | `status` | consulta | texto | | | | `external_id` | consulta | texto | | | | `created_from` | consulta | texto | | | | `created_to` | consulta | texto | | | | `event_code` | consulta | `030` \| `031` \| `032` \| `033` \| `034` | | | | `invoice_cufe` | consulta | texto | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data, has_more, next_cursor} (objeto) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/radian_events` (`listRadianEvents`) #### cURL ```bash curl "https://api.trevol.si/v1/radian_events?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listRadianEvents) ## Un evento radian `GET /v1/radian_events/{id}` · `retrieveRadianEvent` El evento tal como se envió, con su número, su CUDE, la factura y la respuesta de la DIAN. `already_registered`, `already_registered_elsewhere` y `tacit` se deducen de lo guardado. **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El documento ([RadianEvent](/docs/api/objetos#RadianEvent)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/radian_events/{id}` (`retrieveRadianEvent`) #### cURL ```bash curl "https://api.trevol.si/v1/radian_events/rev_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "id": "rev_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b", "object": "radian_event", "environment": "test", "status": "accepted", "event_code": "030", "number": "AR994000123", "prefix": "AR", "external_id": "ERP-ACUSE-OJ1234", "origin": "api", "cude": "abababababababababababababababababababababababababababababababababababababababababababababababab", "issued_at": "2026-10-23T09:15:02-05:00", "invoice": { "cufe": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "number": "OJ1234", "type": "01", "issuer": { "nit": "890330031", "check_digit": 3, "document_type": "31", "name": "OSPINA Y ASOCIADOS SAS" } }, "claim": null, "software": "own", "provider": "company", "dian": { "status_code": "02", "message": "Documento validado por la DIAN", "rules": [] }, "already_registered": false, "already_registered_elsewhere": false, "tacit": false, "created_at": "2026-10-23T09:15:02-05:00", "updated_at": "2026-10-23T09:15:03-05:00" } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveRadianEvent) ## Archivo xml `GET /v1/radian_events/{id}/xml` · `downloadRadianEventXml` El ApplicationResponse firmado que se mandó a la DIAN (`application/xml`). Los eventos no tienen PDF, ZIP ni `dian_response`. **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | xml (archivo) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/radian_events/{id}/xml` (`downloadRadianEventXml`) #### cURL ```bash curl "https://api.trevol.si/v1/radian_events/rev_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/xml" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -o radian_event_xml.xml ``` [Probar en el Playground](https://platform.trevol.si/playground?op=downloadRadianEventXml) ## ¿Procede ya la aceptación tácita (034) de esta factura? (por CUFE) `GET /v1/radian_events/tacit_acceptance` · `retrieveTacitAcceptanceByCufe` Lo mismo que `GET /v1/invoices/{id}/tacit_acceptance`, pero por el CUFE de la factura propia: sirve también para las facturas que no se emitieron con Trevol (emitidas antes, con otro software, y traídas por la sincronización con la DIAN; la sincronización trae las de producción). Consulta GetStatusEvent y aplica la regla de los 3 días hábiles; no envía nada. `invoice_id` solo sale si la factura es de Trevol. 404 si el CUFE no es de una factura propia de la empresa y el ambiente de la llave; 422 si falta `cufe` o la factura no admite el 034 (de contado, una nota, a consumidor final, sin el XML leído por la sincronización…); 429 con más de 30 consultas por minuto por llave (el mismo límite de la otra consulta); 503 si la DIAN no contesta (reintentable). **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `cufe` | consulta | texto | sí | El CUFE de la factura propia. | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El resultado ([TacitAcceptance](/docs/api/objetos#TacitAcceptance)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/radian_events/tacit_acceptance` (`retrieveTacitAcceptanceByCufe`) #### cURL ```bash curl "https://api.trevol.si/v1/radian_events/tacit_acceptance?cufe=abababababababababababababababababababababababababababababababababababababababababababababababab" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "object": "tacit_acceptance", "cufe": "abababababababababababababababababababababababababababababababababababababababababababababababab", "number": "FE4722", "allowed": true, "available_from": "2025-05-14", "rules": [] } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveTacitAcceptanceByCufe) --- # Endpoints de webhooks > Las operaciones de Endpoints de webhooks de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/webhook-endpoints Endpoints que reciben avisos firmados (Standard Webhooks) cuando un documento cambia. Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## Crear un endpoint de webhooks (máximo 5 por empresa y ambiente) `POST /v1/webhook_endpoints` · `createWebhookEndpoint` El `secret` (whsec_…) viene SOLO en esta respuesta. Cada entrega lleva `webhook-id` (el mismo en cada reintento: deduplica con él), `webhook-timestamp` y `webhook-signature`: `v1,` + base64(HMAC-SHA256(clave, `{webhook-id}.{webhook-timestamp}.{cuerpo}`)), con la clave de los 32 bytes que siguen a `whsec_`. No usa Idempotency-Key: la respuesta lleva el secreto y no se guarda. **Cuerpo** ([WebhookEndpointInput](/docs/api/objetos#WebhookEndpointInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `url` | texto | sí | https:// con un nombre de dominio (no una IP), sin usuario ni clave, puerto 443 o 1024–65535. En cada intento se resuelve el nombre y se rechaza toda dirección privada o local. | | `description` | texto (puede ser null) | | | | `events` | lista de `*` \| `invoice.accepted` \| `invoice.rejected` \| `invoice.error` \| `invoice.delivered` \| `invoice.email_bounced` \| `invoice.email_failed` \| `credit_note.accepted` \| `credit_note.rejected` \| `credit_note.error` \| `credit_note.delivered` \| `credit_note.email_bounced` \| `credit_note.email_failed` \| `debit_note.accepted` \| `debit_note.rejected` \| `debit_note.error` \| `debit_note.delivered` \| `debit_note.email_bounced` \| `debit_note.email_failed` \| `balance.low` \| `numbering.low` \| `numbering.expiring` \| `certificate.expiring` \| `support_document.accepted` \| `support_document.rejected` \| `support_document.error` \| `support_document_adjustment.accepted` \| `support_document_adjustment.rejected` \| `support_document_adjustment.error` \| `equivalent_document.accepted` \| `equivalent_document.rejected` \| `equivalent_document.error` \| `equivalent_document.delivered` \| `equivalent_document.email_bounced` \| `equivalent_document.email_failed` \| `equivalent_document_adjustment.accepted` \| `equivalent_document_adjustment.rejected` \| `equivalent_document_adjustment.error` \| `equivalent_document_adjustment.delivered` \| `equivalent_document_adjustment.email_bounced` \| `equivalent_document_adjustment.email_failed` \| `payroll.accepted` \| `payroll.rejected` \| `payroll.error` \| `payroll_adjustment.accepted` \| `payroll_adjustment.rejected` \| `payroll_adjustment.error` \| `radian_event.accepted` \| `radian_event.rejected` \| `radian_event.error` | sí | Eventos del catálogo (49 tipos, hasta 60 en la lista), o ["*"] para todos (no se mezcla). `balance.low`, `numbering.low` y `numbering.expiring` solo existen en producción (llaves live). `webhook.test` no se suscribe. Máximo 60. | **Respuestas** | Estado | Qué pasó | | --- | --- | | 201 | El endpoint, con su `secret` ([WebhookEndpoint](/docs/api/objetos#WebhookEndpoint)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/webhook_endpoints` (`createWebhookEndpoint`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/webhook_endpoints" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://tu-servidor.co/webhooks/trevol", "description": "Producción: facturación", "events": [ "invoice.accepted", "invoice.rejected", "credit_note.accepted" ] }' ``` [Se administra en Webhooks](https://platform.trevol.si/webhooks) ## Mandar un evento `webhook.test` solo a este endpoint (debe estar activo) `POST /v1/webhook_endpoints/{id}/test` · `testWebhookEndpoint` Encola una entrega manual de prueba (3 intentos) y despierta el envío: en segundos llega a tu servidor con la firma de siempre. Hasta 60 por hora. Mira cómo salió en …/deliveries. **Respuestas** | Estado | Qué pasó | | --- | --- | | 202 | La entrega encolada ([WebhookDelivery](/docs/api/objetos#WebhookDelivery)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 429 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/webhook_endpoints/{id}/test` (`testWebhookEndpoint`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/webhook_endpoints/we_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/test" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=testWebhookEndpoint) ## Rotar el secreto `POST /v1/webhook_endpoints/{id}/rotate_secret` · `rotateWebhookEndpointSecret` El `secret` (whsec_…) viene SOLO en esta respuesta. Cada entrega lleva `webhook-id` (el mismo en cada reintento: deduplica con él), `webhook-timestamp` y `webhook-signature`: `v1,` + base64(HMAC-SHA256(clave, `{webhook-id}.{webhook-timestamp}.{cuerpo}`)), con la clave de los 32 bytes que siguen a `whsec_`. El secreto anterior sigue firmando `grace_period_hours` (0 a 72; 24 por defecto): durante la gracia cada entrega lleva las dos firmas, separadas por un espacio. **Cuerpo** (objeto) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `grace_period_hours` | entero | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El endpoint, con el `secret` nuevo ([WebhookEndpoint](/docs/api/objetos#WebhookEndpoint)) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/webhook_endpoints/{id}/rotate_secret` (`rotateWebhookEndpointSecret`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/webhook_endpoints/we_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/rotate_secret" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "grace_period_hours": 24 }' ``` [Se administra en Webhooks](https://platform.trevol.si/webhooks) ## Los endpoints de webhooks de la empresa y el ambiente `GET /v1/webhook_endpoints` · `listWebhookEndpoints` Los de la empresa y el ambiente de la llave (máximo 5). Las llaves de lectura ven el esquema y el host de la `url`, con el camino enmascarado («https://tu-servidor.co/…»): a veces es un secreto. Las de escritura la ven completa. **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data, has_more, next_cursor} (objeto) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/webhook_endpoints` (`listWebhookEndpoints`) #### cURL ```bash curl "https://api.trevol.si/v1/webhook_endpoints" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listWebhookEndpoints) ## Un endpoint `GET /v1/webhook_endpoints/{id}` · `retrieveWebhookEndpoint` El endpoint (nunca el secreto: solo se muestra al crear o rotar). Un id de otra empresa o de otro ambiente es 404. **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El recurso ([WebhookEndpoint](/docs/api/objetos#WebhookEndpoint)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/webhook_endpoints/{id}` (`retrieveWebhookEndpoint`) #### cURL ```bash curl "https://api.trevol.si/v1/webhook_endpoints/we_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveWebhookEndpoint) ## Las entregas de este endpoint, de la más nueva a la más vieja `GET /v1/webhook_endpoints/{id}/deliveries` · `listWebhookEndpointDeliveries` Cada intento con su estado HTTP, su error (`timeout`, `http_5xx`, `blocked_address`…) y su duración. Nunca guardamos la respuesta de tu servidor. Se conservan 30 días. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `limit` | consulta | entero | | | | `starting_after` | consulta | texto | | | | `status` | consulta | `pending` \| `sending` \| `succeeded` \| `failed` \| `canceled` | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data, has_more, next_cursor} (objeto) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/webhook_endpoints/{id}/deliveries` (`listWebhookEndpointDeliveries`) #### cURL ```bash curl "https://api.trevol.si/v1/webhook_endpoints/we_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/deliveries?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listWebhookEndpointDeliveries) ## Cambiar la URL, la descripción, los eventos o pausar y reactivar `PATCH /v1/webhook_endpoints/{id}` · `updateWebhookEndpoint` Solo cambia lo que mandes. `status: paused` cancela las entregas pendientes; `status: enabled` reactiva el endpoint (también uno que Trevol desactivó por fallos sostenidos) y pone en cero el contador de fallos. `disabled` no se puede pedir. **Cuerpo** ([WebhookEndpointUpdate](/docs/api/objetos#WebhookEndpointUpdate), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `url` | texto | | https:// con un nombre de dominio (no una IP), sin usuario ni clave, puerto 443 o 1024–65535. En cada intento se resuelve el nombre y se rechaza toda dirección privada o local. | | `description` | texto (puede ser null) | | | | `events` | lista de `*` \| `invoice.accepted` \| `invoice.rejected` \| `invoice.error` \| `invoice.delivered` \| `invoice.email_bounced` \| `invoice.email_failed` \| `credit_note.accepted` \| `credit_note.rejected` \| `credit_note.error` \| `credit_note.delivered` \| `credit_note.email_bounced` \| `credit_note.email_failed` \| `debit_note.accepted` \| `debit_note.rejected` \| `debit_note.error` \| `debit_note.delivered` \| `debit_note.email_bounced` \| `debit_note.email_failed` \| `balance.low` \| `numbering.low` \| `numbering.expiring` \| `certificate.expiring` \| `support_document.accepted` \| `support_document.rejected` \| `support_document.error` \| `support_document_adjustment.accepted` \| `support_document_adjustment.rejected` \| `support_document_adjustment.error` \| `equivalent_document.accepted` \| `equivalent_document.rejected` \| `equivalent_document.error` \| `equivalent_document.delivered` \| `equivalent_document.email_bounced` \| `equivalent_document.email_failed` \| `equivalent_document_adjustment.accepted` \| `equivalent_document_adjustment.rejected` \| `equivalent_document_adjustment.error` \| `equivalent_document_adjustment.delivered` \| `equivalent_document_adjustment.email_bounced` \| `equivalent_document_adjustment.email_failed` \| `payroll.accepted` \| `payroll.rejected` \| `payroll.error` \| `payroll_adjustment.accepted` \| `payroll_adjustment.rejected` \| `payroll_adjustment.error` \| `radian_event.accepted` \| `radian_event.rejected` \| `radian_event.error` | | Eventos del catálogo (49 tipos, hasta 60 en la lista), o ["*"] para todos (no se mezcla). `balance.low`, `numbering.low` y `numbering.expiring` solo existen en producción (llaves live). `webhook.test` no se suscribe. Máximo 60. | | `status` | `enabled` \| `paused` | | enabled reactiva (también uno desactivado por Trevol) y pone en cero el contador de fallos. | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El recurso ([WebhookEndpoint](/docs/api/objetos#WebhookEndpoint)) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `PATCH /v1/webhook_endpoints/{id}` (`updateWebhookEndpoint`) #### cURL ```bash curl -X PATCH "https://api.trevol.si/v1/webhook_endpoints/we_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "description": "Pausado mientras migramos el servidor", "status": "paused" }' ``` [Se administra en Webhooks](https://platform.trevol.si/webhooks) ## Borrar el endpoint (con sus entregas) `DELETE /v1/webhook_endpoints/{id}` · `deleteWebhookEndpoint` Tu servidor deja de recibir avisos de inmediato. No se puede deshacer; si solo quieres detenerlo un rato, pausa el endpoint. **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {id, object: webhook_endpoint, deleted: true} ([DeletedObject](/docs/api/objetos#DeletedObject)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `DELETE /v1/webhook_endpoints/{id}` (`deleteWebhookEndpoint`) #### cURL ```bash curl -X DELETE "https://api.trevol.si/v1/webhook_endpoints/we_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Se administra en Webhooks](https://platform.trevol.si/webhooks) --- # Eventos > Las operaciones de Eventos de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/events Los eventos que Trevol manda a tus webhooks, guardados 30 días, y su reenvío. Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## Reenviar un evento a un endpoint (entrega manual nueva; el `webhook-id` es el mismo) `POST /v1/events/{id}/resend` · `resendEvent` Vuelve a mandar el evento a un endpoint activo de la misma empresa y el mismo ambiente. Lleva el mismo `webhook-id`: si tu servidor ya lo procesó, deduplica con él. Hasta 60 por hora. **Cuerpo** (objeto, obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `endpoint_id` | texto | sí | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 202 | La entrega encolada ([WebhookDelivery](/docs/api/objetos#WebhookDelivery)) | | 409 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 429 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/events/{id}/resend` (`resendEvent`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/events/evt_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b/resend" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "endpoint_id": "we_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" }' ``` [Probar en el Playground](https://platform.trevol.si/playground?op=resendEvent&ejemplo=reenviar) ## Los eventos de 30 días de la empresa y el ambiente `GET /v1/events` · `listEvents` Solo se guardan eventos si la empresa tiene al menos un endpoint en este ambiente. Útil para recuperar lo que se perdió mientras tu servidor estaba caído. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `limit` | consulta | entero | | | | `starting_after` | consulta | texto | | | | `type` | consulta | texto | | | | `created_from` | consulta | texto | | | | `created_to` | consulta | texto | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data, has_more, next_cursor} (objeto) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/events` (`listEvents`) #### cURL ```bash curl "https://api.trevol.si/v1/events?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listEvents) ## Un evento `GET /v1/events/{id}` · `retrieveEvent` El evento tal como se mandó al endpoint. **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El recurso ([Event](/docs/api/objetos#Event)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/events/{id}` (`retrieveEvent`) #### cURL ```bash curl "https://api.trevol.si/v1/events/evt_3f0c1a2b3f0c1a2b3f0c1a2b3f0c1a2b" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveEvent) --- # Registro de peticiones > Las operaciones de Registro de peticiones de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/requests Qué pidió tu software a la API, cómo le fue y cuánto tardó (30 días, sin cuerpos). Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## El registro de peticiones de la empresa y el ambiente de la llave (30 días) `GET /v1/requests` · `listRequests` Cada petición que pasó la autenticación de una llave, aunque terminara en 403, 422 o 429. Sin cuerpos, consultas, IP ni llaves; `path` es la plantilla. **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `limit` | consulta | entero | | | | `starting_after` | consulta | texto | | | | `status` | consulta | entero | | | | `key_id` | consulta | texto | | | | `created_from` | consulta | texto | | | | `created_to` | consulta | texto | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data: [RequestLog], has_more, next_cursor} (objeto) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/requests` (`listRequests`) #### cURL ```bash curl "https://api.trevol.si/v1/requests?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listRequests) --- # Saldo > Las operaciones de Saldo de la API de Trevol: parámetros, cuerpo, respuestas y ejemplos de código. Fuente: https://platform.trevol.si/docs/api/balance El saldo prepago de la cuenta (lo comparten sus empresas), sus movimientos y las recargas con Wompi. Los ejemplos son peticiones con cURL. La llave va en `TREVOL_API_KEY`; los cuerpos de ejemplo son los de la especificación. Los errores comunes están en [Errores](/docs/errores). ## Preparar una recarga del saldo (para el widget de Wompi) `POST /v1/balance/top_ups` · `createBalanceTopUp` Prepara una recarga del saldo de la cuenta y devuelve lo que necesita el widget de Wompi (`WidgetCheckout`: `publicKey`, `currency`, `amountInCents`, `reference`, `signature: {integrity}` y `expirationTime`). El pago se hace en el widget; cuando Wompi lo aprueba, Trevol abona el saldo (y la factura de la recarga sale a nombre de `buyer`). Preparar no mueve saldo y la firma vence en una hora: si el widget venció, prepara otra. Solo llaves de escritura; exige `Idempotency-Key` (repetir con la misma clave devuelve la MISMA recarga). Sin `buyer`, la factura va a nombre de la empresa de la llave con sus datos (NIT, DV, razón social, correo, teléfono, dirección y municipio); si no alcanzan, 422 en `buyer.*`. El monto va entre la recarga mínima y la máxima (422 `top_up_below_minimum` / `top_up_above_maximum` con `minimum_cop` / `maximum_cop`). Es dinero real con cualquier llave (también `test`): el saldo es uno solo. Hasta 20 por llave cada 10 minutos (429). **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `Idempotency-Key` | cabecera | texto | sí | Obligatoria: 1 a 255 caracteres ASCII visibles, única por pedido (7 días). | **Cuerpo** ([TopUpInput](/docs/api/objetos#TopUpInput), obligatorio) | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `amount_cop` | entero | sí | En pesos, entre la recarga mínima y la máxima. | | `buyer` | [TopUpBuyer](/docs/api/objetos#TopUpBuyer) | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 201 | La recarga preparada ([TopUp](/docs/api/objetos#TopUp)) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 429 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | 503 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `POST /v1/balance/top_ups` (`createBalanceTopUp`) #### cURL ```bash curl -X POST "https://api.trevol.si/v1/balance/top_ups" \ -H "Authorization: Bearer $TREVOL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "amount_cop": 50000 }' ``` #### Respuesta 201 ```json { "object": "top_up", "reference": "TRV-REC-20261031-4C1D2E3F4A5B", "amount_cop": 50000, "status": "prepared", "wompi": { "public_key": "pub_prod_Ejemplo0000000000000000000000", "reference": "TRV-REC-20261031-4C1D2E3F4A5B", "amount_in_cents": 5000000, "currency": "COP", "signature": "5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d", "expiration_time": "2026-10-31T15:20:05.000Z" } } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=createBalanceTopUp&ejemplo=empresa) ## El saldo de la cuenta (lo comparten sus empresas) `GET /v1/balance` · `retrieveBalance` El saldo prepago de Trevol. Es de la **cuenta**, no de la empresa: todas las empresas de la cuenta lo comparten (`shared: true`) y cualquier llave de cualquiera de ellas, de lectura o de escritura, `test` o `live`, ve el mismo. Solo lo que se emite en producción (llaves `live`) lo consume; la habilitación (`test`) no cobra. `available` es lo que se puede gastar (en pesos); `held`, lo apartado por documentos que la DIAN todavía valida; `overdraft`, cuánto puede bajar `available` de cero al apartar un documento; `low_threshold`, el umbral del aviso de saldo bajo (el 20 % de la última recarga; `null` sin recargas). `level`: `empty` con `available` ≤ 0, `low` por debajo de `low_threshold`, si no `ok`. Con `available` por debajo del precio de un documento, emitir en producción responde 402 `insufficient_balance`. El evento `balance.low` avisa por webhook. **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | El saldo ([Balance](/docs/api/objetos#Balance)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/balance` (`retrieveBalance`) #### cURL ```bash curl "https://api.trevol.si/v1/balance" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "object": "balance", "currency": "COP", "available": 48600, "held": 400, "overdraft": 0, "low_threshold": 10000, "level": "ok", "updated_at": "2026-10-31T09:20:05-05:00", "shared": true } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=retrieveBalance) ## Los movimientos del saldo de la cuenta `GET /v1/balance/transactions` · `listBalanceTransactions` Los movimientos del saldo de la cuenta (de todas sus empresas), del más nuevo al más viejo: lo mismo que «Saldo y recargas» en la app. Un documento es UNA fila (`type: document`) con su `status`: `pending` (apartado mientras la DIAN lo valida), `charged` (validado y cobrado) o `released` (la DIAN lo rechazó: no se cobró y `amount` es 0); `company` dice qué empresa lo emitió. Lo demás es una fila por movimiento: `top_up` (recarga, con su `reference`), `top_up_reversal`, `bonus`, `adjustment`, `license_fee`, `expiration` u `other`. `amount` es lo que el movimiento hizo con el disponible, con signo; `available_after`, el disponible justo después. Filtro `type`: `all` (por defecto), `documents`, `funding` (todo lo que no es un documento) o `top_ups`. Paginación por cursor: `limit` (1 a 100, 25 por defecto) y `starting_after` (el `id` de la última fila que tienes). **Parámetros** | Nombre | En | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | --- | | `limit` | consulta | entero | | | | `starting_after` | consulta | texto | | | | `type` | consulta | `all` \| `documents` \| `funding` \| `top_ups` | | | **Respuestas** | Estado | Qué pasó | | --- | --- | | 200 | {object: list, data, has_more, next_cursor} (objeto) | | 422 | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | | Error | Error (application/problem+json) ([Problem](/docs/api/objetos#Problem)) | `GET /v1/balance/transactions` (`listBalanceTransactions`) #### cURL ```bash curl "https://api.trevol.si/v1/balance/transactions?limit=100" \ -H "Authorization: Bearer $TREVOL_API_KEY" ``` #### Respuesta 200 ```json { "object": "list", "has_more": true, "next_cursor": "btx_9300", "data": [ { "id": "btx_9341", "object": "balance_transaction", "type": "document", "status": "pending", "amount": -400, "available_after": 48600, "description": "Factura FE1290 · apartado mientras la DIAN lo valida", "document": { "type": "invoice", "number": "FE1290" }, "company": { "nit": "900373115", "name": "TOSTADORA MONTEVERDE SAS" }, "reference": null, "created_at": "2026-10-31T09:20:05-05:00" }, { "id": "btx_9338", "object": "balance_transaction", "type": "document", "status": "charged", "amount": -400, "available_after": 49000, "description": "Nómina NE15", "document": { "type": "payroll", "number": "NE15" }, "company": { "nit": "900373591", "name": "CAFÉ DEL VALLE SAS" }, "reference": null, "created_at": "2026-10-31T08:02:41-05:00" }, { "id": "btx_9301", "object": "balance_transaction", "type": "document", "status": "released", "amount": 0, "available_after": 49000, "description": "Factura FE1289 · rechazado por la DIAN: no se cobró", "document": { "type": "invoice", "number": "FE1289" }, "company": { "nit": "900373115", "name": "TOSTADORA MONTEVERDE SAS" }, "reference": null, "created_at": "2026-10-30T17:10:00-05:00" }, { "id": "btx_9300", "object": "balance_transaction", "type": "top_up", "status": "completed", "amount": 50000, "available_after": 49400, "description": "Recarga TRV-REC-20261030-8F2A61C0D9B3", "document": null, "company": null, "reference": "TRV-REC-20261030-8F2A61C0D9B3", "created_at": "2026-10-30T16:58:12-05:00" } ] } ``` [Probar en el Playground](https://platform.trevol.si/playground?op=listBalanceTransactions) --- # Objetos > Todos los objetos de la API de Trevol con sus campos, tipos y cuáles son obligatorios. Fuente: https://platform.trevol.si/docs/api/objetos Cada objeto de la API, con sus campos. Los campos obligatorios llevan «sí». Los tipos enlazan a su objeto. ### Acquirer El adquiriente como lo tiene la DIAN (y su correo, si lo conoce). | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `acquirer` | | | | `document_type` | texto | | | | `document_number` | texto | | | | `found` | booleano | | | | `name` | texto (puede ser null) | | | | `email` | texto (puede ser null) | | | ### AirCollection | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `name` | texto | sí | | | `data` | lista de [InteroperabilityData](/docs/api/objetos#InteroperabilityData) | sí | Máximo 50. | ### AirExtension | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `group` | texto | | | | `collections` | lista de [AirCollection](/docs/api/objetos#AirCollection) | sí | Máximo 50. | | `passengers` | lista de [AirCollection](/docs/api/objetos#AirCollection) | sí | Máximo 50. | ### AllowanceCharge | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `type` | `discount` \| `charge` | sí | | | `code` | texto | | Código (texto). | | `reason` | texto | | | | `percent` | decimal | | Número o texto decimal con punto («150000.50»). | | `amount` | decimal | | Número o texto decimal con punto («150000.50»). | | `base` | decimal | | Número o texto decimal con punto («150000.50»). | ### AlternativeCurrency | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | sí | Una tercera moneda (ni la de la factura ni COP). | | `exchange_rate` | decimal | sí | Unidades de esa moneda por 1 unidad de la moneda de la factura. | | `rate_date` | fecha (AAAA-MM-DD) | sí | | ### Balance El saldo prepago de Trevol. Es de la **cuenta**, no de la empresa: todas las empresas de la cuenta lo comparten (`shared: true`) y cualquier llave de cualquiera de ellas, de lectura o de escritura, `test` o `live`, ve el mismo. Solo lo que se emite en producción (llaves `live`) lo consume; la habilitación (`test`) no cobra. `available` es lo que se puede gastar (en pesos); `held`, lo apartado por documentos que la DIAN todavía valida; `overdraft`, cuánto puede bajar `available` de cero al apartar un documento; `low_threshold`, el umbral del aviso de saldo bajo (el 20 % de la última recarga; `null` sin recargas). `level`: `empty` con `available` ≤ 0, `low` por debajo de `low_threshold`, si no `ok`. Con `available` por debajo del precio de un documento, emitir en producción responde 402 `insufficient_balance`. El evento `balance.low` avisa por webhook. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `balance` | sí | | | `currency` | `COP` | sí | | | `available` | entero | sí | Lo que se puede gastar, en pesos (puede ser negativo). | | `held` | entero | sí | Apartado por documentos que la DIAN todavía valida. | | `overdraft` | entero | sí | Cuánto puede bajar `available` de cero al apartar un documento. | | `low_threshold` | entero (puede ser null) | sí | El umbral del aviso de saldo bajo; null sin recargas. | | `level` | `ok` \| `low` \| `empty` | sí | | | `updated_at` | fecha y hora (puede ser null) | sí | Cuándo cambió el saldo por última vez; null si nunca tuvo. | | `shared` | `true` | sí | Siempre true: el saldo es de la cuenta y lo comparten todas sus empresas. | ### BalanceEventData `balance.low` (solo producción): el saldo de la cuenta quedó bajo o se agotó. Lleva solo el nivel: el saldo es de la cuenta y lo comparten todas sus empresas. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `balance` | | | | `level` | `low` \| `depleted` | | | ### BalanceTransaction Un movimiento del saldo de la cuenta (un documento es una sola fila). | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | sí | Úsalo en `starting_after` para pedir la página siguiente. | | `object` | `balance_transaction` | sí | | | `type` | `document` \| `top_up` \| `bonus` \| `adjustment` \| `top_up_reversal` \| `license_fee` \| `expiration` \| `other` | sí | | | `status` | `charged` \| `pending` \| `released` \| `completed` | sí | De un documento: pending, charged o released. Lo demás: completed. | | `amount` | entero | sí | Lo que hizo con el disponible, en pesos y con signo (released: 0). | | `available_after` | entero (puede ser null) | sí | El disponible justo después. | | `description` | texto | sí | En español, como en la app. | | `document` | objeto (puede ser null) | sí | Solo en `type: document`. | | `company` | objeto (puede ser null) | sí | La empresa de la cuenta que emitió el documento. | | `reference` | texto (puede ser null) | | La referencia TRV-REC-… de una recarga o de su reverso. | | `created_at` | fecha y hora | sí | | ### Carrier | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `document_type` | texto | | Tipo de documento del transportador (tabla 13.2.1). | | `document_number` | texto | | Sin DV ni puntos; el DV lo calcula Trevol. | | `name` | texto | | | | `tax_responsibilities` | lista de texto | | Tabla 13.2.6.1 (O-13, O-15, O-23, O-47, R-99-PN). Máximo 20. | | `email` | texto | | | | `phone` | texto | | | | `address` | [ForeignAddress](/docs/api/objetos#ForeignAddress) | | | ### CertificateEventData `certificate.expiring` (los dos ambientes): el certificado de firma vence en 30, 15, 7 y 1 día. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `certificate` | | | | `expires_on` | fecha (AAAA-MM-DD) | | | | `days_left` | entero | | | ### CinemaExtension | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `address` | texto | sí | | | `cinema_room` | texto | | | | `screenings` | lista de [CinemaScreening](/docs/api/objetos#CinemaScreening) | sí | Máximo 50. | ### CinemaScreening | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `room` | texto | sí | | | `seats` | texto | sí | | | `screening` | texto | sí | | | `location` | texto | | | | `date` | fecha (AAAA-MM-DD) | | | | `time` | texto | | | ### Company La empresa de la llave, su ambiente y sus numeraciones activas. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `company` | | | | `id` | texto | | | | `nit` | texto (puede ser null) | | | | `check_digit` | entero (puede ser null) | | | | `name` | texto (puede ser null) | | La razón social. | | `trade_name` | texto (puede ser null) | | | | `status` | `enabled` \| `not_enabled` | | | | `environment` | `test` \| `live` \| `null` (puede ser null) | | | | `numbering` | lista de objeto | | Las numeraciones (resoluciones) activas del ambiente de la llave. | | `key` | objeto | | La llave con que se llamó. | | `capabilities` | objeto libre | | Lo que ESTA llave puede emitir ahora (documento soporte, equivalente, nómina, eventos RADIAN). Informativo: si una lectura falla, esa clave no aparece. | ### Consumption | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `unit` | texto | | Unidad de medida (tabla 13.3.6). Por defecto 94 (unidad). | | `quantity` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `subtotal` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `discounts` | lista de [ConsumptionAdjustment](/docs/api/objetos#ConsumptionAdjustment) | | Máximo 50. | | `charges` | lista de [ConsumptionAdjustment](/docs/api/objetos#ConsumptionAdjustment) | | Máximo 50. | | `meter` | [Meter](/docs/api/objetos#Meter) | | | ### ConsumptionAdjustment | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `reason` | texto | sí | | | `amount` | decimal | sí | Número o texto decimal con punto («150000.50»). | ### Contingency | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `physical_document` | [PhysicalDocument](/docs/api/objetos#PhysicalDocument) | | | ### Currency | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | sí | Moneda (tabla 13.3.3, ISO 4217), distinta de COP. | | `exchange_rate` | decimal | sí | TRM o tasa pactada: pesos por 1 unidad de la moneda (hasta 6 decimales). | | `rate_date` | fecha (AAAA-MM-DD) | sí | El día de la tasa; no puede ser posterior a la factura. | | `alternative` | [SupportCurrency](/docs/api/objetos#SupportCurrency) | | | ### Customer | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `final_consumer` | booleano | | | | `document_type` | texto | | Tipo de documento (tabla 13.2.1 de la DIAN): 13 CC, 31 NIT, 22 CE, 41 PA… | | `document_number` | texto | | Sin DV ni puntos; el DV lo calcula Trevol. | | `check_digit` | entero | | Opcional: si viene, tiene que ser el DV del NIT. | | `name` | texto | | | | `person_type` | `legal` \| `natural` | | Por defecto se deduce del NIT. | | `email` | texto | | | | `phone` | texto | | | | `address` | texto | | | | `city` | texto | | | | `municipality_code` | texto | | Código DANE del municipio (5 dígitos). | | `department_code` | texto | | | | `tax_responsibilities` | lista de texto | | Tabla 13.2.6.1 (O-13, O-15, O-23, O-47, R-99-PN). Máximo 20. | | `tax_scheme` | texto | | TaxScheme del adquiriente: 01, 04, ZA o ZZ (por defecto ZZ). | | `country` | texto | | País del cliente (tabla 13.4.1, ISO 3166-1 alfa-2). Distinto de "CO": cliente en el exterior, sin `municipality_code`; con `city`, `region` y `postal_code`. Solo en facturas. | | `region` | texto | | Departamento, estado o provincia del cliente en el exterior. Solo en facturas. | | `region_code` | texto | | Código de la región del cliente en el exterior (máximo 6). Solo en facturas. | | `postal_code` | texto | | Código postal del cliente en el exterior (máximo 10). Solo en facturas. | ### DeletedObject | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | sí | | | `object` | texto | sí | | | `deleted` | `true` | sí | | ### Delivery Con la versión 2026-10-22, el PDF, el ZIP y el correo se hacen en segundo plano después del 201: casi siempre llegan `pending` y se completan en segundos (GET o el evento `….delivered`). Si pides …/pdf o …/zip antes, se generan al momento. Con la 2026-10-21 se hacen antes del 201, como en la fase 1. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `status` | `pending` \| `completed` \| `null` (puede ser null) | | null si el documento no está `accepted`; `completed` con PDF, ZIP y un correo en estado final (sent, skipped, failed, bounced o complained). | | `pdf` | booleano | | | | `zip` | booleano | | | | `xml` | booleano | | | | `email` | objeto (puede ser null) | | | ### DeliveryTerms | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `incoterm` | texto | | | | `conditions` | texto | | | ### Document | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | | inv_… \| cn_… \| dn_… | | `object` | `invoice` \| `credit_note` \| `debit_note` | | | | `environment` | `test` \| `live` | | | | `status` | `pending` \| `sending` \| `accepted` \| `rejected` \| `error` \| `contingency` \| `processing` | | `processing` solo en la versión 2026-10-22: la DIAN no confirmó y Trevol sigue resolviendo el documento (el resultado llega por GET y por webhook; no crees otro documento para la misma venta). En 2026-10-21 ese documento se ve como `error`. | | `number` | texto (puede ser null) | | | | `prefix` | texto (puede ser null) | | | | `external_id` | texto (puede ser null) | | | | `origin` | `api` \| `app` \| `contapyme` | | | | `cufe` | texto (puede ser null) | | | | `cude` | texto (puede ser null) | | | | `qr` | texto (puede ser null) | | | | `issued_at` | fecha y hora (puede ser null) | | | | `customer` | [Customer](/docs/api/objetos#Customer) | | | | `items` | lista de [Item](/docs/api/objetos#Item) | | | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `invoice` | [InvoiceReference](/docs/api/objetos#InvoiceReference) | | | | `document_type` | texto | | Tipo de factura (tabla 13.1.3): 01 venta, 02 exportación. | | `operation_type` | texto | | Tipo de operación (tabla 13.1.5.1). | | `currency` | [Currency](/docs/api/objetos#Currency) | | | | `cop_totals` | booleano | | | | `delivery_terms` | [DeliveryTerms](/docs/api/objetos#DeliveryTerms) | | | | `shipment` | [Shipment](/docs/api/objetos#Shipment) | | | | `due_diligence` | texto | | Debida diligencia (tabla 18.3). | | `interoperability` | [Interoperability](/docs/api/objetos#Interoperability) | | | | `totals` | objeto (puede ser null) | | Montos como texto decimal con 2 decimales, siempre en pesos. Con moneda extranjera, `currency` trae el equivalente. | | `dian` | objeto | | | | `delivery` | [Delivery](/docs/api/objetos#Delivery) | | | | `created_at` | fecha y hora | | | | `updated_at` | fecha y hora (puede ser null) | | | ### DocumentEventData Resumen de un documento. Pide el documento completo con GET /v1/{invoices|credit_notes|debit_notes}/{id}. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `invoice` \| `credit_note` \| `debit_note` | sí | | | `id` | texto | sí | inv_… \| cn_… \| dn_… (el mismo de la respuesta de la API). | | `status` | `pending` \| `sending` \| `accepted` \| `rejected` \| `error` \| `contingency` | sí | Nunca `processing`: ese estado solo existe en las respuestas de la API. | | `environment` | `test` \| `live` | sí | | | `number` | texto (puede ser null) | | | | `prefix` | texto (puede ser null) | | | | `external_id` | texto (puede ser null) | | Tu identificador, solo en los documentos creados por la API. | | `origin` | `api` \| `app` \| `contapyme` | | | | `cufe` | texto (puede ser null) | | En las facturas. | | `cude` | texto (puede ser null) | | En las notas crédito y débito. | | `issued_at` | fecha y hora (puede ser null) | | | | `total` | texto (puede ser null) | | Texto decimal con dos decimales. | | `invoice` | objeto (puede ser null) | | En las notas: la factura de Trevol que corrigen. | | `dian` | objeto | sí | | | `delivery` | objeto | sí | | | `error` | objeto | | Solo en `….error`. El texto es fijo por código: nunca el mensaje de la DIAN. | ### DocumentReference | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | | | | `date` | fecha (AAAA-MM-DD) | | | ### EquivalentAdjustmentItem | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | | Código (texto). | | `description` | texto | sí | | | `unit` | texto | | Unidad de medida (tabla 13.3.6). Por defecto 94 (unidad). | | `quantity` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `unit_price` | decimal | sí | Precio unitario SIN impuestos. | | `taxes` | lista de [ItemTax](/docs/api/objetos#ItemTax) | | Impuestos de la línea: {code, rate} porcentual, {code: "01", excluded: true} o nominal {code, unit_amount, unit?, quantity?}. Máximo 12. | | `discounts` | lista de [ItemAllowance](/docs/api/objetos#ItemAllowance) | | Máximo 10. | | `charges` | lista de [ItemAllowance](/docs/api/objetos#ItemAllowance) | | Máximo 10. | | `free_of_charge` | [FreeOfCharge](/docs/api/objetos#FreeOfCharge) | | | | `standard_code` | [StandardCode](/docs/api/objetos#StandardCode) | | | | `brand` | texto | | | | `model` | texto | | | | `income` | `own` \| `collected` \| `third_party` | | Mandatos (tabla 16.16): "own" (por defecto), "collected" o "third_party". | | `mandator` | [ItemMandator](/docs/api/objetos#ItemMandator) | | | ### EquivalentDocument Un documento equivalente electrónico (tiquete POS 20 y los demás tipos de la tabla 16.3). Se expide como la factura: archivos `pdf`, `xml`, `zip` y `dian_response`. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | | pos_… (todos los tipos de documento equivalente) | | `object` | `equivalent_document` | | | | `environment` | `test` \| `live` | | | | `status` | `pending` \| `sending` \| `accepted` \| `rejected` \| `error` \| `contingency` \| `processing` \| `validating` | | `processing` solo en la versión 2026-10-22: la DIAN no confirmó y Trevol sigue resolviendo el documento (el resultado llega por GET y por webhook; no crees otro documento para la misma venta). En 2026-10-21 ese documento se ve como `error`. `validating`: el set de pruebas de la DIAN (habilitación) recibió el documento y todavía lo valida; no es un error, responde 202 en las dos versiones y Trevol solo consulta (nunca reenvía). | | `number` | texto (puede ser null) | | | | `prefix` | texto (puede ser null) | | | | `external_id` | texto (puede ser null) | | | | `origin` | `api` \| `app` \| `contapyme` | | | | `cude` | texto (puede ser null) | | El código único del documento (CUDE, 96 hex). | | `qr` | texto (puede ser null) | | | | `issued_at` | fecha y hora (puede ser null) | | | | `customer` | [Customer](/docs/api/objetos#Customer) | | | | `items` | lista de [EquivalentItem](/docs/api/objetos#EquivalentItem) | | | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `document_type` | `20` \| `25` \| `27` \| `30` \| `35` \| `40` \| `45` \| `50` \| `55` \| `60` | | Tipo de documento equivalente (tabla 16.3). | | `operation_type` | texto | | Tipo de operación (CustomizationID, tabla 16.4.1). | | `totals` | objeto (puede ser null) | | Montos como texto decimal con 2 decimales, siempre en pesos. Con moneda extranjera, `currency` trae el equivalente. | | `dian` | objeto | | | | `delivery` | [Delivery](/docs/api/objetos#Delivery) | | | | `created_at` | fecha y hora | | | | `updated_at` | fecha y hora (puede ser null) | | | | `notes` | lista de texto | | | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | | | `rounding` | booleano o decimal | | | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | | | `pos` | [Pos](/docs/api/objetos#Pos) | | | | `extension` | [Extension](/docs/api/objetos#Extension) | | | | `origin_note` | [OriginNote](/docs/api/objetos#OriginNote) | | | | `contingency` | objeto (puede ser null) | | | ### EquivalentDocumentAdjustment Una nota de ajuste de un documento equivalente (crédito 94 o débito 93). Se expide como la factura: archivos `pdf`, `xml`, `zip` y `dian_response`. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | | posa_… | | `object` | `equivalent_document_adjustment` | | | | `environment` | `test` \| `live` | | | | `status` | `pending` \| `sending` \| `accepted` \| `rejected` \| `error` \| `contingency` \| `processing` \| `validating` | | `processing` solo en la versión 2026-10-22: la DIAN no confirmó y Trevol sigue resolviendo el documento (el resultado llega por GET y por webhook; no crees otro documento para la misma venta). En 2026-10-21 ese documento se ve como `error`. `validating`: el set de pruebas de la DIAN (habilitación) recibió el documento y todavía lo valida; no es un error, responde 202 en las dos versiones y Trevol solo consulta (nunca reenvía). | | `number` | texto (puede ser null) | | | | `prefix` | texto (puede ser null) | | | | `external_id` | texto (puede ser null) | | | | `origin` | `api` \| `app` \| `contapyme` | | | | `cude` | texto (puede ser null) | | El código único del documento (CUDE, 96 hex). | | `qr` | texto (puede ser null) | | | | `issued_at` | fecha y hora (puede ser null) | | | | `customer` | [Customer](/docs/api/objetos#Customer) | | | | `items` | lista de [EquivalentAdjustmentItem](/docs/api/objetos#EquivalentAdjustmentItem) | | | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `totals` | objeto (puede ser null) | | Montos como texto decimal con 2 decimales, siempre en pesos. Con moneda extranjera, `currency` trae el equivalente. | | `dian` | objeto | | | | `delivery` | [Delivery](/docs/api/objetos#Delivery) | | | | `created_at` | fecha y hora | | | | `updated_at` | fecha y hora (puede ser null) | | | | `notes` | lista de texto | | | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | | | `rounding` | booleano o decimal | | | | `note_type` | `credit` \| `debit` | | | | `concept` | texto | | Concepto de la nota (tabla 16.6): 1 a 5. | | `description` | texto | | | | `equivalent_document` | [EquivalentDocumentReference](/docs/api/objetos#EquivalentDocumentReference) | | | ### EquivalentDocumentAdjustmentInput | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | Opcional con `equivalent_document.id`: se toma el adquirente del documento (si viene, tiene que ser el mismo). | | `items` | lista de [EquivalentAdjustmentItem](/docs/api/objetos#EquivalentAdjustmentItem) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `note_type` | `credit` \| `debit` | sí | "credit" (nota de ajuste crédito, 94) o "debit" (débito, 93). | | `concept` | texto | sí | Concepto de la nota (tabla 16.6): "1" devolución parcial, "2" anulación del documento (lleva exactamente sus valores), "3" rebaja o descuento, "4" ajuste de precio, "5" otros. | | `description` | texto | | Opcional (20 a 5000 caracteres). Sin ella, Trevol pone el texto de la tabla (el EXACTO en la nota débito: NADBF04). | | `equivalent_document` | [EquivalentDocumentReference](/docs/api/objetos#EquivalentDocumentReference) | sí | El documento que ajusta: {"id": "pos_…"} (de Trevol: validado, de la empresa y el ambiente de la llave) o {number, cude, issue_date, document_type, total?} si es de otra plataforma (`total` hace falta para anularlo). | ### EquivalentDocumentEventData Resumen de un documento equivalente electrónico o de su nota de ajuste (nunca datos del comprador ni líneas). Pide el documento completo con GET /v1/{equivalent_documents|equivalent_document_adjustments}/{id}. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `status` | `pending` \| `sending` \| `accepted` \| `rejected` \| `error` \| `contingency` | sí | Nunca `processing`: ese estado solo existe en las respuestas de la API. | | `environment` | `test` \| `live` | sí | | | `number` | texto (puede ser null) | | | | `prefix` | texto (puede ser null) | | | | `external_id` | texto (puede ser null) | | Tu identificador, solo en los documentos creados por la API. | | `origin` | `api` \| `app` \| `contapyme` | | | | `issued_at` | fecha y hora (puede ser null) | | | | `total` | texto (puede ser null) | | Texto decimal con dos decimales. | | `dian` | objeto | sí | | | `error` | objeto | | Solo en `….error`. El texto es fijo por código: nunca el mensaje de la DIAN. | | `object` | `equivalent_document` \| `equivalent_document_adjustment` | sí | | | `id` | texto | sí | pos_… \| posa_… | | `cude` | texto (puede ser null) | | Código único del documento. | | `document_type` | texto | | El tipo de documento equivalente (20 POS, 25, 27, 30…). Solo en `equivalent_document`. | | `equivalent_document` | objeto | | En la nota de ajuste: el documento equivalente que ajusta. | | `delivery` | objeto | sí | | ### EquivalentDocumentInput | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | El adquirente. Para consumidor final: {"final_consumer": true}. La relación de juegos (30) no lleva `customer`: el adquirente es la DIAN. | | `items` | lista de [EquivalentItem](/docs/api/objetos#EquivalentItem) | sí | Las líneas (1 a 500). Retenciones solo como autorretenciones del emisor (`self_withholding: true`). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `document_type` | texto | | Tipo de documento equivalente (tabla 16.3): "20" tiquete POS (por defecto), "25" boleta de cine, "27" espectáculos públicos, "30" juegos localizados y no localizados (se acepta "32"), "35" transporte de pasajeros, "40" peajes, "45" extracto, "50" tiquete aéreo, "55" bolsa de valores o "60" servicios públicos. | | `operation_type` | texto | | Tipo de operación (CustomizationID, tabla 16.4.1) del tipo de documento. Por defecto, el primero del tipo ("10" en el POS). | | `pos` | [Pos](/docs/api/objetos#Pos) | | Obligatorio en el tiquete POS (20) y prohibido en los demás: la caja que expide el tiquete y, opcional, los beneficios del comprador. | | `extension` | [Extension](/docs/api/objetos#Extension) | | La información propia del tipo, con UNA clave, la de su `document_type`: `cinema` (25), `show` (27), `games` (30), `transport` (35), `air` (50), `stock_exchange` (55) o `public_services` (60). Prohibida en el POS (20), peajes (40) y extracto (45). | | `prefix` | texto | | Solo en los tipos sin resolución (todos menos el POS): el prefijo del consecutivo propio (1 a 4 letras o dígitos). Por defecto, el del tipo (DEC, DEEP, DEJL, DETT, DEP, EXT, DETA, DEBV, SPD). | | `origin_note` | [OriginNote](/docs/api/objetos#OriginNote) | | La nota de ajuste que dio origen a este documento (opcional). | | `contingency` | [Contingency](/docs/api/objetos#Contingency) | | Solo la contingencia 07 (el emisor transcribe un documento físico): `physical_document` con number, date y type. La 08 (la DIAN no contestó) no se pide: la decide Trevol y el documento responde `202` con `status: contingency`. | | `numbering` | [Numbering](/docs/api/objetos#Numbering) | | Solo en el POS: el prefijo de la resolución POS que numera (una empresa con varias cajas o establecimientos tiene varias). Sin él, la más nueva vigente. Si no existe, `numbering_unavailable` (`no_resolution`). | ### EquivalentDocumentReference Uno de: objeto, objeto. ### EquivalentItem | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | | Código (texto). | | `description` | texto | sí | | | `unit` | texto | | Unidad de medida (tabla 13.3.6). Por defecto 94 (unidad). | | `quantity` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `unit_price` | decimal | sí | Precio unitario SIN impuestos. | | `taxes` | lista de [ItemTax](/docs/api/objetos#ItemTax) | | Impuestos de la línea: {code, rate} porcentual, {code: "01", excluded: true} o nominal {code, unit_amount, unit?, quantity?}. Máximo 12. | | `discounts` | lista de [ItemAllowance](/docs/api/objetos#ItemAllowance) | | Máximo 10. | | `charges` | lista de [ItemAllowance](/docs/api/objetos#ItemAllowance) | | Máximo 10. | | `withholdings` | lista de [ItemWithholding](/docs/api/objetos#ItemWithholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `free_of_charge` | [FreeOfCharge](/docs/api/objetos#FreeOfCharge) | | | | `standard_code` | [StandardCode](/docs/api/objetos#StandardCode) | | | | `brand` | texto | | | | `model` | texto | | | | `income` | `own` \| `collected` \| `third_party` | | Mandatos (tabla 16.16): "own" (por defecto), "collected" (ingreso recaudado para terceros) o "third_party" (facturado para terceros); los dos últimos con `mandator`. En 272 y 274 la línea del mandante va con "collected". | | `mandator` | [ItemMandator](/docs/api/objetos#ItemMandator) | | El mandante de un ingreso para terceros (con NIT en 272 y 274). | ### Event Exactamente lo que se manda al endpoint (los bytes firmados) y lo que devuelve GET /v1/events. El mismo `id` (también en la cabecera `webhook-id`) llega en cada reintento y en un reenvío manual: úsalo para no procesar dos veces el mismo evento. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | sí | | | `object` | `event` | sí | | | `type` | `invoice.accepted` \| `invoice.rejected` \| `invoice.error` \| `invoice.delivered` \| `invoice.email_bounced` \| `invoice.email_failed` \| `credit_note.accepted` \| `credit_note.rejected` \| `credit_note.error` \| `credit_note.delivered` \| `credit_note.email_bounced` \| `credit_note.email_failed` \| `debit_note.accepted` \| `debit_note.rejected` \| `debit_note.error` \| `debit_note.delivered` \| `debit_note.email_bounced` \| `debit_note.email_failed` \| `balance.low` \| `numbering.low` \| `numbering.expiring` \| `certificate.expiring` \| `support_document.accepted` \| `support_document.rejected` \| `support_document.error` \| `support_document_adjustment.accepted` \| `support_document_adjustment.rejected` \| `support_document_adjustment.error` \| `equivalent_document.accepted` \| `equivalent_document.rejected` \| `equivalent_document.error` \| `equivalent_document.delivered` \| `equivalent_document.email_bounced` \| `equivalent_document.email_failed` \| `equivalent_document_adjustment.accepted` \| `equivalent_document_adjustment.rejected` \| `equivalent_document_adjustment.error` \| `equivalent_document_adjustment.delivered` \| `equivalent_document_adjustment.email_bounced` \| `equivalent_document_adjustment.email_failed` \| `payroll.accepted` \| `payroll.rejected` \| `payroll.error` \| `payroll_adjustment.accepted` \| `payroll_adjustment.rejected` \| `payroll_adjustment.error` \| `radian_event.accepted` \| `radian_event.rejected` \| `radian_event.error` \| `webhook.test` | sí | | | `timestamp` | fecha y hora | sí | UTC. | | `environment` | `test` \| `live` | sí | | | `api_version` | texto | sí | La versión vigente más nueva de la API; el resumen del documento no cambia con la versión de la llave. | | `data` | uno de: [DocumentEventData](/docs/api/objetos#DocumentEventData), [SupportDocumentEventData](/docs/api/objetos#SupportDocumentEventData), [EquivalentDocumentEventData](/docs/api/objetos#EquivalentDocumentEventData), [PayrollEventData](/docs/api/objetos#PayrollEventData), [RadianEventData](/docs/api/objetos#RadianEventData), [BalanceEventData](/docs/api/objetos#BalanceEventData), [NumberingLowEventData](/docs/api/objetos#NumberingLowEventData), [NumberingExpiringEventData](/docs/api/objetos#NumberingExpiringEventData), [CertificateEventData](/docs/api/objetos#CertificateEventData), [TestEventData](/docs/api/objetos#TestEventData) | sí | | ### EventClaim | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | | Código (texto). | | `note` | texto | | | ### Extension La información propia del tipo de documento equivalente: UNA clave, la de su `document_type` (cinema 25, show 27, games 30, transport 35, air 50, stock_exchange 55, public_services 60). | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `cinema` | [CinemaExtension](/docs/api/objetos#CinemaExtension) | | | | `show` | [ShowExtension](/docs/api/objetos#ShowExtension) | | | | `games` | [GamesExtension](/docs/api/objetos#GamesExtension) | | | | `transport` | [TransportExtension](/docs/api/objetos#TransportExtension) | | | | `air` | [AirExtension](/docs/api/objetos#AirExtension) | | | | `stock_exchange` | [StockExchangeExtension](/docs/api/objetos#StockExchangeExtension) | | | | `public_services` | [PublicServicesExtension](/docs/api/objetos#PublicServicesExtension) | | | ### ForeignAddress | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `country` | texto | | País (tabla 13.4.1). Sin `country`: Colombia. | | `city` | texto | | Obligatoria fuera de Colombia. | | `region` | texto | | | | `region_code` | texto | | | | `postal_code` | texto | | | | `municipality_code` | texto | | Solo en Colombia (DANE, 5 dígitos). | | `street` | texto | | Calle y número. | ### FreeOfCharge | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `reference_price` | decimal | | Número o texto decimal con punto («150000.50»). | | `price_type` | texto | | | ### Game | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `type` | texto | sí | | | `total` | entero | sí | | ### GameCredit | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `serial` | texto | sí | | | `amount` | decimal | sí | Número o texto decimal con punto («150000.50»). | ### GamesExtension | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `venue` | [GamesVenue](/docs/api/objetos#GamesVenue) | sí | | | `games` | lista de [Game](/docs/api/objetos#Game) | sí | Máximo 2. | | `credits` | lista de [GameCredit](/docs/api/objetos#GameCredit) | | Máximo 500. | ### GamesVenue | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `address` | texto | sí | | | `deposit` | decimal | sí | Número o texto decimal con punto («150000.50»). | ### Interoperability | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `group` | texto | sí | | | `collections` | lista de [AirCollection](/docs/api/objetos#AirCollection) | sí | Máximo 10. | ### InteroperabilityCollection | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `name` | texto | sí | | | `data` | lista de [InteroperabilityData](/docs/api/objetos#InteroperabilityData) | sí | Máximo 50. | ### InteroperabilityData | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `name` | texto | sí | | | `value` | texto | | | ### InvoiceInput | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | | | `items` | lista de [Item](/docs/api/objetos#Item) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `document_type` | texto | | Tipo de factura (tabla 13.1.3): "01" venta (por defecto) o "02" exportación. La contingencia (03/04) todavía no está disponible en la API. | | `operation_type` | texto | | Tipo de operación (tabla 13.1.5.1): "10" estándar (por defecto), "09" AIU, "11" mandatos, "12" transporte de carga, "14" notarios, "15" compra o "16" venta de divisas, o "SS-CUFE" (sector salud, con `interoperability`). | | `currency` | [Currency](/docs/api/objetos#Currency) | | Moneda extranjera: los valores del pedido van en ESA moneda; la factura y el CUFE van en pesos (× la tasa). Sin `currency`, la factura es en pesos. | | `cop_totals` | booleano | | Con `currency`: informa la moneda y la tasa en la interoperabilidad («Exportación» / «Factura de Venta»). | | `delivery_terms` | [DeliveryTerms](/docs/api/objetos#DeliveryTerms) | | Condiciones de entrega de la exportación (tabla 13.3.7: INCOTERM). | | `shipment` | [Shipment](/docs/api/objetos#Shipment) | | El envío físico de la mercancía (fecha, dirección y transportador). No se llama `delivery`: esa clave de la respuesta es el estado de los archivos y del correo. | | `due_diligence` | texto | | Debida diligencia (tabla 18.3: "01"–"04"). Obligatoria en la compra y venta de divisas (15/16) y prohibida en los demás tipos de operación. | | `interoperability` | [Interoperability](/docs/api/objetos#Interoperability) | | Grupo de interoperabilidad (Suplemento H). Obligatorio con `operation_type` "SS-CUFE". | ### InvoiceReference Uno de: objeto, objeto. ### Item | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | | Código (texto). | | `description` | texto | sí | | | `unit` | texto | | Unidad de medida (tabla 13.3.6). Por defecto 94 (unidad). | | `quantity` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `unit_price` | decimal | sí | Precio unitario SIN impuestos. | | `taxes` | lista de [ItemTax](/docs/api/objetos#ItemTax) | | Impuestos de la línea: {code, rate} porcentual, {code: "01", excluded: true} o nominal {code, unit_amount, unit?, quantity?}. Máximo 12. | | `discounts` | lista de [ItemAllowance](/docs/api/objetos#ItemAllowance) | | Máximo 10. | | `charges` | lista de [ItemAllowance](/docs/api/objetos#ItemAllowance) | | Máximo 10. | | `withholdings` | lista de [ItemWithholding](/docs/api/objetos#ItemWithholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `free_of_charge` | [FreeOfCharge](/docs/api/objetos#FreeOfCharge) | | | | `standard_code` | [StandardCode](/docs/api/objetos#StandardCode) | | | | `brand` | texto | | | | `model` | texto | | | | `aiu` | texto | | AIU (solo con `operation_type` "09"): "A" administración, "I" imprevistos, "U" utilidad. Solo en facturas. | | `aiu_contract` | texto | | El objeto del contrato AIU: solo en la línea A, y obligatorio en ella. Solo en facturas. | | `income` | `own` \| `third_party` | | Mandatos (11): "own" (por defecto) o "third_party" (con `mandator`). Solo en facturas. | | `mandator` | [ItemMandator](/docs/api/objetos#ItemMandator) | | El mandante de un ingreso para terceros. Solo en facturas. | | `consignment` | [ItemConsignment](/docs/api/objetos#ItemConsignment) | | La remesa del RNDC (transporte de carga, 12). Solo en facturas. | | `notary` | [ItemNotary](/docs/api/objetos#ItemNotary) | | Matrícula inmobiliaria y valor comercial (notarios, 14). Solo en facturas. | | `properties` | lista de [ItemProperty](/docs/api/objetos#ItemProperty) | | Propiedades libres de la línea (hasta 20; no en el transporte de carga). Solo en facturas. Máximo 20. | ### ItemAllowance | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | | Código (texto). | | `reason` | texto | | | | `percent` | decimal | | Número o texto decimal con punto («150000.50»). | | `amount` | decimal | | Número o texto decimal con punto («150000.50»). | ### ItemConsignment | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `rndc_filing` | texto | sí | Número de radicación de la remesa en el RNDC (1 a 100 000 millones). | | `number` | texto | sí | Consecutivo interno de la remesa (alfanumérico, hasta 15 caracteres). | | `freight` | decimal | sí | Pesos, sin decimales (la ley los fija en pesos: no se convierte). | | `quantity` | decimal | sí | Cantidad transportada, sin decimales. | | `unit` | texto | sí | "KGM" kilogramos o "GLL" galones. | ### ItemMandator | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `document_type` | texto | sí | Tipo de documento (tabla 13.2.1 de la DIAN): 13 CC, 31 NIT, 22 CE, 41 PA… | | `document_number` | texto | sí | Sin DV ni puntos; el DV lo calcula Trevol. | | `name` | texto | sí | | ### ItemNotary | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `property_registration` | texto | sí | | | `commercial_value` | decimal | sí | Pesos: no se convierte. | ### ItemProperty | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `name` | texto | sí | | | `value` | texto | sí | | | `quantity` | decimal | | Número o texto decimal con punto («150000.50»). | | `unit` | texto | | Unidad de medida (tabla 13.3.6); solo con `quantity` (por defecto 94). | ### ItemTax | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | sí | Código (texto). | | `rate` | decimal | | Tarifa en % (ICA y ReteICA: por mil). | | `excluded` | booleano | | | | `unit_amount` | decimal | | Número o texto decimal con punto («150000.50»). | | `unit` | texto | | Unidad de medida (tabla 13.3.6). Por defecto 94 (unidad). | | `quantity` | decimal | | Número o texto decimal con punto («150000.50»). | | `volume_cc` | decimal | | Número o texto decimal con punto («150000.50»). | | `name` | texto | | | ### ItemWithholding | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | sí | Código (texto). | | `rate` | decimal | sí | Tarifa en % (ICA y ReteICA: por mil). | | `self_withholding` | booleano | | | ### Meter | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `number` | texto | sí | | | `previous_date` | fecha (AAAA-MM-DD) | | | | `previous_reading` | decimal | | Número o texto decimal con punto («150000.50»). | | `current_date` | fecha (AAAA-MM-DD) | | | | `current_reading` | decimal | | Número o texto decimal con punto («150000.50»). | | `method` | texto | | | | `days` | entero | | | ### NoteInput | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `customer` | [Customer](/docs/api/objetos#Customer) | | | | `items` | lista de [Item](/docs/api/objetos#Item) | sí | Las líneas (1 a 500). Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `period` | [Period](/docs/api/objetos#Period) | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `prepayments` | lista de [Prepayment](/docs/api/objetos#Prepayment) | | Anticipos (informativos: no restan del total). Máximo 20. | | `withholdings` | lista de [Withholding](/docs/api/objetos#Withholding) | | Retenciones (informativas): 05 ReteIVA, 06 ReteRenta, 07 ReteICA. Máximo 10. | | `rounding` | booleano o decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `concept` | texto | sí | Concepto de corrección: NC 1–6, ND 1–4. | | `description` | texto | | | | `invoice` | [InvoiceReference](/docs/api/objetos#InvoiceReference) | | | ### Numbering | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `prefix` | texto | sí | Prefijo de la resolución POS o de documento soporte que numera (el de `GET /v1/company` → `capabilities.equivalent_documents.pos_numbering` o `capabilities.support_documents.numbering`). | ### NumberingExpiringEventData `numbering.expiring` (solo producción): la resolución vence en 30 y en 7 días. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `numbering` | | | | `document` | `invoice` \| `support_document` \| `equivalent_document` | | De qué documento es la numeración (la fase 3 avisa también del documento soporte y del POS). | | `prefix` | texto (puede ser null) | | | | `resolution_number` | texto | | | | `valid_to` | fecha (AAAA-MM-DD) | | | | `days_left` | entero | | | ### NumberingLowEventData `numbering.low` (solo producción): a la resolución le quedan pocos números (10 % del rango, y otra vez con 10 números). | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `numbering` | | | | `document` | `invoice` \| `support_document` \| `equivalent_document` | | De qué documento es la numeración (la fase 3 avisa también del documento soporte y del POS). | | `prefix` | texto (puede ser null) | | | | `resolution_number` | texto | | | | `remaining` | entero | | | | `to` | entero | | | ### OriginNote | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `number` | texto | sí | | | `cude` | texto | sí | El CUDE de la nota de ajuste (96 hex). | | `issue_date` | fecha (AAAA-MM-DD) | sí | | ### Payment | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `form` | `cash` \| `credit` | sí | cash (contado) \| credit (crédito: con due_date o term_days). | | `term_days` | entero | | | | `due_date` | fecha (AAAA-MM-DD) | | | | `methods` | lista de [PaymentMethod](/docs/api/objetos#PaymentMethod) | | Medios de pago (tabla 13.3.4.2): 10 efectivo, 42 consignación, 47 transferencia… Máximo 5. | ### PaymentMethod | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | sí | Código (texto). | | `reference` | texto | | | ### Payroll Una nómina electrónica individual (102). Archivos `xml`, `zip` y `dian_response`. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | | pr_… | | `object` | `payroll` | | | | `environment` | `test` \| `live` | | | | `status` | `pending` \| `sending` \| `validating` \| `accepted` \| `rejected` \| `error` \| `processing` | | `validating`: el set de pruebas de la DIAN (habilitación) recibió la nómina y todavía la valida; no es un error, responde 202 en las dos versiones y Trevol solo consulta (nunca reenvía). `processing`: solo en la versión 2026-10-22, la DIAN no contestó y Trevol sigue resolviendo el documento. | | `number` | texto (puede ser null) | | | | `prefix` | texto (puede ser null) | | | | `external_id` | texto (puede ser null) | | | | `origin` | `api` \| `app` | | | | `cune` | texto (puede ser null) | | El código único de la nómina (CUNE, 96 hex). | | `qr` | texto (puede ser null) | | | | `issued_at` | fecha y hora (puede ser null) | | | | `submission` | `sync` \| `test_set` \| `null` (puede ser null) | | `sync`: SendNominaSync; `test_set`: el set de pruebas de habilitación. | | `period` | [PayrollPeriod](/docs/api/objetos#PayrollPeriod) | | | | `pay_period` | texto | | | | `currency` | texto | | | | `exchange_rate` | decimal | | Tasa de cambio a pesos. Obligatoria con una moneda distinta de COP y prohibida con COP. | | `novelty` | [PayrollNovelty](/docs/api/objetos#PayrollNovelty) | | | | `notes` | lista de texto | | Máximo 20. | | `employee` | [PayrollEmployee](/docs/api/objetos#PayrollEmployee) | | | | `payment` | [PayrollPayment](/docs/api/objetos#PayrollPayment) | | | | `payment_dates` | lista de fecha (AAAA-MM-DD) | | Máximo 31. | | `earnings` | [PayrollEarnings](/docs/api/objetos#PayrollEarnings) | | | | `deductions` | [PayrollDeductions](/docs/api/objetos#PayrollDeductions) | | | | `rounding` | decimal | | Redondeo hasta 2.00; no suma a los totales. | | `generation_place` | [PayrollPlace](/docs/api/objetos#PayrollPlace) | | | | `employer` | [PayrollEmployer](/docs/api/objetos#PayrollEmployer) | | | | `totals` | objeto (puede ser null) | | Montos como texto decimal con 2 decimales. Null en un ajuste Eliminar. | | `dian` | objeto | | | | `files` | objeto | | Qué archivos hay (…/xml, …/zip, …/dian_response). No hay PDF. | | `created_at` | fecha y hora | | | | `updated_at` | fecha y hora (puede ser null) | | | ### PayrollAdjustment Una nota de ajuste de nómina electrónica (103: Reemplazar o Eliminar). Archivos `xml`, `zip` y `dian_response`. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | | pra_… | | `object` | `payroll_adjustment` | | | | `environment` | `test` \| `live` | | | | `status` | `pending` \| `sending` \| `validating` \| `accepted` \| `rejected` \| `error` \| `processing` | | `validating`: el set de pruebas de la DIAN (habilitación) recibió la nómina y todavía la valida; no es un error, responde 202 en las dos versiones y Trevol solo consulta (nunca reenvía). `processing`: solo en la versión 2026-10-22, la DIAN no contestó y Trevol sigue resolviendo el documento. | | `number` | texto (puede ser null) | | | | `prefix` | texto (puede ser null) | | | | `external_id` | texto (puede ser null) | | | | `origin` | `api` \| `app` | | | | `cune` | texto (puede ser null) | | El código único de la nómina (CUNE, 96 hex). | | `qr` | texto (puede ser null) | | | | `issued_at` | fecha y hora (puede ser null) | | | | `submission` | `sync` \| `test_set` \| `null` (puede ser null) | | `sync`: SendNominaSync; `test_set`: el set de pruebas de habilitación. | | `type` | `replace` \| `delete` \| `null` (puede ser null) | | | | `predecessor` | [PayrollReference](/docs/api/objetos#PayrollReference) (puede ser null) | | | | `period` | [PayrollPeriod](/docs/api/objetos#PayrollPeriod) | | | | `pay_period` | texto | | | | `currency` | texto | | | | `exchange_rate` | decimal | | Tasa de cambio a pesos. Obligatoria con una moneda distinta de COP y prohibida con COP. | | `notes` | lista de texto | | Máximo 20. | | `employee` | [PayrollEmployee](/docs/api/objetos#PayrollEmployee) | | | | `payment` | [PayrollPayment](/docs/api/objetos#PayrollPayment) | | | | `payment_dates` | lista de fecha (AAAA-MM-DD) | | Máximo 31. | | `earnings` | [PayrollEarnings](/docs/api/objetos#PayrollEarnings) | | | | `deductions` | [PayrollDeductions](/docs/api/objetos#PayrollDeductions) | | | | `rounding` | decimal | | Redondeo hasta 2.00; no suma a los totales. | | `generation_place` | [PayrollPlace](/docs/api/objetos#PayrollPlace) | | | | `employer` | [PayrollEmployer](/docs/api/objetos#PayrollEmployer) | | | | `totals` | objeto (puede ser null) | | Montos como texto decimal con 2 decimales. Null en un ajuste Eliminar. | | `dian` | objeto | | | | `files` | objeto | | Qué archivos hay (…/xml, …/zip, …/dian_response). No hay PDF. | | `created_at` | fecha y hora | | | | `updated_at` | fecha y hora (puede ser null) | | | ### PayrollAdjustmentInput | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. En las nóminas es único entre las nóminas y sus ajustes (aparte de las facturas y los eventos). | | `type` | `replace` \| `delete` | sí | "replace" (Reemplazar: el ajuste lleva todos los datos de la nómina menos `novelty`) o "delete" (Eliminar: solo `predecessor`, `notes`, `generation_place` y `employer`). | | `predecessor` | [PayrollReference](/docs/api/objetos#PayrollReference) | sí | El documento que se ajusta: {"id": "pr_…"} (una nómina de Trevol) o {"id": "pra_…"} (otro ajuste, el último de la cadena), o sus datos {number, cune, issue_date}. | | `period` | [PayrollPeriod](/docs/api/objetos#PayrollPeriod) | | | | `pay_period` | texto | | | | `currency` | texto | | | | `exchange_rate` | decimal | | Número o texto decimal con punto («150000.50»). | | `notes` | lista de texto | | Máximo 20. | | `employee` | [PayrollEmployee](/docs/api/objetos#PayrollEmployee) | | | | `payment` | [PayrollPayment](/docs/api/objetos#PayrollPayment) | | | | `payment_dates` | lista de fecha (AAAA-MM-DD) | | Máximo 31. | | `earnings` | [PayrollEarnings](/docs/api/objetos#PayrollEarnings) | | | | `deductions` | [PayrollDeductions](/docs/api/objetos#PayrollDeductions) | | | | `rounding` | decimal | | true = redondeo al peso; o un valor entre -2.00 y 2.00. | | `generation_place` | [PayrollPlace](/docs/api/objetos#PayrollPlace) | | | | `employer` | [PayrollEmployer](/docs/api/objetos#PayrollEmployer) | | | ### PayrollBasic | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `days` | entero | sí | | | `amount` | decimal | sí | Número o texto decimal con punto («150000.50»). | ### PayrollCompensatedVacation | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `days` | entero | sí | | | `amount` | decimal | sí | Número o texto decimal con punto («150000.50»). | ### PayrollCompensation | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `ordinary` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `extraordinary` | decimal | sí | Número o texto decimal con punto («150000.50»). | ### PayrollDeductions | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `health` | [PayrollPercentAmount](/docs/api/objetos#PayrollPercentAmount) | sí | Salud (obligatoria). | | `pension` | [PayrollPercentAmount](/docs/api/objetos#PayrollPercentAmount) | sí | Pensión (obligatoria). | | `solidarity_fund` | [PayrollSolidarityFund](/docs/api/objetos#PayrollSolidarityFund) | | | | `unions` | lista de [PayrollPercentAmount](/docs/api/objetos#PayrollPercentAmount) | | Máximo 100. | | `sanctions` | lista de [PayrollSanction](/docs/api/objetos#PayrollSanction) | | Máximo 100. | | `payroll_loans` | lista de [PayrollLoan](/docs/api/objetos#PayrollLoan) | | Máximo 100. | | `third_party_payments` | lista de decimal | | Máximo 100. | | `advances` | lista de decimal | | Máximo 100. | | `other` | lista de decimal | | Máximo 100. | | `voluntary_pension` | decimal | | Número o texto decimal con punto («150000.50»). | | `withholding_tax` | decimal | | Número o texto decimal con punto («150000.50»). | | `afc` | decimal | | Número o texto decimal con punto («150000.50»). | | `cooperative` | decimal | | Número o texto decimal con punto («150000.50»). | | `tax_garnishment` | decimal | | Número o texto decimal con punto («150000.50»). | | `complementary_plan` | decimal | | Número o texto decimal con punto («150000.50»). | | `education` | decimal | | Número o texto decimal con punto («150000.50»). | | `refund` | decimal | | Número o texto decimal con punto («150000.50»). | | `debt` | decimal | | Número o texto decimal con punto («150000.50»). | ### PayrollDisability | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `start` | fecha (AAAA-MM-DD) | | | | `end` | fecha (AAAA-MM-DD) | | | | `days` | entero | sí | | | `type` | texto | sí | Tabla 5.5.6: "1" común, "2" profesional, "3" laboral. | | `amount` | decimal | sí | Número o texto decimal con punto («150000.50»). | ### PayrollEarnings | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `basic` | [PayrollBasic](/docs/api/objetos#PayrollBasic) | sí | El sueldo básico (obligatorio). | | `transport` | lista de [PayrollTransport](/docs/api/objetos#PayrollTransport) | | Máximo 100. | | `overtime` | lista de [PayrollOvertime](/docs/api/objetos#PayrollOvertime) | | Máximo 500. | | `vacation` | [PayrollVacation](/docs/api/objetos#PayrollVacation) | | | | `service_bonus` | [PayrollServiceBonus](/docs/api/objetos#PayrollServiceBonus) | | | | `severance` | [PayrollSeverance](/docs/api/objetos#PayrollSeverance) | | | | `disabilities` | lista de [PayrollDisability](/docs/api/objetos#PayrollDisability) | | Máximo 100. | | `leaves` | [PayrollLeaves](/docs/api/objetos#PayrollLeaves) | | | | `bonuses` | lista de [PayrollSalaryAmounts](/docs/api/objetos#PayrollSalaryAmounts) | | Máximo 100. | | `allowances` | lista de [PayrollSalaryAmounts](/docs/api/objetos#PayrollSalaryAmounts) | | Máximo 100. | | `strikes` | lista de [PayrollUnpaidPeriod](/docs/api/objetos#PayrollUnpaidPeriod) | | Máximo 100. | | `other_concepts` | lista de [PayrollOtherConcept](/docs/api/objetos#PayrollOtherConcept) | | Máximo 100. | | `compensations` | lista de [PayrollCompensation](/docs/api/objetos#PayrollCompensation) | | Máximo 100. | | `epctv_bonuses` | lista de [PayrollEpctvBonus](/docs/api/objetos#PayrollEpctvBonus) | | Máximo 100. | | `commissions` | lista de decimal | | Máximo 100. | | `third_party_payments` | lista de decimal | | Máximo 100. | | `advances` | lista de decimal | | Máximo 100. | | `endowment` | decimal | | Número o texto decimal con punto («150000.50»). | | `sustenance_support` | decimal | | Número o texto decimal con punto («150000.50»). | | `telework` | decimal | | Número o texto decimal con punto («150000.50»). | | `retirement_bonus` | decimal | | Número o texto decimal con punto («150000.50»). | | `indemnity` | decimal | | Número o texto decimal con punto («150000.50»). | | `refund` | decimal | | Número o texto decimal con punto («150000.50»). | ### PayrollEmployee | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `type` | texto | sí | Tipo de trabajador (tabla 5.5.3). | | `subtype` | texto | | | | `high_risk_pension` | booleano | | | | `document_type` | texto | sí | Tipo de documento del trabajador (tabla 5.2.1). | | `document_number` | texto | sí | Solo dígitos, sin puntos ni comas ni espacios. | | `first_surname` | texto | sí | | | `second_surname` | texto | sí | Obligatorio (NIE047): la DIAN lo exige aunque la base no. | | `first_name` | texto | sí | | | `other_names` | texto | | | | `workplace` | [PayrollWorkplace](/docs/api/objetos#PayrollWorkplace) | sí | | | `integral_salary` | booleano | | | | `contract_type` | texto | sí | Tipo de contrato (tabla 5.5.2). | | `salary` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `code` | texto | | El código interno del empleado (filtra las listas con `employee_code`; no es un dato de identificación). | ### PayrollEmployer | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `first_surname` | texto | sí | | | `second_surname` | texto | sí | | | `first_name` | texto | sí | | | `other_names` | texto | | | ### PayrollEpctvBonus | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `salary_payment` | decimal | | Número o texto decimal con punto («150000.50»). | | `non_salary_payment` | decimal | | Número o texto decimal con punto («150000.50»). | | `food_salary` | decimal | | Número o texto decimal con punto («150000.50»). | | `food_non_salary` | decimal | | Número o texto decimal con punto («150000.50»). | ### PayrollEventData Resumen de una nómina electrónica (102) o de su nota de ajuste (103): SIN el trabajador (ni su documento, nombre o salario) y SIN montos. Pide el documento completo con GET /v1/{payrolls|payroll_adjustments}/{id}. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `payroll` \| `payroll_adjustment` | sí | | | `id` | texto | sí | pr_… \| pra_… | | `status` | `pending` \| `sending` \| `validating` \| `accepted` \| `rejected` \| `error` | sí | | | `environment` | `test` \| `live` | sí | | | `number` | texto (puede ser null) | | | | `prefix` | texto (puede ser null) | | | | `external_id` | texto (puede ser null) | | | | `origin` | `api` \| `app` | | | | `cune` | texto (puede ser null) | | | | `issued_at` | fecha y hora (puede ser null) | | | | `type` | `replace` \| `delete` \| `null` (puede ser null) | | Solo en la nota de ajuste. | | `predecessor` | objeto (puede ser null) | | | | `submission` | `sync` \| `test_set` \| `null` (puede ser null) | | | | `dian` | objeto | sí | | | `error` | objeto | | Solo en `….error`. Texto fijo por código. | ### PayrollInput | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. En las nóminas es único entre las nóminas y sus ajustes (aparte de las facturas y los eventos). | | `period` | [PayrollPeriod](/docs/api/objetos#PayrollPeriod) | sí | El periodo que se liquida y el tiempo laborado (`time_worked` se calcula si no se manda: años de 360 días y meses de 30). | | `pay_period` | texto | sí | Periodo de nómina (tabla 5.5.1): "1" semanal, "2" decenal, "3" catorcenal, "4" quincenal, "5" mensual, "6" otro. | | `currency` | texto | | Moneda (ISO 4217). Por defecto COP; con otra va `exchange_rate`. | | `exchange_rate` | decimal | | Tasa de cambio a pesos. Obligatoria con una moneda distinta de COP y prohibida con COP. | | `novelty` | [PayrollNovelty](/docs/api/objetos#PayrollNovelty) | | El CUNE del documento con la novedad contractual, si la hay. Solo en la nómina. | | `notes` | lista de texto | | Máximo 20. | | `employee` | [PayrollEmployee](/docs/api/objetos#PayrollEmployee) | sí | El trabajador. Sus datos solo salen en las respuestas a esta llave (la del empleador): nunca en webhooks, en el registro de peticiones ni en lo que se guarda de la idempotencia. | | `payment` | [PayrollPayment](/docs/api/objetos#PayrollPayment) | sí | | | `payment_dates` | lista de fecha (AAAA-MM-DD) | sí | Las fechas de pago (1 a 31). Máximo 31. | | `earnings` | [PayrollEarnings](/docs/api/objetos#PayrollEarnings) | sí | | | `deductions` | [PayrollDeductions](/docs/api/objetos#PayrollDeductions) | sí | | | `rounding` | decimal | | Redondeo hasta 2.00; no suma a los totales. | | `generation_place` | [PayrollPlace](/docs/api/objetos#PayrollPlace) | | Dónde se generó el documento. Por defecto, el municipio de la empresa. | | `employer` | [PayrollEmployer](/docs/api/objetos#PayrollEmployer) | | Solo si la empresa es persona natural: sus apellidos y nombres (si la razón social no se separa bien en «apellido1 apellido2 nombre1»). | ### PayrollLeaves | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `maternity_paternity` | lista de [PayrollPaidPeriod](/docs/api/objetos#PayrollPaidPeriod) | | Máximo 100. | | `paid` | lista de [PayrollPaidPeriod](/docs/api/objetos#PayrollPaidPeriod) | | Máximo 100. | | `unpaid` | lista de [PayrollUnpaidPeriod](/docs/api/objetos#PayrollUnpaidPeriod) | | Máximo 100. | ### PayrollLoan | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `description` | texto | sí | | | `amount` | decimal | sí | Número o texto decimal con punto («150000.50»). | ### PayrollNovelty | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `cune` | texto | sí | | ### PayrollOtherConcept | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `description` | texto | sí | | | `salary` | decimal | | Número o texto decimal con punto («150000.50»). | | `non_salary` | decimal | | Número o texto decimal con punto («150000.50»). | ### PayrollOvertime | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `type` | texto | sí | Tabla 5.5.5: HED, HEN, HRN, HEDDF, HRDDF, HENDF, HRNDF. El porcentaje sale de la tabla. | | `quantity` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `amount` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `start` | texto | | AAAA-MM-DDTHH:MM:SS (NIE074). | | `end` | texto | | AAAA-MM-DDTHH:MM:SS (NIE074). | | `percent` | decimal | | Número o texto decimal con punto («150000.50»). | ### PayrollPaidPeriod | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `start` | fecha (AAAA-MM-DD) | | | | `end` | fecha (AAAA-MM-DD) | | | | `days` | entero | sí | | | `amount` | decimal | sí | Número o texto decimal con punto («150000.50»). | ### PayrollPayment | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `form` | texto | | Forma de pago (tabla 5.3.3.1); por defecto "1" (contado). | | `method` | texto | sí | Medio de pago (tabla 5.3.3.2). | | `bank` | texto | | | | `account_type` | texto | | | | `account_number` | texto | | | ### PayrollPercentAmount | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `percent` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `amount` | decimal | sí | Número o texto decimal con punto («150000.50»). | ### PayrollPeriod | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `hire_date` | fecha (AAAA-MM-DD) | sí | | | `start` | fecha (AAAA-MM-DD) | sí | | | `end` | fecha (AAAA-MM-DD) | sí | | | `termination_date` | fecha (AAAA-MM-DD) | | | | `time_worked` | entero | | | ### PayrollPlace | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `country` | texto | | | | `department_code` | texto | | | | `municipality_code` | texto | | Código DANE del municipio (5 dígitos). | ### PayrollReference Uno de: objeto, objeto. ### PayrollSalaryAmounts | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `salary` | decimal | | Número o texto decimal con punto («150000.50»). | | `non_salary` | decimal | | Número o texto decimal con punto («150000.50»). | ### PayrollSanction | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `public` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `private` | decimal | sí | Número o texto decimal con punto («150000.50»). | ### PayrollServiceBonus | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `days` | entero | sí | | | `amount` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `non_salary_amount` | decimal | | Número o texto decimal con punto («150000.50»). | ### PayrollSeverance | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `amount` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `percent` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `interest` | decimal | sí | Número o texto decimal con punto («150000.50»). | ### PayrollSolidarityFund | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `percent` | decimal | | Número o texto decimal con punto («150000.50»). | | `amount` | decimal | | Número o texto decimal con punto («150000.50»). | | `subsistence_percent` | decimal | | Número o texto decimal con punto («150000.50»). | | `subsistence_amount` | decimal | | Número o texto decimal con punto («150000.50»). | ### PayrollTransport | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `allowance` | decimal | | Número o texto decimal con punto («150000.50»). | | `salary_per_diem` | decimal | | Número o texto decimal con punto («150000.50»). | | `non_salary_per_diem` | decimal | | Número o texto decimal con punto («150000.50»). | ### PayrollUnpaidPeriod | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `start` | fecha (AAAA-MM-DD) | | | | `end` | fecha (AAAA-MM-DD) | | | | `days` | entero | sí | | ### PayrollVacation | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `regular` | lista de [PayrollPaidPeriod](/docs/api/objetos#PayrollPaidPeriod) | | Máximo 100. | | `compensated` | lista de [PayrollCompensatedVacation](/docs/api/objetos#PayrollCompensatedVacation) | | Máximo 100. | ### PayrollWorkplace | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `country` | texto | | | | `department_code` | texto | | | | `municipality_code` | texto | | Código DANE del municipio (5 dígitos). | | `address` | texto | | | ### Period | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `start` | fecha (AAAA-MM-DD) | | | | `end` | fecha (AAAA-MM-DD) | | | ### PhysicalDocument | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `number` | texto | | | | `date` | fecha (AAAA-MM-DD) | | | | `type` | texto | | Codificación propia del tipo del documento físico (hasta 10 caracteres). Por defecto, FISICO. | ### Pos | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `register` | [PosRegister](/docs/api/objetos#PosRegister) | sí | | | `buyer` | [PosBuyer](/docs/api/objetos#PosBuyer) | | | ### PosBuyer | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | | Por defecto, el documento del adquirente. | | `name` | texto | | Por defecto, el nombre del adquirente. | | `points` | decimal | | Puntos del comprador. Por defecto, 0. | ### PosRegister | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `plate` | texto | sí | Placa de inventario de la caja (DEPD26). | | `location` | texto | sí | Ubicación de la caja (DEPD28). | | `cashier` | texto | sí | Nombre del cajero o vendedor (DEPD30). | | `type` | texto | sí | Tipo de caja (DEPD32). | | `sale_code` | texto | | Código de la venta. Por defecto, el número del documento (DEPD34). | ### Prepayment | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | sí | | | `amount` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `received_date` | fecha (AAAA-MM-DD) | sí | | | `paid_date` | fecha (AAAA-MM-DD) | | | | `instructions` | texto | | | ### Preview El resultado de calcular sin emitir: el documento que se emitiría, con sus totales. No gasta numeración, saldo ni le manda nada a la DIAN. Lleva además los campos del pedido normalizados (cambian de un tipo de documento a otro). | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `preview` | | | | `document` | texto | | El `object` del documento que se emitiría (`invoice`, `credit_note`, `payroll`…). | | `environment` | `test` \| `live` \| `null` (puede ser null) | | | | `totals` | objeto libre | | Los totales calculados: la misma forma que `totals` del documento (en nómina, devengados, deducciones y total del comprobante). | ### Problem | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `type` | texto | sí | | | `title` | texto | sí | | | `status` | entero | sí | | | `code` | `authentication_required` \| `certificate_problem` \| `company_not_enabled` \| `dian_rejected` \| `dian_unavailable` \| `duplicate_document` \| `external_id_in_use` \| `file_unavailable` \| `idempotency_key_required` \| `idempotency_key_reused` \| `insufficient_balance` \| `internal_error` \| `invalid_api_key` \| `invalid_json` \| `invalid_request` \| `invalid_state` \| `method_not_allowed` \| `not_found` \| `numbering_unavailable` \| `permission_denied` \| `previous_event_pending` \| `rate_limited` \| `request_in_progress` \| `tacit_acceptance_not_due` \| `top_up_above_maximum` \| `top_up_below_minimum` \| `top_ups_unavailable` \| `unsupported_version` \| `validation_error` \| `webhook_limit_reached` | sí | | | `detail` | texto | | | | `retryable` | booleano | sí | | | `retry_after` | entero | | | | `request_id` | texto | | | | `errors` | lista de objeto | | | | `document` | [Document](/docs/api/objetos#Document) | | | | `existing_id` | texto | | En `external_id_in_use` y `duplicate_document`: el id (con el prefijo de su clase) del recurso que ya existe. | | `reason` | `missing_legal_representative` \| `missing_pin` \| `missing_software` \| `missing_technical_key` \| `no_resolution` \| `payroll_not_enabled` \| `prefix_in_use` \| `resolution_exhausted` \| `resolution_expired` \| `resolution_not_yet_valid` | | En `company_not_enabled` (`missing_legal_representative`, `missing_pin`, `missing_software`, `payroll_not_enabled`) y en `numbering_unavailable` (`missing_technical_key`, `no_resolution`, `prefix_in_use`, `resolution_exhausted`, `resolution_expired`, `resolution_not_yet_valid`): por qué. | | `previous` | texto | | En `previous_event_pending`: el evento que la DIAN todavía no muestra. | | `minimum_cop` | entero (puede ser null) | | En `top_up_below_minimum`: la recarga mínima, en pesos. | | `maximum_cop` | entero (puede ser null) | | En `top_up_above_maximum`: la recarga máxima, en pesos. | | `limit` | texto | | En `rate_limited`: qué límite se pasó. | | `available_from` | fecha (AAAA-MM-DD) (puede ser null) | | En `tacit_acceptance_not_due`: desde cuándo procede la aceptación tácita. `null` si la DIAN todavía no muestra el recibo del bien (032) de la compradora: consulta de nuevo en unos minutos con `GET /v1/invoices/{id}/tacit_acceptance`. | | `rules` | lista de objeto libre | | En `tacit_acceptance_not_due`: las reglas (LGC…) que la DIAN aplicaría; las locales llevan `local: true`. | ### PublicServicesExtension | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `service` | texto | sí | | | `company` | texto | sí | | | `reason` | texto | | | | `contract` | texto | sí | | | `use` | texto | sí | | | `stratum` | texto | sí | | | `payment_due_date` | fecha (AAAA-MM-DD) | | | | `payment_reference` | texto | | | | `due_date` | fecha (AAAA-MM-DD) | | | | `period` | fecha (AAAA-MM-DD) | | | | `cycle_days` | entero | | | | `notes` | lista de texto | | Máximo 10. | | `consumptions` | lista de [Consumption](/docs/api/objetos#Consumption) | sí | Máximo 50. | | `third_parties` | lista de [ThirdPartyCharge](/docs/api/objetos#ThirdPartyCharge) | | Máximo 20. | ### RadianEvent Un evento RADIAN 030–034 sobre una factura. Solo archivo `xml`. Un rechazo es final. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | | | | `object` | `radian_event` | | | | `environment` | `test` \| `live` | | | | `status` | `pending` \| `sending` \| `validating` \| `accepted` \| `rejected` \| `error` \| `processing` | | `validating`: la DIAN respondió 98 («en proceso de validación»); no es un error, responde 202 en las dos versiones y Trevol solo consulta (el 98 significa «no reenvíes»). `processing`: solo en la 2026-10-22, un envío en duda o el evento anterior que aún no aparece: Trevol lo sigue resolviendo. `error` incluye el envío en duda de la 2026-10-21. | | `event_code` | `030` \| `031` \| `032` \| `033` \| `034` | | | | `number` | texto (puede ser null) | | | | `prefix` | texto (puede ser null) | | | | `external_id` | texto (puede ser null) | | | | `origin` | `api` \| `app` | | | | `cude` | texto (puede ser null) | | | | `issued_at` | fecha y hora (puede ser null) | | | | `invoice` | objeto | | 030–033: la factura recibida (con el emisor). 034: {id, cufe, number} de la factura propia; sin `id` si no se emitió con Trevol (la trajo la sincronización con la DIAN). | | `claim` | [EventClaim](/docs/api/objetos#EventClaim) (puede ser null) | | | | `software` | `own` \| `dian_free` | | | | `provider` | `dian` \| `company` | | | | `dian` | objeto | | | | `already_registered` | booleano | | true: GetStatusEvent ya mostraba el CUDE de ESTE evento (un envío en duda que sí llegó); se cerró `accepted` sin reenviar. | | `already_registered_elsewhere` | booleano | | true: la DIAN rechazó con LGC01, la factura ya tiene ese evento (quizá de otro software); no hace falta enviarlo. | | `tacit` | booleano | | 033 rechazada con DC24c: la factura ya quedó aceptada tácitamente. | | `created_at` | fecha y hora | | | | `updated_at` | fecha y hora (puede ser null) | | | ### RadianEventData Resumen de un evento RADIAN (030–034): sin nombres ni NIT del emisor de la factura. Pide el evento completo con GET /v1/radian_events/{id}. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `radian_event` | sí | | | `id` | texto | sí | | | `status` | `pending` \| `sending` \| `validating` \| `accepted` \| `rejected` \| `error` | sí | | | `environment` | `test` \| `live` | sí | | | `event_code` | `030` \| `031` \| `032` \| `033` \| `034` | sí | | | `number` | texto (puede ser null) | | | | `prefix` | texto (puede ser null) | | | | `external_id` | texto (puede ser null) | | | | `origin` | `api` \| `app` | | | | `cude` | texto (puede ser null) | | | | `issued_at` | fecha y hora (puede ser null) | | | | `invoice` | objeto | | | | `dian` | objeto | sí | | | `error` | objeto | | Solo en `….error`. Texto fijo por código. | ### RadianEventInput | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. En los eventos es único entre los eventos (aparte de las facturas y las nóminas). | | `event_code` | texto | sí | "030" acuse de recibo, "031" reclamo, "032" recibo del bien o servicio, "033" aceptación expresa (las cuatro sobre una factura RECIBIDA) o "034" aceptación tácita (sobre una factura PROPIA a crédito). Los 035 a 051 necesitan un participante RADIAN y no están disponibles. | | `invoice` | [ReceivedInvoice](/docs/api/objetos#ReceivedInvoice) | | 030–033: la factura RECIBIDA sobre la que va el evento (CUFE, número y quién la emitió). 034: la factura PROPIA por su CUFE, `{cufe, number?}` (solo esos dos), en lugar de `invoice_id`: sirve también para las facturas que no se emitieron con Trevol (las trae la sincronización con la DIAN). Si el CUFE es de una factura de Trevol, es lo mismo que mandar su `invoice_id`. | | `invoice_id` | texto | | 034: el id (`inv_…`) de una factura de Trevol: propia, validada, a crédito y que no sea de consumidor final. Va aparte de `invoice` para que cada esquema sea plano en los SDK. Para una factura que no se emitió con Trevol, usa `invoice: {cufe}` (exactamente uno de los dos). | | `claim` | [EventClaim](/docs/api/objetos#EventClaim) | | Solo en el reclamo (031) y obligatorio en él: {code: "01" a "04", note?}. | | `software` | `own` \| `dian_free` | | 030–033: `own` (el Software Propio de la empresa, por defecto) o `dian_free` (el software gratuito de la DIAN). El 034 se firma con el Software Propio. | | `provider` | `dian` \| `company` | | 030–033: quién es el proveedor tecnológico: `dian` (por defecto con `dian_free`) o `company` (por defecto con `own`). | ### ReceivedInvoice | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `cufe` | texto | | El CUFE de la factura (96 caracteres hexadecimales). | | `number` | texto | | El número de la factura, con su prefijo. En el 034 es opcional: si viene, tiene que ser el de la factura de ese CUFE. | | `type` | texto | | Tipo de documento de la factura ("01" a "04"; por defecto "01"). No va en el 034. | | `issuer` | [ReceivedInvoiceIssuer](/docs/api/objetos#ReceivedInvoiceIssuer) | | | | `acquirer_document_type` | texto | | El tipo de documento con que el emisor identificó a la empresa en la factura ("31" NIT o "13" cédula). Por defecto, el de la empresa (si no, la regla AAH09). | ### ReceivedInvoiceIssuer | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `nit` | texto | | | | `check_digit` | entero | | Opcional: si viene, tiene que ser el DV del NIT. | | `document_type` | texto | | Tipo de documento (tabla 13.2.1 de la DIAN): 13 CC, 31 NIT, 22 CE, 41 PA… | | `name` | texto | | Se guarda (va en el XML) pero no sale en los webhooks. | ### References | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `purchase_order` | [DocumentReference](/docs/api/objetos#DocumentReference) | | | | `despatches` | lista de [DocumentReference](/docs/api/objetos#DocumentReference) | | Máximo 20. | | `receipts` | lista de [DocumentReference](/docs/api/objetos#DocumentReference) | | Máximo 20. | ### RequestLog Una petición de la API (sin cuerpo, consulta, IP ni llave). Se guardan 30 días y son las de la empresa y el ambiente de la llave. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `request` | sí | | | `id` | texto | sí | El `Request-Id` de la respuesta: compártelo si pides ayuda. | | `method` | `GET` \| `POST` \| `PUT` \| `PATCH` \| `DELETE` | sí | | | `path` | texto | sí | La plantilla de la ruta, no la ruta real: /v1/invoices/{id}/pdf | | `status` | entero | sí | El estado HTTP que se respondió. | | `code` | texto (puede ser null) | | El `code` del error (validation_error…); null si salió bien. | | `duration_ms` | entero | sí | Lo que tardó Trevol en responder, en milisegundos. | | `key_id` | texto (puede ser null) | | La llave que hizo la petición (key_…, la de GET /v1/company); null si ya se borró. | | `document_id` | texto (puede ser null) | | El documento (inv_…, cn_…, dn_…) sobre el que se actuó, si lo hay. | | `idempotent_replayed` | booleano | | Se respondió con el resultado guardado de una Idempotency-Key repetida. | | `api_version` | `2026-10-21` \| `2026-10-22` \| `null` (puede ser null) | | | | `created_at` | fecha y hora | sí | | ### Seller | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `resident` | booleano | | Residente fiscal en Colombia. Por defecto, `country` es CO. | | `person_type` | `legal` \| `natural` | | Por defecto se deduce del NIT. | | `document_type` | texto | sí | Residente: solo "31" (NIT). No residente: 21, 22, 31, 41, 42, 47 o 50 (tabla 16.2.1). | | `document_number` | texto | sí | Sin dígito de verificación; Trevol calcula el DV del NIT. | | `check_digit` | entero | | Opcional: si viene, tiene que ser el DV del NIT. | | `name` | texto | sí | | | `address` | texto | | | | `tax_scheme` | texto | | "ZZ" (no aplica, por defecto) o "01" (IVA). | | `tax_responsibilities` | lista de texto | | O-13, O-15, O-23, O-47, O-48, O-49 o R-99-PN (por defecto R-99-PN). Máximo 20. | | `country` | texto | | ISO 3166-1 alfa-2 (tabla 16.4.1). Por defecto CO. | | `municipality_code` | texto | | Solo vendedor residente: código DANE del municipio. | | `department_code` | texto | | | | `postal_code` | texto | | Residente: de la tabla 16.4.4, del mismo departamento. No residente: libre, hasta 10 caracteres. | | `city` | texto | | Obligatoria si no es residente. | | `region` | texto | | Solo vendedor no residente. | ### Shipment | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `date` | fecha (AAAA-MM-DD) | | | | `address` | [ForeignAddress](/docs/api/objetos#ForeignAddress) | | | | `carrier` | [Carrier](/docs/api/objetos#Carrier) | | | ### ShowEvent | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `name` | texto | sí | | | `locality` | texto | sí | | | `pulep` | texto | | | | `sale_type` | texto | | | | `sale_phase` | texto | | | | `category` | texto | | | | `municipality_code` | texto | | Código DANE del municipio (5 dígitos). | | `venue` | texto | | | | `date` | fecha (AAAA-MM-DD) | | | | `time` | texto | | | ### ShowExtension | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `producer` | [ShowProducer](/docs/api/objetos#ShowProducer) | sí | | | `event` | [ShowEvent](/docs/api/objetos#ShowEvent) | sí | | ### ShowProducer | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `name` | texto | sí | | | `nit` | texto | sí | | | `lep` | texto | | | ### StandardCode | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `scheme` | texto | | | | `value` | texto | | | ### StockExchangeExtension | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `trade_number` | texto | sí | | | `settlement_date` | fecha (AAAA-MM-DD) | sí | | | `nominal_quantity` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `prices` | [StockExchangePrices](/docs/api/objetos#StockExchangePrices) | sí | | | `yields` | decimal | | Número o texto decimal con punto («150000.50»). | | `value` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `commission_percent` | decimal | sí | Número o texto decimal con punto («150000.50»). | ### StockExchangePrices | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `registration` | texto | | | | `total_assignment` | texto | | | ### SupportAdjustmentItem | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | | Código (texto). | | `description` | texto | sí | | | `unit` | texto | | Unidad de medida (tabla 13.3.6). Por defecto 94 (unidad). | | `quantity` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `unit_price` | decimal | sí | Precio unitario SIN impuestos. | | `taxes` | lista de [SupportItemTax](/docs/api/objetos#SupportItemTax) | | Solo IVA: {code: "01", rate: 0 \| 5 \| 19} o {code: "01", excluded: true}. Máximo 12. | | `discounts` | lista de [ItemAllowance](/docs/api/objetos#ItemAllowance) | | Descuentos de la línea, sin código; la razón va de 10 a 5000 caracteres (NSBE04). Máximo 10. | | `charges` | lista de [SupportItemCharge](/docs/api/objetos#SupportItemCharge) | | Máximo 10. | | `standard_code` | [StandardCode](/docs/api/objetos#StandardCode) | | | ### SupportAdjustmentReference Uno de: objeto, objeto. ### SupportCurrency | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | sí | Moneda del documento (ISO 4217); los valores del pedido van en ESA moneda. | | `exchange_rate` | decimal | sí | Pesos por 1 unidad de la moneda. | | `rate_date` | fecha (AAAA-MM-DD) | sí | | ### SupportDocument Un documento soporte en adquisiciones a no obligados a facturar (Invoice 05). Se genera, no se expide: archivos `xml` y `dian_response`. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | | sd_… | | `object` | `support_document` | | | | `environment` | `test` \| `live` | | | | `status` | `pending` \| `sending` \| `accepted` \| `rejected` \| `error` \| `processing` | | `processing` solo en la versión 2026-10-22: la DIAN no confirmó y Trevol sigue resolviendo el documento (el resultado llega por GET y por webhook; no crees otro documento para la misma venta). En 2026-10-21 ese documento se ve como `error`. | | `number` | texto (puede ser null) | | | | `prefix` | texto (puede ser null) | | | | `external_id` | texto (puede ser null) | | | | `origin` | `api` \| `app` \| `contapyme` | | | | `qr` | texto (puede ser null) | | | | `issued_at` | fecha y hora (puede ser null) | | | | `items` | lista de [SupportItem](/docs/api/objetos#SupportItem) | | | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `currency` | [SupportCurrency](/docs/api/objetos#SupportCurrency) | | | | `totals` | objeto (puede ser null) | | Montos como texto decimal con 2 decimales, siempre en pesos. Con moneda extranjera, `currency` trae el equivalente. | | `dian` | objeto | | | | `delivery` | objeto | | El documento soporte se genera, no se expide: no hay PDF, ZIP ni correo. Solo hay XML y la respuesta de la DIAN. | | `created_at` | fecha y hora | | | | `updated_at` | fecha y hora (puede ser null) | | | | `cuds` | texto (puede ser null) | | El código único del documento (CUDS, 96 hex). | | `seller` | [Seller](/docs/api/objetos#Seller) | | | | `notes` | lista de texto | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | | | `transmission` | `per_operation` \| `weekly` | | | | `adjustment_note` | [SupportAdjustmentReference](/docs/api/objetos#SupportAdjustmentReference) (puede ser null) | | | ### SupportDocumentAdjustment Una nota de ajuste de un documento soporte (CreditNote 95). Se genera, no se expide: archivos `xml` y `dian_response`. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | | sda_… | | `object` | `support_document_adjustment` | | | | `environment` | `test` \| `live` | | | | `status` | `pending` \| `sending` \| `accepted` \| `rejected` \| `error` \| `processing` | | `processing` solo en la versión 2026-10-22: la DIAN no confirmó y Trevol sigue resolviendo el documento (el resultado llega por GET y por webhook; no crees otro documento para la misma venta). En 2026-10-21 ese documento se ve como `error`. | | `number` | texto (puede ser null) | | | | `prefix` | texto (puede ser null) | | | | `external_id` | texto (puede ser null) | | | | `origin` | `api` \| `app` \| `contapyme` | | | | `qr` | texto (puede ser null) | | | | `issued_at` | fecha y hora (puede ser null) | | | | `items` | lista de [SupportAdjustmentItem](/docs/api/objetos#SupportAdjustmentItem) | | | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `currency` | [SupportCurrency](/docs/api/objetos#SupportCurrency) | | | | `totals` | objeto (puede ser null) | | Montos como texto decimal con 2 decimales, siempre en pesos. Con moneda extranjera, `currency` trae el equivalente. | | `dian` | objeto | | | | `delivery` | objeto | | El documento soporte se genera, no se expide: no hay PDF, ZIP ni correo. Solo hay XML y la respuesta de la DIAN. | | `created_at` | fecha y hora | | | | `updated_at` | fecha y hora (puede ser null) | | | | `cuds` | texto (puede ser null) | | El código único del documento (CUDS, 96 hex). | | `seller` | [Seller](/docs/api/objetos#Seller) | | | | `notes` | lista de texto | | | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | | | `concept` | texto | | Concepto de la nota (tabla 16.2.4): 1 a 5. | | `description` | texto | | | | `support_document` | [SupportDocumentReference](/docs/api/objetos#SupportDocumentReference) | | | ### SupportDocumentAdjustmentInput | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `seller` | [Seller](/docs/api/objetos#Seller) | | Opcional con `support_document.id`: se toma el vendedor del documento soporte (si viene, tiene que ser el mismo). | | `items` | lista de [SupportAdjustmentItem](/docs/api/objetos#SupportAdjustmentItem) | sí | Las líneas de la nota (1 a 500): sin retenciones ni fecha de compra. Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `currency` | [SupportCurrency](/docs/api/objetos#SupportCurrency) | | | | `concept` | texto | sí | Concepto de la nota de ajuste (tabla 16.2.4): "1" devolución parcial, "2" anulación del documento soporte (lleva exactamente sus valores), "3" rebaja o descuento, "4" ajuste de precio, "5" otros. | | `description` | texto | sí | Qué se corrige y por qué (de 20 a 5000 caracteres, NSBF04). | | `support_document` | [SupportDocumentReference](/docs/api/objetos#SupportDocumentReference) | sí | El documento soporte que ajusta: {"id": "sd_…"} (de Trevol: validado, de la empresa y el ambiente de la llave) o {number, cuds, issue_date, total?} si es de otra plataforma (`total` hace falta para anularlo). | ### SupportDocumentEventData Resumen de un documento soporte o de su nota de ajuste (nunca datos del proveedor ni líneas). Pide el documento completo con GET /v1/{support_documents|support_document_adjustments}/{id}. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `status` | `pending` \| `sending` \| `accepted` \| `rejected` \| `error` \| `contingency` | sí | Nunca `processing`: ese estado solo existe en las respuestas de la API. | | `environment` | `test` \| `live` | sí | | | `number` | texto (puede ser null) | | | | `prefix` | texto (puede ser null) | | | | `external_id` | texto (puede ser null) | | Tu identificador, solo en los documentos creados por la API. | | `origin` | `api` \| `app` \| `contapyme` | | | | `issued_at` | fecha y hora (puede ser null) | | | | `total` | texto (puede ser null) | | Texto decimal con dos decimales. | | `dian` | objeto | sí | | | `error` | objeto | | Solo en `….error`. El texto es fijo por código: nunca el mensaje de la DIAN. | | `object` | `support_document` \| `support_document_adjustment` | sí | | | `id` | texto | sí | sd_… \| sda_… | | `cuds` | texto (puede ser null) | | Código único del documento soporte. | | `support_document` | objeto | | En la nota de ajuste: el documento soporte que ajusta. | | `delivery` | null | | El documento soporte no se expide al vendedor: no hay PDF ni correo. | ### SupportDocumentInput | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `external_id` | texto | | Tu identificador del documento: único entre los documentos API de la empresa y el ambiente. | | `seller` | [Seller](/docs/api/objetos#Seller) | sí | Quien vende y no está obligado a facturar (el SNO). La empresa de la llave es la compradora: sus datos salen de la empresa. No hay `customer`. | | `items` | lista de [SupportItem](/docs/api/objetos#SupportItem) | sí | Las líneas (1 a 500). Retenciones solo por línea (`withholdings`: 05 ReteIVA, 06 ReteRenta); las del documento no se publican. Máximo 500. | | `payment` | [Payment](/docs/api/objetos#Payment) | | | | `notes` | lista de texto | | Máximo 10. | | `references` | [References](/docs/api/objetos#References) | | | | `allowance_charges` | lista de [AllowanceCharge](/docs/api/objetos#AllowanceCharge) | | Descuentos y recargos globales (no tocan las bases). Máximo 20. | | `currency` | [SupportCurrency](/docs/api/objetos#SupportCurrency) | | | | `transmission` | `per_operation` \| `weekly` | | `per_operation` (por defecto, un documento por compra) o `weekly` (acumulado semanal con un mismo vendedor: cada línea lleva su `purchase_date`). | | `adjustment_note` | [SupportAdjustmentReference](/docs/api/objetos#SupportAdjustmentReference) | | La nota de ajuste que originó este documento (opcional): {"id": "sda_…"} o {number, cuds, issue_date}. | | `numbering` | [Numbering](/docs/api/objetos#Numbering) | | Opcional: el prefijo de la resolución de documento soporte que numera (una empresa con varios rangos). Sin él, la más nueva vigente. Si no existe, `numbering_unavailable` (`no_resolution`). | ### SupportDocumentReference Uno de: objeto, objeto. ### SupportItem | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | | Código (texto). | | `description` | texto | sí | | | `unit` | texto | | Unidad de medida (tabla 13.3.6). Por defecto 94 (unidad). | | `quantity` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `unit_price` | decimal | sí | Precio unitario SIN impuestos. | | `taxes` | lista de [SupportItemTax](/docs/api/objetos#SupportItemTax) | | Solo IVA: {code: "01", rate: 0 \| 5 \| 19} o {code: "01", excluded: true}. Máximo 12. | | `discounts` | lista de [ItemAllowance](/docs/api/objetos#ItemAllowance) | | Descuentos de la línea, sin código; la razón va de 10 a 5000 caracteres (DSBE04). Máximo 10. | | `charges` | lista de [SupportItemCharge](/docs/api/objetos#SupportItemCharge) | | Máximo 10. | | `withholdings` | lista de [SupportItemWithholding](/docs/api/objetos#SupportItemWithholding) | | Las practica la empresa al vendedor: 05 ReteIVA (15 o 100 %) o 06 ReteRenta. No restan del total a pagar. Máximo 10. | | `standard_code` | [StandardCode](/docs/api/objetos#StandardCode) | | | | `purchase_date` | fecha (AAAA-MM-DD) | | Fecha de la compra. Obligatoria en el acumulado semanal (entre 6 días antes de hoy y hoy); por operación no se informa. | ### SupportItemCharge | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `reason` | texto | | | | `percent` | decimal | | Número o texto decimal con punto («150000.50»). | | `amount` | decimal | | Número o texto decimal con punto («150000.50»). | ### SupportItemTax | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | sí | Código (texto). | | `rate` | decimal | | Tarifa en % (ICA y ReteICA: por mil). | | `excluded` | booleano | | | ### SupportItemWithholding | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | sí | Código (texto). | | `rate` | decimal | sí | Tarifa en % (ICA y ReteICA: por mil). | ### TacitAcceptance ¿Procede ya la aceptación tácita (034) sobre una factura propia? Solo lectura: consulta GetStatusEvent con el certificado de la empresa y no envía nada. Límite propio: 30 por minuto por llave (las dos consultas, juntas). | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `tacit_acceptance` | | | | `invoice_id` | texto | | Solo si la factura es de Trevol. | | `cufe` | texto | | El CUFE de la factura. | | `number` | texto | | El número de la factura. | | `allowed` | booleano | | | | `available_from` | fecha (AAAA-MM-DD) (puede ser null) | | Desde cuándo procede (3 días hábiles después del recibo del bien, 032). | | `rules` | lista de objeto | | Las reglas (LGC…) que impiden el 034 ahora; llevan `local: true`. | ### TestEventData `webhook.test`: lo manda «Probar» (app) o POST …/test, solo a ese endpoint. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `test` | | | | `message` | texto | | | ### ThirdPartyCharge | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `service` | texto | sí | | | `company` | [ThirdPartyCompany](/docs/api/objetos#ThirdPartyCompany) | sí | | | `reason` | texto | | | | `contract` | texto | | | | `period` | texto | | | | `note` | texto | | | | `total` | decimal | sí | Número o texto decimal con punto («150000.50»). | | `charges` | lista de [ConsumptionAdjustment](/docs/api/objetos#ConsumptionAdjustment) | | Máximo 50. | ### ThirdPartyCompany | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `nit` | texto | sí | | | `name` | texto | sí | | ### TopUp | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `object` | `top_up` | sí | | | `reference` | texto | sí | | | `amount_cop` | entero | sí | | | `status` | `prepared` | sí | | | `wompi` | objeto | sí | Lo que necesita el widget de Wompi (WidgetCheckout). | ### TopUpBuyer El comprador de la factura de la recarga. Sin él, la empresa de la llave. | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `document_type` | `13` \| `31` \| `22` \| `41` | sí | 13 cédula, 31 NIT, 22 cédula de extranjería, 41 pasaporte (lo que acepta la facturación de la recarga). | | `document_number` | texto | sí | Sin DV ni puntos (con 13 y 31, solo dígitos). | | `check_digit` | texto o entero | | Con NIT, obligatorio: el DV del NIT. No va con los demás. | | `person_type` | `legal` \| `natural` | sí | legal (persona jurídica, solo con NIT) o natural. | | `name` | texto | sí | Razón social o, para una persona natural, nombres y apellidos (al menos dos palabras). | | `email` | texto | sí | A dónde llega la factura de la recarga. | | `phone` | texto | | | | `address` | texto | | | | `municipality_code` | texto | | Código DANE del municipio (5 dígitos). | ### TopUpInput | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `amount_cop` | entero | sí | En pesos, entre la recarga mínima y la máxima. | | `buyer` | [TopUpBuyer](/docs/api/objetos#TopUpBuyer) | | | ### TransportExtension | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `mode` | texto | sí | | | `vehicle_id` | texto | sí | | | `vehicle` | texto | sí | | | `origin` | texto | sí | | | `destination` | texto | sí | | ### WebhookDelivery Un evento hacia un endpoint, con sus reintentos (8 automáticos en unas 32 horas; 3 si es manual). | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | sí | | | `object` | `webhook_delivery` | sí | | | `endpoint_id` | texto | sí | | | `event_id` | texto | sí | | | `event_type` | texto (puede ser null) | | | | `status` | `pending` \| `sending` \| `succeeded` \| `failed` \| `canceled` | sí | | | `attempts` | entero | sí | | | `manual` | booleano | | | | `next_attempt_at` | fecha y hora (puede ser null) | | | | `last_attempt` | objeto (puede ser null) | | | | `created_at` | fecha y hora (puede ser null) | | | | `succeeded_at` | fecha y hora (puede ser null) | | | ### WebhookEndpoint | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | texto | sí | | | `object` | `webhook_endpoint` | sí | | | `environment` | `test` \| `live` | sí | | | `url` | texto | sí | La URL completa solo la ven las llaves de escritura; las de lectura ven el esquema y el host (el camino va enmascarado: a veces es un secreto). | | `description` | texto (puede ser null) | | | | `events` | lista de `*` \| `invoice.accepted` \| `invoice.rejected` \| `invoice.error` \| `invoice.delivered` \| `invoice.email_bounced` \| `invoice.email_failed` \| `credit_note.accepted` \| `credit_note.rejected` \| `credit_note.error` \| `credit_note.delivered` \| `credit_note.email_bounced` \| `credit_note.email_failed` \| `debit_note.accepted` \| `debit_note.rejected` \| `debit_note.error` \| `debit_note.delivered` \| `debit_note.email_bounced` \| `debit_note.email_failed` \| `balance.low` \| `numbering.low` \| `numbering.expiring` \| `certificate.expiring` \| `support_document.accepted` \| `support_document.rejected` \| `support_document.error` \| `support_document_adjustment.accepted` \| `support_document_adjustment.rejected` \| `support_document_adjustment.error` \| `equivalent_document.accepted` \| `equivalent_document.rejected` \| `equivalent_document.error` \| `equivalent_document.delivered` \| `equivalent_document.email_bounced` \| `equivalent_document.email_failed` \| `equivalent_document_adjustment.accepted` \| `equivalent_document_adjustment.rejected` \| `equivalent_document_adjustment.error` \| `equivalent_document_adjustment.delivered` \| `equivalent_document_adjustment.email_bounced` \| `equivalent_document_adjustment.email_failed` \| `payroll.accepted` \| `payroll.rejected` \| `payroll.error` \| `payroll_adjustment.accepted` \| `payroll_adjustment.rejected` \| `payroll_adjustment.error` \| `radian_event.accepted` \| `radian_event.rejected` \| `radian_event.error` | sí | Eventos del catálogo (49 tipos, hasta 60 en la lista), o ["*"] para todos (no se mezcla). `balance.low`, `numbering.low` y `numbering.expiring` solo existen en producción (llaves live). `webhook.test` no se suscribe. Máximo 60. | | `catalog_version` | `1` \| `2` | | Qué catálogo recibe con `["*"]`: `1` = el de la fase 2 (facturas, notas, saldo, numeración y certificado); `2` = también documento soporte, documento equivalente, nómina y eventos RADIAN. Los endpoints creados antes de la fase 3 quedan en 1 hasta que guardes de nuevo su lista de eventos (PATCH `events`); los nuevos nacen en 2. Una lista explícita siempre recibe lo que pide. | | `status` | `enabled` \| `paused` \| `disabled` | sí | `paused`: lo pausaste tú. `disabled`: lo desactivó Trevol por fallos sostenidos (10 seguidos durante 5 días); se reactiva con PATCH status=enabled. | | `disabled_reason` | `sustained_failures` \| `null` (puede ser null) | | | | `secret_rotated_at` | fecha y hora (puede ser null) | | | | `created_at` | fecha y hora (puede ser null) | | | | `updated_at` | fecha y hora (puede ser null) | | | | `secret` | texto | | SOLO al crear o rotar, una única vez: guárdalo. Verifica cada entrega con él (Standard Webhooks). | ### WebhookEndpointInput | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `url` | texto | sí | https:// con un nombre de dominio (no una IP), sin usuario ni clave, puerto 443 o 1024–65535. En cada intento se resuelve el nombre y se rechaza toda dirección privada o local. | | `description` | texto (puede ser null) | | | | `events` | lista de `*` \| `invoice.accepted` \| `invoice.rejected` \| `invoice.error` \| `invoice.delivered` \| `invoice.email_bounced` \| `invoice.email_failed` \| `credit_note.accepted` \| `credit_note.rejected` \| `credit_note.error` \| `credit_note.delivered` \| `credit_note.email_bounced` \| `credit_note.email_failed` \| `debit_note.accepted` \| `debit_note.rejected` \| `debit_note.error` \| `debit_note.delivered` \| `debit_note.email_bounced` \| `debit_note.email_failed` \| `balance.low` \| `numbering.low` \| `numbering.expiring` \| `certificate.expiring` \| `support_document.accepted` \| `support_document.rejected` \| `support_document.error` \| `support_document_adjustment.accepted` \| `support_document_adjustment.rejected` \| `support_document_adjustment.error` \| `equivalent_document.accepted` \| `equivalent_document.rejected` \| `equivalent_document.error` \| `equivalent_document.delivered` \| `equivalent_document.email_bounced` \| `equivalent_document.email_failed` \| `equivalent_document_adjustment.accepted` \| `equivalent_document_adjustment.rejected` \| `equivalent_document_adjustment.error` \| `equivalent_document_adjustment.delivered` \| `equivalent_document_adjustment.email_bounced` \| `equivalent_document_adjustment.email_failed` \| `payroll.accepted` \| `payroll.rejected` \| `payroll.error` \| `payroll_adjustment.accepted` \| `payroll_adjustment.rejected` \| `payroll_adjustment.error` \| `radian_event.accepted` \| `radian_event.rejected` \| `radian_event.error` | sí | Eventos del catálogo (49 tipos, hasta 60 en la lista), o ["*"] para todos (no se mezcla). `balance.low`, `numbering.low` y `numbering.expiring` solo existen en producción (llaves live). `webhook.test` no se suscribe. Máximo 60. | ### WebhookEndpointUpdate | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `url` | texto | | https:// con un nombre de dominio (no una IP), sin usuario ni clave, puerto 443 o 1024–65535. En cada intento se resuelve el nombre y se rechaza toda dirección privada o local. | | `description` | texto (puede ser null) | | | | `events` | lista de `*` \| `invoice.accepted` \| `invoice.rejected` \| `invoice.error` \| `invoice.delivered` \| `invoice.email_bounced` \| `invoice.email_failed` \| `credit_note.accepted` \| `credit_note.rejected` \| `credit_note.error` \| `credit_note.delivered` \| `credit_note.email_bounced` \| `credit_note.email_failed` \| `debit_note.accepted` \| `debit_note.rejected` \| `debit_note.error` \| `debit_note.delivered` \| `debit_note.email_bounced` \| `debit_note.email_failed` \| `balance.low` \| `numbering.low` \| `numbering.expiring` \| `certificate.expiring` \| `support_document.accepted` \| `support_document.rejected` \| `support_document.error` \| `support_document_adjustment.accepted` \| `support_document_adjustment.rejected` \| `support_document_adjustment.error` \| `equivalent_document.accepted` \| `equivalent_document.rejected` \| `equivalent_document.error` \| `equivalent_document.delivered` \| `equivalent_document.email_bounced` \| `equivalent_document.email_failed` \| `equivalent_document_adjustment.accepted` \| `equivalent_document_adjustment.rejected` \| `equivalent_document_adjustment.error` \| `equivalent_document_adjustment.delivered` \| `equivalent_document_adjustment.email_bounced` \| `equivalent_document_adjustment.email_failed` \| `payroll.accepted` \| `payroll.rejected` \| `payroll.error` \| `payroll_adjustment.accepted` \| `payroll_adjustment.rejected` \| `payroll_adjustment.error` \| `radian_event.accepted` \| `radian_event.rejected` \| `radian_event.error` | | Eventos del catálogo (49 tipos, hasta 60 en la lista), o ["*"] para todos (no se mezcla). `balance.low`, `numbering.low` y `numbering.expiring` solo existen en producción (llaves live). `webhook.test` no se suscribe. Máximo 60. | | `status` | `enabled` \| `paused` | | enabled reactiva (también uno desactivado por Trevol) y pone en cero el contador de fallos. | ### Withholding | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `code` | texto | sí | Código (texto). | | `rate` | decimal | sí | Tarifa en % (ICA y ReteICA: por mil). | | `self_withholding` | booleano | | | | `base` | decimal | | Número o texto decimal con punto («150000.50»). | --- # Errores > Catálogo de errores de la API de Trevol: códigos estables, estado HTTP, cuándo pasan y si se reintentan. Formato application/problem+json (RFC 9457). Fuente: https://platform.trevol.si/docs/errores Cada error trae un `code` estable (programa contra él, no contra el texto), el estado HTTP, un `detail` en español, `retryable` y el `request_id` de la petición. El `type` apunta a esta página, con un ancla por código: `https://platform.trevol.si/docs/errores#validation_error`. ```json respuesta { "type": "https://platform.trevol.si/docs/errores#validation_error", "title": "Los datos no son válidos", "status": 422, "code": "validation_error", "detail": "La tarifa de IVA debe ser una de la tabla 13.3.11: 0.00, 5.00, 16.00, 19.00.", "errors": [ { "field": "items[0].taxes[0].rate", "pointer": "/items/0/taxes/0/rate", "message": "…" } ], "retryable": false, "request_id": "req_…" } ``` Cuando la DIAN rechaza un documento, `document.dian.rules` trae cada regla con su explicación y cómo corregirla: [Reglas de la DIAN](/docs/errores/reglas-dian). Qué se reintenta y cómo: [Reintentos](/docs/reintentos). ## Autenticación y permisos ### authentication_required **Falta la llave de la API** · HTTP 401 · No se reintenta: corrige primero Falta `Authorization: Bearer` con la llave. ### invalid_api_key **La llave de la API no es válida** · HTTP 401 · No se reintenta: corrige primero La llave no existe, fue revocada o no es de la API (debe empezar por `tvl_test_` o `tvl_live_`). ### permission_denied **Sin permiso para esto** · HTTP 403 · No se reintenta: corrige primero La llave es de solo lectura, o la petición viene de una IP que la llave no permite. ## El pedido ### not_found **No existe** · HTTP 404 · No se reintenta: corrige primero La ruta no existe, o el documento no es de la empresa y el ambiente de la llave. ### method_not_allowed **Método no permitido** · HTTP 405 · No se reintenta: corrige primero La ruta existe pero no con ese método (la cabecera `Allow` dice cuáles). ### unsupported_version **Versión de la API desconocida** · HTTP 400 · No se reintenta: corrige primero `Trevol-Version` trae una versión que no existe. ### invalid_json **El cuerpo no es JSON válido** · HTTP 400 · No se reintenta: corrige primero El cuerpo no es JSON o no es un objeto. ### invalid_request **El pedido no es válido** · HTTP 400 · No se reintenta: corrige primero El pedido no es válido (por ejemplo, una `Idempotency-Key` con caracteres no permitidos). ### validation_error **Los datos no son válidos** · HTTP 422 · No se reintenta: corrige primero Los datos no son válidos. `errors[]` trae cada campo con su puntero (RFC 6901). No se gastó ningún número. ## Idempotencia y duplicados ### idempotency_key_required **Falta Idempotency-Key** · HTTP 400 · No se reintenta: corrige primero Crear o corregir un documento sin `Idempotency-Key`. ### idempotency_key_reused **Esa Idempotency-Key ya se usó con otro pedido** · HTTP 422 · No se reintenta: corrige primero La misma `Idempotency-Key` con otro cuerpo u otra operación. Usa una clave nueva por pedido. ### request_in_progress **Ese pedido todavía se está procesando** · HTTP 409 · Se puede reintentar Otra petición está enviando ese documento ahora. Consulta su estado en unos segundos. ### external_id_in_use **Ese external_id ya existe** · HTTP 409 · No se reintenta: corrige primero Ya hay un documento con ese `external_id` en la empresa y el ambiente; `existing_id` dice cuál (con el prefijo de su recurso). El `external_id` es único entre facturas, notas, documentos soporte y equivalentes (y sus notas); aparte, entre nóminas y sus ajustes; y aparte, entre eventos RADIAN. ### duplicate_document **Ya existe un documento igual** · HTTP 409 · No se reintenta: corrige primero Ya hay un documento vivo igual: una nómina (102) del mismo trabajador y mes en producción, o un evento RADIAN del mismo código sobre la misma factura. `existing_id` dice cuál (`pr_…`, `rev_…`). Para reintentar un evento: `POST /v1/radian_events/{id}/send`; para cambiar una nómina: una nota de ajuste. ## Documentos y la DIAN ### invalid_state **El documento no está en un estado que lo permita** · HTTP 409 · No se reintenta: corrige primero El documento no está en un estado que lo permita (por ejemplo, corregir uno ya validado: para eso es la nota; un `PUT` sobre un documento que Trevol o la DIAN están verificando —también el set de pruebas de la nómina y del documento equivalente—: espera el resultado; o reenviar un evento RADIAN rechazado: un rechazo es final). ### previous_event_pending **Falta el evento anterior en la DIAN** · HTTP 409 · Se puede reintentar Solo en la versión 2026-10-21: el evento (031, 032, 033) necesita que la DIAN muestre el anterior (`previous`) y todavía no aparece. No se envió nada; el evento queda en error con su número (viene en `document`): reintenta con `POST /v1/radian_events/{id}/send`. En 2026-10-22 es un 202 `processing` y Trevol lo manda cuando aparezca. ### tacit_acceptance_not_due **La aceptación tácita todavía no procede** · HTTP 422 · No se reintenta: corrige primero La aceptación tácita (034) todavía no procede. `available_from` dice desde cuándo (3 días hábiles después del recibo del bien) y `rules` las reglas LGC que lo impiden (las locales llevan `local: true`). El evento queda en error con su número: reintenta con `POST …/send` cuando proceda. `GET /v1/invoices/{id}/tacit_acceptance` (o, por CUFE, `GET /v1/radian_events/tacit_acceptance?cufe=…`) lo dice antes. ### dian_rejected **La DIAN rechazó el documento** · HTTP 422 · No se reintenta: corrige primero La DIAN rechazó el documento. `document` trae el documento con `dian.rules` traducidas (ver las [reglas de la DIAN](/docs/errores/reglas-dian)); se corrige con `PUT` y el mismo número. ### file_unavailable **El archivo no está disponible** · HTTP 409 · No se reintenta: corrige primero El documento todavía no tiene archivos (no está validado), o no se pudieron preparar ahora (503, reintentable). ### dian_unavailable **La DIAN no respondió** · HTTP 503 · Se puede reintentar Solo en la versión 2026-10-21: la DIAN no contestó. El documento queda en error y Trevol lo verifica en segundo plano (solo consulta; no reenvía). Reintenta con la misma `Idempotency-Key`: sigue con el mismo documento. En 2026-10-22 la respuesta es un 202 `processing`. ## Empresa y numeración ### company_not_enabled **La empresa no está habilitada para facturar** · HTTP 422 · No se reintenta: corrige primero La empresa no ha terminado la habilitación ante la DIAN, o le falta algo para esa familia de documentos. `reason` dice qué: `missing_software` (el software del modo), `missing_pin`, `payroll_not_enabled` (la nómina no está habilitada ni en pruebas) o `missing_legal_representative` (eventos 030 y 032). Se revisa antes de numerar. ### numbering_unavailable **La numeración no está disponible** · HTTP 422 · No se reintenta: corrige primero No hay numeración disponible: sin resolución activa (o sin rango de documento soporte o POS), agotada o vencida, o ContaPyme lleva la numeración de producción. `reason`: `no_resolution`, `resolution_exhausted`, `resolution_expired`, `resolution_not_yet_valid`, `prefix_in_use` o `missing_technical_key` (POS). ### certificate_problem **Hay un problema con el certificado de la empresa** · HTTP 422 · No se reintenta: corrige primero El certificado de la empresa falta o no sirve para firmar. ## Saldo y recargas ### insufficient_balance **No hay saldo** · HTTP 402 · No se reintenta: corrige primero Producción sin saldo. El documento queda `pending` con su número y se envía al reintentar con saldo. ### top_up_below_minimum **La recarga es menor que la mínima** · HTTP 422 · No se reintenta: corrige primero Preparar una recarga (`POST /v1/balance/top_ups`) por menos de la recarga mínima ($ 5.000). `minimum_cop` dice cuánto es. ### top_up_above_maximum **La recarga es mayor que la máxima** · HTTP 422 · No se reintenta: corrige primero Preparar una recarga (`POST /v1/balance/top_ups`) por más de la recarga máxima. `maximum_cop` dice cuánto es. ### top_ups_unavailable **Las recargas no están disponibles** · HTTP 503 · Se puede reintentar Las recargas no están disponibles en este momento. `Retry-After` dice cuándo volver a intentar. ## Límites y disponibilidad ### webhook_limit_reached **Ya hay el máximo de endpoints de webhooks** · HTTP 409 · No se reintenta: corrige primero Ya hay 5 webhooks en la empresa y el ambiente. Borra uno para crear otro. ### rate_limited **Demasiadas peticiones** · HTTP 429 · Se puede reintentar Pasaste un límite de la llave. `Retry-After` dice cuántos segundos esperar. ### internal_error **Error interno de Trevol** · HTTP 500 · Se puede reintentar Error de Trevol. Escríbenos con el `request_id`. --- # Reglas de la DIAN > Las reglas de validación de la DIAN que Trevol traduce al español, con el área del pedido donde se corrigen y cómo corregirlas. Lista completa en https://platform.trevol.si/docs/errores/reglas-dian.md --- # Registro de cambios > Qué cambió en cada versión de la API de Trevol (2026-10-21, 2026-10-22), a quién afecta y cómo migrar, y los cambios que no estrenan versión. Fuente: https://platform.trevol.si/docs/cambios La versión de la API se elige con la cabecera `Trevol-Version` ([Versiones](/docs/versiones)). Una versión nueva solo sale cuando algo **cambia el comportamiento** de lo que ya funciona. Los cambios aditivos (campos, rutas, tipos de evento nuevos) no la estrenan: están más abajo, en «Sin versión nueva». Para cada versión: qué cambió, a quién afecta y cómo migrar. ## 2026-10-22 **Qué cambió.** Con la DIAN sin contestar, la creación de un documento responde **`202 Accepted`** con `status: "processing"` (o `sending`, si otra petición o el fondo lo está enviando) en lugar del `503 dian_unavailable`. El `202` trae `Location` y `Retry-After: 30`. Trevol termina el documento por su cuenta (pregunta a la DIAN por el CUFE y, si hace falta, lo reenvía) y avisa por webhook con `….accepted`, `….rejected` o `….error`. Repetir la petición con la misma `Idempotency-Key` sobre un documento que Trevol está resolviendo no envía nada: responde otro `202` con el estado actual (o el `201` si ya se validó). En los eventos RADIAN, la falta del evento anterior en la DIAN (`previous_event_pending`) pasa a ser un `202 processing`. **A quién afecta.** A las llaves creadas desde esta versión (nacen en `2026-10-22`) y a quien mande `Trevol-Version: 2026-10-22`. Las llaves anteriores siguen en `2026-10-21` mientras no mandes el encabezado. **Cómo migrar.** Trata el `202` como un **resultado**, no como un error: guarda el `id` y el número, **no crees otro documento para la misma venta** y espera el webhook (o consulta con `GET`). Los SDK ya lo hacen: devuelven el modelo con su `status` y no reintentan un `202`. Un SDK fija `Trevol-Version` en `2026-10-22`; si todavía no quieres cambiar, crea el cliente con `apiVersion: '2026-10-21'` (o la opción equivalente de tu lenguaje). ## 2026-10-21 **Qué cambió.** La primera versión pública de la API v1: facturas, notas crédito y débito, llaves por empresa y ambiente, idempotencia, errores `application/problem+json`. Con la DIAN sin contestar responde **`503 dian_unavailable`** (el documento queda en `error` y Trevol lo verifica en segundo plano, solo consulta). **A quién afecta.** A las llaves creadas antes del 22 de octubre de 2026 y a quien mande `Trevol-Version: 2026-10-21`. **Cómo migrar.** No hace falta migrar para seguir como hasta ahora. Cuando quieras el `202 processing`, manda `Trevol-Version: 2026-10-22` ([más arriba](#2026-10-22)). ## Sin versión nueva Estos cambios son aditivos o de texto: valen igual para las dos versiones y no piden migrar nada. ### 2026-11-01: el ejemplo principal de factura es una venta nacional Solo cambian los ejemplos de la especificación y de la documentación: el primer ejemplo de `POST /v1/invoices`, `POST /v1/invoices/preview` y `PUT /v1/invoices/{id}` pasa a ser una venta nacional en pesos (cliente con NIT, dos ítems con IVA del 19 %, pago a crédito a 30 días), y la exportación en dólares queda como segundo ejemplo. Los fragmentos de código, los SDK de referencia y las colecciones de Postman, Insomnia y Bruno se regeneraron con ese ejemplo. La API no cambia. ### 2026-11-01: la documentación se muda a platform.trevol.si - **El `type` de los errores cambia de dirección.** Antes `https://trevol.si/api/errores#`; ahora `https://platform.trevol.si/docs/errores#`, con un ancla por cada `code`. Es lo único que cambia en el cuerpo de un error. **A quién afecta:** a quien compare el `type` como texto (programa contra `code`, que no cambia). Las direcciones viejas del sitio dejan de existir, sin redirecciones. - Hasta siete días después del despliegue, una repetición con la misma `Idempotency-Key` de un error 4xx guardado antes puede traer todavía la dirección anterior en el `type`. - **El `User-Agent` de los webhooks** pasa a `Trevol-Webhooks/1.0 (+https://platform.trevol.si/docs/webhooks)`. - **Especificación OpenAPI:** cada operación tiene su `operationId` (por ejemplo `createInvoice`), los listados de documentos declaran el sobre de lista (`{object, data, has_more, next_cursor}`), las respuestas declaran las cabeceras `Request-Id`, `Trevol-Version` e `Idempotent-Replayed`, y todos los cuerpos de petición tienen ejemplo. `Problem.request_id` deja de ser obligatorio en el esquema (el borde de la API no siempre lo tiene). - **Corrección de la especificación:** `check_digit` de `GET /v1/company` se declaraba texto, pero la API siempre lo ha devuelto como **número** (por ejemplo `6`). La especificación ahora dice `integer` (o `null`), como en `customer`, `seller` y el emisor de una factura recibida. La respuesta no cambia. - **Nuevo:** [SDK](/docs/sdks) en siete lenguajes, colecciones de Postman, Insomnia y Bruno ([Descargas](/docs/descargas)), esta documentación con ejemplos por lenguaje, un servidor MCP y `llms.txt` ([Para IA](/docs/ia)). ### 2026-10-31: el saldo por la API Tres rutas nuevas: `GET /v1/balance`, `GET /v1/balance/transactions` y `POST /v1/balance/top_ups`, y los códigos de error `top_up_below_minimum`, `top_up_above_maximum` y `top_ups_unavailable`. Ver [Saldo](/docs/saldo). ### 2026-10-23: aceptación tácita por CUFE `POST /v1/radian_events` admite el evento 034 sobre una factura emitida fuera de Trevol, identificada por su CUFE (`invoice: {cufe}`), y `GET /v1/radian_events/tacit_acceptance?cufe=…` la consulta antes. Ver [Eventos RADIAN](/docs/eventos-radian#tacita-cufe). ### 2026-10-22 y 2026-10-23: documento soporte, documento equivalente, nómina y RADIAN Se suman a la API el **documento soporte** (05) y su nota de ajuste (95), los **documentos equivalentes** (POS 20 y otros nueve tipos) con su nota de ajuste, la **nómina electrónica** (102) y su nota de ajuste (103) y los **eventos RADIAN** 030 a 034, con sus tipos de evento de webhook nuevos y los códigos de error `duplicate_document`, `previous_event_pending` y `tacit_acceptance_not_due`. Los webhooks creados antes con `["*"]` siguen recibiendo solo los 22 tipos de entonces hasta que guardes de nuevo su lista de eventos. Ver [Webhooks](/docs/webhooks#eventos). ### 2026-10-22: webhooks y eventos `POST /v1/webhook_endpoints` y sus rutas (rotar secreto, probar, entregas), `GET /v1/events` y su reenvío, y el registro de peticiones (`GET /v1/requests`). Avisos firmados con Standard Webhooks. Ver [Webhooks](/docs/webhooks). ### 2026-10-21: la primera API Facturas, notas crédito y débito, `GET /v1/company` y `GET /v1/dian/acquirer`. Ver [Facturas](/docs/facturas).