Inicio › Blog › API e integraciones
Tutorial · API · API e integraciones · 5 min de lecturaTu primera factura electrónica por API en 15 minutos (sandbox gratis)
Si tu empresa tiene un ERP, un CRM o un sistema propio, no necesitas que nadie digite las facturas: un POST con el cliente y las líneas, y la factura sale firmada con tu certificado y validada por la DIAN. Este tutorial va de cero a la primera factura aceptada en sandbox, con los mismos ejemplos de la documentación.
Antes de empezar
- La API vive en tu propia instancia:
https://<tu-nit>.miplata.co/api/external/…. No hay un host compartido ni un proveedor intermedio; tu empresa factura con su Software ID y su certificado. - Sandbox es un interruptor de toda la instancia (Configuración → Entorno), la DIAN de habilitación y las pasarelas de prueba. Misma llave, mismas rutas, sin consumir documentos. Es donde haremos todo este tutorial.
- La documentación tiene cada endpoint con ejemplos en cURL, Node.js y Python, además de versiones en texto plano (
/api-docs.md) y JSON para tu asistente de código, y una colección Postman.

Paso a paso
Crea una llave API
En tu cuenta, Configuración → Integraciones → Claves API → Crear clave API: nombre (por ejemplo «ERP»), IP o rango permitido (obligatorio) y límite por minuto. La llave se muestra una sola vez; guárdala en tu gestor de secretos. Puedes crear tokens personales (PAT) por integración o colaborador desde la misma llave sin repartir la raíz.
Pon la instancia en Sandbox
Configuración → Entorno → Sandbox. Los documentos irán a la DIAN de habilitación, sin validez fiscal ni consumo de cupo.
GET /companyte lo confirma enmode: "TEST"y cada documento llevaenvironment. En Sandbox hay además un simulador de caída de la DIAN para probar tus reintentos y webhooks.curl -sS https://TU-EMPRESA.miplata.co/api/external/company \ -H "X-API-Key: TU_LLAVE"Emite la factura
Un
POST /invoicescon el cliente y las líneas. En la primera emisión a un NIT envías sus datos; en las siguientes bastacustomer_niteitems, el resto se toma de su ficha. El IVA va por línea entax_percent.curl -sS -X POST https://TU-EMPRESA.miplata.co/api/external/invoices \ -H "X-API-Key: TU_LLAVE" -H "Content-Type: application/json" \ -H "Idempotency-Key: ORD-1001" \ -d '{ "customer_nit": "900123456", "customer_name": "Acme SAS", "customer_type_doc": "31", "customer_person_type": "1", "customer_tax_regime": "48", "customer_email": "pagos@acme.co", "customer_city_code": "11001", "customer_department_code": "11", "payment_means_code": "10", "order_reference": "ORD-1001", "items": [ { "description": "Consultoría", "quantity": 10, "unit_price": 150000, "tax_percent": 19 } ] }'La respuesta (
201) trae el documento ya numerado, firmado y enviado a la DIAN. El consecutivo lo pone miPlata: no lo mandas tú. Elides un UUID; guárdalo para consultar, cobrar o corregir:{ "id": "3f2b8c1e-7a4d-4b9e-9c61-2d5f0a8e4b17", "environment": "TEST", "invoice_number": "SETP990000123", "invoice_type": "01", "cufe": "a1b2c3d4e5f6...9f", "qr_code": "https://catalogo-vpfe-hab.dian.gov.co/document/searchqr?documentkey=a1b2...", "dian_status": "SENT", "total_amount": "1785000.00", "balance": "1785000.00" }Espera la aceptación
dian_statusllega comoSENT(enviada, esperando la respuesta) o ya comoACCEPTED; si la DIAN no responde,QUEUEDy miPlata reintenta sola. La DIAN responde en segundos. Dos formas de saberlo:- Webhook: registra una URL con
POST /webhooks(eventosinvoice.accepted,invoice.rejected,invoice.pending_dian; las notas crédito disparancredit_note.*); cada entrega va firmada con HMAC-SHA256 enX-MiPlata-Signature(yX-MiPlata-Signature-V2sobreX-MiPlata-Timestamp+ cuerpo). - Consulta:
GET /invoices/{id}hasta quedian_statusseaACCEPTED(oREJECTED, con el código de regla y su explicación).
curl -sS https://TU-EMPRESA.miplata.co/api/external/invoices/3f2b8c1e-7a4d-4b9e-9c61-2d5f0a8e4b17 \ -H "X-API-Key: TU_LLAVE"- Webhook: registra una URL con
Descarga el PDF y el XML
GET /invoices/{id}/pdfdevuelve la representación gráfica con QR;GET /invoices/{id}/attached-documentel AttachedDocument firmado (XML + ApplicationResponse de la DIAN) que el cliente puede exigir.Corrige con una nota crédito
Una factura aceptada no se edita: se emite una nota crédito (
invoice_type: "91") que referencia el original. Sus webhooks son de la familiacredit_note.*(credit_note.accepted,credit_note.rejected…), noinvoice.*.curl -sS -X POST https://TU-EMPRESA.miplata.co/api/external/invoices \ -H "X-API-Key: TU_LLAVE" -H "Content-Type: application/json" \ -d '{ "invoice_type": "91", "correction_invoice_id": "3f2b8c1e-7a4d-4b9e-9c61-2d5f0a8e4b17", "correction_concept": "2", "correction_reason": "Anulación de la factura", "customer_nit": "900123456", "items": [ { "description": "Consultoría", "quantity": 10, "unit_price": 150000, "tax_percent": 19 } ] }'Pasa a producción
Configuración → Entorno → Producción. Nada cambia en tu código: misma llave, mismas rutas. Desde ahí cada documento transmitido a la DIAN consume una unidad del cupo del plan (1.000 en el Plan 12 Meses, 250 en el de 3) y las recargas cubren el resto.
Tres cosas que evitan dolores de cabeza
- Idempotencia. Manda
Idempotency-Key(tu número de pedido) en cadaPOST /invoices: si tu sistema reintenta con el mismo cuerpo dentro de 24 horas, recibes200conIdempotent-Replayed: truey el mismo documento, en vez de una segunda factura con otro consecutivo. Unorder_referencerepetido en facturas de venta (sin esa llave o pasadas las 24 horas) responde409con el id existente. - DIAN caída. Cuando la DIAN no responde, el documento queda
QUEUEDya firmado y numerado; miPlata reintenta (3 × 20 s, luego cada 30 minutos, 48 horas) y avisa porinvoice.pending_diany luegoinvoice.accepted. No reintentes elPOST. - Rechazos.
invoice.rejectedtrae la regla (por ejemploFAU14: el total a pagar no cuadra;FAJ43b: el nombre del emisor no coincide con el RUT) con causa y corrección enerrors_explained. Corriges conPUT /invoices/{id}(solo los campos que cambian, más"finalize": true, "send_to_dian": true): mismo número, nuevo CUFE, reenvío en la misma llamada.POST /invoices/{id}/retry-nowno sirve para esto: solo transmite documentos en cola por caída de la DIAN. Guía: factura rechazada por la DIAN.

