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
# 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
- Entra en tu panel: https://app.apifactu.es → API keys → «Crear clave».
- Copia la clave en el momento de crearla: por seguridad no se vuelve a mostrar entera.
- Guárdala en una variable de entorno (por ejemplo
APIFACTU_API_KEY); no la escribas en el código ni la subas al repositorio.
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
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
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á).
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
| Error | Cuándo | Qué hacer |
|---|---|---|
ApifactuConfigError | Falta la clave o tiene un formato que no es eck_live_ / eck_test_ | Revisa la variable de entorno |
ApifactuApiError | Validación (422) u otro 4xx | Corrige el payload; no reintentes |
ApifactuRateLimitError | 429 tras agotar los reintentos | Espera lo que indique Retry-After |
ApifactuServerError | 5xx tras agotar los reintentos | Reintenta más tarde con la misma idempotency key |
Todos heredan de ApifactuError. Los tipos (CreateInvoiceRequest, InvoiceResponse, WebhookEvent…) se exportan del paquete.