Librería para JavaScript / TypeScript

Paquete @apifactu/sdk · versión 1.2.0 · Node.js 18 a 22 · ESM y CommonJS, tipos incluidos

Cliente oficial para Node y para tu servidor (no para el navegador: la clave nunca debe salir de tu backend). Reintenta en 429 y 5xx, nunca en 4xx, y verifica la firma de los webhooks por ti.

1. Instalación

npm@apifactu/sdk
# Desde npm
npm install @apifactu/sdk

# O con el ZIP que descargas en tu panel → Integraciones
npm install ./apifactu-sdk-js-1.2.0.zip

2. Tu clave

  1. Entra en tu panel: https://app.apifactu.es → API keys → «Crear clave».
  2. Copia la clave en el momento de crearla: por seguridad no se vuelve a mostrar entera.
  3. Guárdala en una variable de entorno (por ejemplo APIFACTU_API_KEY); no la escribas en el código ni la subas al repositorio.
Probar sin efectos fiscales. Crea en tu panel una clave de pruebas (empieza por eck_test_) y úsala igual que la real. El host es el mismo (https://api.apifactu.es): el entorno lo decide la clave. Con eck_test_ las facturas se numeran y devuelven QR y huella, pero no se comunican a Hacienda. Cuando todo funcione, cambia la clave por una eck_live_ y ya estás facturando de verdad.

3. Primera factura

POST/v1/invoices
import { ApifactuClient } from "@apifactu/sdk";
// CommonJS: const { ApifactuClient } = require("@apifactu/sdk");

const client = new ApifactuClient({ apiKey: process.env.APIFACTU_API_KEY });  // eck_test_… para probar

const inv = await client.invoices.create(
  {
    referencia: "PED-1001",          // tu identificador del pedido
    base_imponible: 100,
    tipo_iva: 21,
    nif_receptor: "12345678Z",
    nombre_receptor: "Cliente SL",
  },
  "PED-1001",                        // idempotency key: reintentar nunca duplica
);
console.log(inv.status, inv.numero_completo, inv.qr_url);

Guarda inv.id: es lo que necesitan anular y rectificar. La confirmación de Hacienda llega después, por webhook.

4. Consultar

GET/v1/invoices
const detalle = await client.invoices.get(inv.id);            // estado, QR, huella, CSV de la AEAT
const pagina  = await client.invoices.list({ status: "ACCEPTED", page: 1, per_page: 20 });

5. Webhooks firmados

Registra la URL de tu servidor y verifica cada entrega sobre el cuerpo crudo (en Express, usa express.raw() en esa ruta; si el framework ya ha parseado el JSON, la firma no coincidirá).

POST/v1/webhooks
const wh = await client.webhooks.register("https://mitienda.es/webhook",
                                          ["invoice.accepted", "invoice.rejected"]);
// wh.secret solo se muestra al crearlo: guárdalo

// En tu endpoint (ejemplo con Express)
import { verifyWebhook, parseWebhookEvent } from "@apifactu/sdk";

app.post("/webhook", express.raw({ type: "*/*" }), (req, res) => {
  if (!verifyWebhook(req.body, req.headers, process.env.WEBHOOK_SECRET)) return res.sendStatus(400);
  const event = parseWebhookEvent(req.body.toString());
  // event.event === "invoice.accepted" | "invoice.rejected" …
  res.sendStatus(200);
});

verifyWebhook usa la firma versionada X-Apifactu-Signature (t=…,v1=…, con protección frente a reenvíos) si viene, y si no la firma plana X-Fiscal-Signature. Lee las cabeceras sin distinguir mayúsculas.

Errores

ErrorCuándoQué hacer
ApifactuConfigErrorFalta la clave o tiene un formato que no es eck_live_ / eck_test_Revisa la variable de entorno
ApifactuApiErrorValidación (422) u otro 4xxCorrige el payload; no reintentes
ApifactuRateLimitError429 tras agotar los reintentosEspera lo que indique Retry-After
ApifactuServerError5xx tras agotar los reintentosReintenta más tarde con la misma idempotency key

Todos heredan de ApifactuError. Los tipos (CreateInvoiceRequest, InvoiceResponse, WebhookEvent…) se exportan del paquete.

Crear cuenta y obtener API key →