Guía de integración
Emití facturas electrónicas de Costa Rica (Hacienda v4.4) directamente desde tu POS, ERP o sistema a la medida. Nosotros construimos el XML, lo firmamos (XAdES-EPES), lo enviamos a Hacienda, generamos el PDF y se lo mandamos por correo a tu cliente. Vos solo mandás los datos.
x-api-keyURL base: https://facturaya-production-e613.up.railway.app/v1Documentación interactiva: https://facturaya-production-e613.up.railway.app/docs (Swagger, probá los endpoints en vivo)Antes de empezar necesitás:
fya_… y tu ID de empresa — ambos en el panel: Configuración → API para desarrolladores..p12 de Hacienda), una sola vez desde el panel.En el panel, entrá a Configuración → API para desarrolladores y tocá Generar llave. La llave se muestra una sola vez: guardala en una variable de entorno (nunca en el código fuente ni en el repositorio).
# Ejemplo (Linux/macOS)
export FYA_KEY="fya_tu_llave_secreta"
export FYA_EMPRESA="tu-id-de-empresa"
export FYA_BASE="https://facturaya-production-e613.up.railway.app/v1"Todas las peticiones deben incluir el header x-api-key: fya_…. Si la llave es inválida, está revocada o el plan venció, la API responde 401.
Enviá los datos de la venta. FacturaYA calcula el IVA, asigna la clave (50 díg.) y el consecutivo (20 díg.), y arma el XML v4.4. Todavía no se envía a Hacienda: primero se crea.
curl -X POST "$FYA_BASE/empresas/$FYA_EMPRESA/comprobantes" \
-H "x-api-key: $FYA_KEY" \
-H "Content-Type: application/json" \
-d '{
"tipo": "FE",
"condicionVenta": "01",
"moneda": "CRC",
"receptor": {
"tipoIdentificacion": "01",
"identificacion": "112345678",
"nombre": "Cliente Ejemplo S.A.",
"correo": "cliente@correo.cr"
},
"lineas": [
{
"cabys": "8399000000000",
"detalle": "Servicio de asesoría",
"cantidad": 1,
"precioUnitario": 75000,
"codigoTarifaIva": "08"
}
]
}'
# Respuesta:
# { "id": "a1b2…", "clave": "506…", "consecutivo": "00100001010000000123" }Guardá el id que devuelve: lo usás en los siguientes pasos.
Este paso firma el XML con el certificado de la empresa y lo transmite a Hacienda.
curl -X POST "$FYA_BASE/empresas/$FYA_EMPRESA/comprobantes/{id}/emitir" \
-H "x-api-key: $FYA_KEY"Hacienda responde de forma asíncrona. Tenés dos maneras de enterarte del resultado:
Opción A — Consultar el estado (pull):
curl -X POST "$FYA_BASE/empresas/$FYA_EMPRESA/comprobantes/{id}/consultar" \
-H "x-api-key: $FYA_KEY"
# → { "estado": "aceptado" } (o "rechazado", "procesando", "contingencia")Opción B — Webhooks (push, recomendado):
En vez de preguntar en un loop, registrás una URL de tu sistema y FacturaYA te avisa apenas el comprobante queda aceptado, rechazado o en contingencia. Ver el Paso 6.
El PDF sale con el diseño que la empresa configuró en el panel (logo, colores, QR).
# PDF
curl "$FYA_BASE/empresas/$FYA_EMPRESA/comprobantes/{id}/pdf" \
-H "x-api-key: $FYA_KEY" -o factura.pdf
# XML firmado + respuesta de Hacienda
curl "$FYA_BASE/empresas/$FYA_EMPRESA/comprobantes/{id}/xml" \
-H "x-api-key: $FYA_KEY" -o factura.xmlEn el panel (o por API) registrás la URL de tu sistema. Cuando un comprobante cambia de estado, te hacemos un POST JSON firmado, con reintentos automáticos (3 intentos: 0s, 2s, 6s).
Lo que te llega:
POST https://tusistema.com/webhooks/facturaya
Headers:
x-facturaya-event: comprobante.aceptado
x-facturaya-timestamp: 1751500000
x-facturaya-signature: v1=<hmac_sha256_hex>
Body:
{
"evento": "comprobante.aceptado",
"fecha": "2026-07-02T18:30:00.000Z",
"data": {
"id": "a1b2…", "tipo": "FE", "clave": "506…",
"consecutivo": "00100001010000000123",
"estado": "aceptado", "total": 84750, "moneda": "CRC",
"receptor": "Cliente Ejemplo S.A."
}
}Verificá la firma (así sabés que el POST viene de nosotros):
const crypto = require("crypto");
function firmaValida(req, secreto) {
const ts = req.headers["x-facturaya-timestamp"];
const firma = req.headers["x-facturaya-signature"]; // "v1=abc..."
const esperada = crypto
.createHmac("sha256", secreto) // secreto: se muestra 1 vez al crear el webhook
.update(ts + "." + req.rawBody) // rawBody = cuerpo EXACTO recibido
.digest("hex");
return firma === "v1=" + esperada;
}
// En tu handler:
app.post("/webhooks/facturaya", (req, res) => {
if (!firmaValida(req, process.env.FYA_WEBHOOK_SECRET)) return res.sendStatus(401);
res.sendStatus(200); // respondé 2xx rápido
procesarEnSegundoPlano(req.body); // y procesá después
});¿Todavía no tenés endpoint para probar? Usá nuestro eco público (POST https://facturaya-production-e613.up.railway.app/v1/webhooks/echo, siempre responde 200) o el botón Probar del panel.
Según los esquemas oficiales de Hacienda v4.4 (los mismos XSD con que validamos antes de transmitir). Se listan los campos que vos enviás por la API (JSON); entre paréntesis, el nombre del nodo XML.
Clave (50 díg.), NumeroConsecutivo (20 díg.), FechaEmision, todo el Emisor, los totales e impuestos y la firma XAdES-EPES.obl = siempre obligatorio · cond = condicional · opc = opcional · — no aplica · *partida obligatoria para mercancías físicas.
FEFactura Electrónica
Obligatorio: tipo:"FE" · receptor con nombre + tipoIdentificacion + identificacion + ubicación (provincia, canton, distrito, otrasSenas) · lineas[] (cada línea: cabys de 13 díg., detalle, cantidad, precioUnitario, codigoTarifaIva)
Condicional: condicionVenta (si se omite = 01 contado) · plazoCredito (si es a crédito) · mediosPago (si se omite = efectivo) · moneda + tipoCambio (si no es CRC) · receptor.correo (para enviarle el PDF) · lineas[].descuento
Particularidad: El receptor debe ir identificado (con cédula y ubicación).
TETiquete Electrónico
Obligatorio: tipo:"TE" · lineas[] (cada línea: cabys de 13 díg., detalle, cantidad, precioUnitario, codigoTarifaIva)
Condicional: receptor (opcional — venta al mostrador; si lo enviás, al menos nombre) · condicionVenta · mediosPago
Particularidad: No requiere receptor identificado; no sirve como respaldo de crédito fiscal del comprador.
NCNota de Crédito
Obligatorio: tipo:"NC" · referencias[] (tipoDoc, numero = clave del documento original, fechaEmision, codigo, razon) · lineas de lo que se acredita
Condicional: receptor (normalmente el mismo de la factura original) · condicionVenta
Particularidad: SIEMPRE referencia el documento que corrige/anula. codigo: 01 anula, 02 corrige monto, 03 corrige otros. Rebaja el monto.
NDNota de Débito
Obligatorio: tipo:"ND" · referencias[] (documento original) · lineas del monto que se aumenta
Condicional: receptor · condicionVenta
Particularidad: Igual estructura que la NC, pero AUMENTA el monto adeudado sobre un comprobante previo.
FECFactura Electrónica de Compra
Obligatorio: tipo:"FEC" · receptor = el PROVEEDOR (nombre + identificacion + ubicación) · codigoActividadReceptor (se deriva de tu empresa si falta) · referencias[] · lineas[] (cada línea: cabys de 13 díg., detalle, cantidad, precioUnitario, codigoTarifaIva)
Condicional: condicionVenta · plazoCredito (si es a crédito)
Particularidad: Invierte los roles: tu empresa (emisor) documenta una compra a un proveedor no inscrito. Receptor y código de actividad del receptor obligatorios.
FEEFactura Electrónica de Exportación
Obligatorio: tipo:"FEE" · lineas[] con partidaArancelaria (≥12 díg.) para mercancías físicas
Condicional: receptor (opcional; si es extranjero: tipoIdentificacion 05 + otrasSenasExtranjero) · condicionVenta
Particularidad: Exenta de IVA (usá una tarifa exenta en codigoTarifaIva). La partida arancelaria es obligatoria para bienes; para servicios no aplica.
REPRecibo Electrónico de Pago
Obligatorio: tipo:"REP" · receptor identificado · referencias[] (la factura que se está pagando) · lineas del pago · mediosPago (cómo se pagó)
Condicional: moneda + tipoCambio (si no es CRC)
Particularidad: Documenta el PAGO de un crédito previo (no una venta nueva). No lleva CAByS/cantidad tradicional: la línea es de pago. Referencia y medio de pago obligatorios.
Otros endpoints (misma autenticación)
GET …/comprobantesListado con estado y saldoGET …/comprobantes/referenciablesDocs con saldo para NC/ND/REPGET · POST …/clientesCatálogo de clientesGET · POST …/productosCatálogo de productos (CAByS)GET …/planConsumo del mes vs. límiteGET …/recepcionFacturas de proveedores recibidasErrores comunes
Códigos válidos según el estándar Anexos y Estructuras v4.4 de Hacienda (rige desde el 01/06/2025). Tomados de las enumeraciones de los XSD oficiales; usá el código (no el texto) en cada campo.
Tipo de identificación
receptor.tipoIdentificacion
Condición de venta
condicionVenta
Medio de pago
mediosPago[].medio
Tarifa de IVA
lineas[].codigoTarifaIva
Tipo de documento de referencia
referencias[].tipoDoc
Código de referencia (motivo)
referencias[].codigo
Nota: la unidad de medida (enum UnidadMedidaType v4.4) y el CAByS (13 díg.) tienen catálogos extensos; si omitís unidadMedida, la plataforma la deriva del CAByS. Para exoneraciones y otros impuestos, ver el Swagger.