Tu primera factura
Un recorrido completo con curl. Ten a mano tu API key y al menos un
externalProductId ya mapeado por el equipo de Contadito.
El paso 4 emite una factura legal
Consume un correlativo del rango CAI de tu empresa. Si todavía estás armando tu integración, quédate en Pruebas y vuelve aquí cuando lo que quieras sea facturar de verdad.
export CONTADITO_API_KEY="ctd_sk_..."
export BASE="https://integration.contadito.com"
Paso 1 — ¿está viva la API?
curl -s $BASE/v1/health
{ "status": "ok" }
Paso 2 — registra un cliente (opcional)
Solo lo necesitas si vas a emitir una venta exonerada o si quieres que la factura salga a nombre de alguien. Una venta de mostrador puede omitirlo.
curl -s -X POST $BASE/v1/clients \
-H "Authorization: Bearer $CONTADITO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"clientName": "Servicios Digitales S. de R.L.",
"email": "facturacion@cliente.hn",
"legalId": "08011999123456",
"phone": "+504 9999-9999",
"address": "Tegucigalpa, Honduras"
}'
{
"id": 1042,
"clientName": "Servicios Digitales S. de R.L.",
"email": "facturacion@cliente.hn",
"legalId": "08011999123456",
"phone": "+504 9999-9999",
"address": "Tegucigalpa, Honduras",
"createdAt": "2026-08-10T15:04:05.000Z"
}
Guarda ese id: es el clientId de la factura. Guárdalo en tu base de
datos, junto a tu propio id de cliente. Si alguna vez lo pierdes, puedes
listar tus clientes con GET /v1/clients:
curl -s "$BASE/v1/clients?page=1&limit=50" \
-H "Authorization: Bearer $CONTADITO_API_KEY"
{
"clients": [ { "id": 1042, "clientName": "Servicios Digitales S. de R.L.", "...": "..." } ],
"count": 1
}
Eso sí: ese endpoint no filtra por RTN ni por nombre, así que no lo uses en cada
venta. Ver GET /v1/clients.
Paso 3 — previsualiza (sin emitir nada)
Antes de gastar un correlativo real, comprueba que los precios, los impuestos y el formato impreso salgan como esperas:
curl -s -X POST $BASE/v1/invoices/preview \
-H "Authorization: Bearer $CONTADITO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"clientId": 1042,
"items": [
{ "externalProductId": "SKU-WEB-HOSTING-01", "quantity": 2 }
]
}'
{
"preview": true,
"total": 2300,
"totalInLetters": "DOS MIL TRESCIENTOS LEMPIRAS EXACTOS",
"taxBreakdown": [{ "rate": 15, "taxAmount": 300, "taxedAmount": 2000 }],
"pdfBase64": "JVBERi0xLjQK..."
}
Mismo body y mismos cálculos que la emisión real, pero no consume CAI, no
avanza el correlativo y no lleva Idempotency-Key. Repítelo cuantas veces
quieras mientras ajustas tu integración. El PDF viene marcado SIN VALIDEZ
FISCAL: no es entregable a un cliente. Ver
POST /v1/invoices/preview.
Paso 4 — emite la factura
curl -s -X POST $BASE/v1/invoices \
-H "Authorization: Bearer $CONTADITO_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-2026-0917" \
-d '{
"clientId": 1042,
"items": [
{ "externalProductId": "SKU-WEB-HOSTING-01", "quantity": 2 }
]
}'
{
"id": 8801,
"invoiceNumber": 1573,
"cai": "A1B2C3-D4E5F6-A1B2C3-D4E5F6-A1B2C3-12",
"invoiceDate": "2026-08-10T15:04:05.000Z",
"clientId": 1042,
"importe": 2000,
"discountTotal": 0,
"exemptedAmount": 0,
"exoneratedAmount": 0,
"totalTaxes": 300,
"total": 2300,
"totalInLetters": "DOS MIL TRESCIENTOS LEMPIRAS EXACTOS",
"isPaid": true,
"isAnnulled": false,
"taxBreakdown": [
{ "rate": 15, "taxAmount": 300, "taxedAmount": 2000 }
],
"lines": [
{
"externalProductId": "SKU-WEB-HOSTING-01",
"quantity": 2,
"unitPrice": 1000,
"importe": 2000,
"discountAmount": 0,
"subTotal": 2000,
"exemptAmount": null
}
],
"replayed": false,
"pdfBase64": "JVBERi0xLjQK..."
}
pdfBase64 es el comprobante A4 listo para imprimir o enviar por correo. Si
alguna vez viene null, la factura igual se emitió: descarga el PDF con
GET /v1/invoices/{id}/pdf.
Fíjate en lo que no mandaste: impuestos, correlativo, CAI. Todo eso lo pone
Contadito. Tú mandas qué se vendió y cuánto; el precio salió de tu lista de
precios, aunque puedes fijarlo por línea con
items[].price cuando la
venta se cerró a otro precio.
Antes de mandar esta llamada en producción
El Idempotency-Key tiene que estar persistido en tu base de datos antes de
hacer la solicitud, no generado al vuelo. Si no, un timeout te deja sin forma
segura de reintentar. Lee Idempotencia y reintentos.
Paso 5 — vuelve a leerla
curl -s $BASE/v1/invoices/8801 \
-H "Authorization: Bearer $CONTADITO_API_KEY"
Devuelve el mismo objeto (con replayed: false y pdfBase64: null). La consulta
está limitada a tu empresa: cualquier id fuera de ese alcance responde
404 EXTERNAL_INVOICE_NOT_FOUND.
Y el PDF, cuando lo necesites de nuevo:
curl -s $BASE/v1/invoices/8801/pdf \
-H "Authorization: Bearer $CONTADITO_API_KEY" \
-o factura-8801.pdf
Ejemplo en Node.js
const BASE = 'https://integration.contadito.com';
async function emitirFactura(orden) {
const res = await fetch(`${BASE}/v1/invoices`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CONTADITO_API_KEY}`,
'Content-Type': 'application/json',
// Persistido junto con la orden ANTES de llegar aquí.
'Idempotency-Key': orden.idempotencyKey,
},
body: JSON.stringify({
clientId: orden.contaditoClientId,
items: orden.lineas.map((l) => ({
externalProductId: l.sku,
quantity: l.cantidad,
})),
}),
});
const body = await res.json();
if (!res.ok) {
// Programa contra body.code, nunca contra body.message.
throw new ContaditoError(body.code, body.message, res.status);
}
return body; // body.replayed te dice si fue repetición
}
Ejemplo en PHP
<?php
$ch = curl_init('https://integration.contadito.com/v1/invoices');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('CONTADITO_API_KEY'),
'Content-Type: application/json',
'Idempotency-Key: ' . $orden['idempotency_key'],
],
CURLOPT_POSTFIELDS => json_encode([
'clientId' => $orden['contadito_client_id'],
'items' => [
['externalProductId' => 'SKU-WEB-HOSTING-01', 'quantity' => 2],
],
]),
]);
$respuesta = json_decode(curl_exec($ch), true);
$estado = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($estado >= 400) {
throw new RuntimeException($respuesta['code'] . ': ' . $respuesta['message']);
}
Y ahora
- Entiende qué significa cada total antes de imprimir el comprobante.
- Programa el manejo de errores.
- Revisa los límites de la v1.