Guía de integración

Conectá tu sistema con la API de FacturaYA

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.

Tecnología: REST + JSON sobre HTTPSAutenticación: header 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:

  1. Una cuenta en FacturaYA con un plan que incluya API (PYME o Empresarial).
  2. Tu llave fya_… y tu ID de empresa — ambos en el panel: Configuración → API para desarrolladores.
  3. La empresa emisora configurada (cédula + certificado .p12 de Hacienda), una sola vez desde el panel.
1

Guardá tus credenciales

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).

bash
# 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.

2

Creá el comprobante

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.

POST /empresas/{id}/comprobantes
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.

3

Firmá y enviá a Hacienda

Este paso firma el XML con el certificado de la empresa y lo transmite a Hacienda.

POST …/comprobantes/{id}/emitir
curl -X POST "$FYA_BASE/empresas/$FYA_EMPRESA/comprobantes/{id}/emitir" \
  -H "x-api-key: $FYA_KEY"
4

Conocé el resultado (aceptado / rechazado)

Hacienda responde de forma asíncrona. Tenés dos maneras de enterarte del resultado:

Opción A — Consultar el estado (pull):

POST …/comprobantes/{id}/consultar
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.

5

Descargá el PDF y el XML

El PDF sale con el diseño que la empresa configuró en el panel (logo, colores, QR).

GET …/pdf · GET …/xml
# 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.xml
6

Webhooks: recibí los eventos en tu sistema

En 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:

webhook
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):

node.js
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.

Campos obligatorios por tipo de documento

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.

La plataforma calcula por vos (no los enviás): Clave (50 díg.), NumeroConsecutivo (20 díg.), FechaEmision, todo el Emisor, los totales e impuestos y la firma XAdES-EPES.
Campo (nodo XML)FETENC/NDFECFEEREP
receptor (Receptor)oblopcopcoblopcobl
referencias (InformacionReferencia)opcopcobloblopcobl
lineas (DetalleServicio)obloblcondoblcondobl
codigoActividadReceptoropcopcoblopc
lineas[].partidaArancelariaopcobl*
mediosPago (MedioPago)opcopcopcopcopcobl
plazoCredito (PlazoCredito)condicional en todos: obligatorio si la venta es a crédito (condicionVenta 02/10)

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.

Referencia rápida

Otros endpoints (misma autenticación)

GET …/comprobantesListado con estado y saldo
GET …/comprobantes/referenciablesDocs con saldo para NC/ND/REP
GET · POST …/clientesCatálogo de clientes
GET · POST …/productosCatálogo de productos (CAByS)
GET …/planConsumo del mes vs. límite
GET …/recepcionFacturas de proveedores recibidas

Errores comunes

401Llave inválida/revocada, o el plan venció / no incluye API
400“Alcanzaste los N documentos” — se agotó el tope mensual del plan → subí de plan
400“no cumple el esquema v4.4” — falta o sobra un campo; el mensaje dice cuál

Apéndice — Catálogos de códigos (v4.4)

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

01Cédula física
02Cédula jurídica
03DIMEX
04NITE
05Extranjero no domiciliado
06No contribuyente

Condición de venta

condicionVenta

01Contado
02Crédito
03Consignación
04Apartado
05Arrendamiento con opción de compra
06Arrendamiento en función financiera
07Cobro a favor de un tercero
08Servicios prestados al Estado a crédito
09Pago del servicio prestado al Estado
10Venta a crédito hasta 90 días (IVA)
11Pago de venta a crédito hasta 90 días (IVA)
99Otros

Medio de pago

mediosPago[].medio

01Efectivo
02Tarjeta
03Cheque
04Transferencia / depósito bancario
05Recaudado por terceros
06SINPE Móvil
07Plataforma digital
99Otros

Tarifa de IVA

lineas[].codigoTarifaIva

01Tarifa 0% (Art. 32, num 1, RLIVA)
02Tarifa reducida 1%
03Tarifa reducida 2%
04Tarifa reducida 4%
05Transitorio 0%
06Transitorio 4%
07Transitorio 8%
08Tarifa general 13%
09Tarifa reducida 0,5%
10Tarifa exenta
11Tarifa 0% sin derecho a crédito

Tipo de documento de referencia

referencias[].tipoDoc

01Factura electrónica
02Nota de débito electrónica
03Nota de crédito electrónica
04Tiquete electrónico
05Nota de despacho
06Contrato
07Procedimiento
08Comprobante emitido en contingencia
09Devolución de mercadería
10Comprobante rechazado por Hacienda
11Sustituye factura rechazada por el receptor
12Sustituye factura de exportación
13Facturación mes vencido
15Sustituye una Factura Electrónica de Compra
16Comprobante de proveedor no domiciliado
99Otros

Código de referencia (motivo)

referencias[].codigo

01Anula documento de referencia
02Corrige texto del documento de referencia
04Referencia a otro documento
05Sustituye comprobante provisional por contingencia
06Devolución de mercancía
07Sustituye comprobante electrónico
08Factura endosada
09Nota de crédito financiera
10Nota de débito financiera
99Otros

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.