Preguntas frecuentes
¿Hay un entorno de pruebas separado con otra URL?
No. Sandbox es un estado de toda la instancia, no un host ni una llave distinta. Eso evita el error clásico de apuntar producción al sandbox: cambias el interruptor y listo.
¿Qué pasa si envío un cliente que ya existe con datos distintos?
Los campos que envíes actualizan su ficha; los que omitas se conservan. Si solo mandas customer_nit, se usa la ficha tal como está.
¿Puedo emitir muchas facturas de golpe?
Sí: POST /invoices/bulk acepta un lote con client_reference por ítem y GET /invoices/bulk/{batch_id} devuelve el estado de cada una.
¿Cómo cobro la factura desde la API?
POST /invoices/{id}/pay-link genera el enlace de pago (Bre-B, Nequi, Daviplata o PayPal); el webhook invoice.paid avisa cuando el cliente paga. Guía: cobrar con Bre-B y Nequi.
¿Y la nómina y el documento soporte?
Mismo esquema: POST /payroll para documentos 102/103 e invoice_type: "05" / "95" para documento soporte y su nota de ajuste. Todo en la documentación.
¿Qué lenguaje debo usar?
Cualquiera con cliente HTTP y JSON. La documentación muestra cURL, Node.js y Python; la colección Postman cubre todos los endpoints públicos.
Lo mismo, en producción, con un interruptor
Cambia la instancia a Producción en Configuración → Entorno. Misma llave, mismas rutas; el cupo de documentos del plan empieza a contar ahí.