API REST v1

API para desarrolladores

Conecta Faktu con tu tienda, tu CRM o tus scripts. Incluida en el plan Pro.

Antes de empezar

La API lee y crea los datos de una empresa: clientes, productos, presupuestos, gastos y borradores de factura, y marca facturas como cobradas. Todas las respuestas son JSON en UTF-8; los importes van en euros con dos decimales.

Nada fiscal por API. Ninguna llamada emite, anula ni rectifica una factura: eso se hace en Faktu, que es quien la registra en VeriFactu. Un borrador creado por API se revisa y se emite desde el panel.

URL base:

https://www.faktu.org/api/v1

Autenticación

Crea una clave en Faktu, en Ajustes → Integraciones → API REST (solo la persona propietaria o un administrador del espacio). La clave se enseña una sola vez: guárdala en un gestor de secretos. Si se filtra, revócala allí mismo y crea otra.

Mándala en cada petición, siempre por HTTPS:

curl https://www.faktu.org/api/v1/clientes \
  -H "Authorization: Bearer fk_live_…"

Cada clave tiene un alcance:

  • lectura: solo GET.
  • lectura y escritura: además POST, PATCH y DELETE.

Endpoints

MétodoRutaQué haceAlcance
GET/facturasListar facturaslectura
GET/facturas/{id}Ver una facturalectura
POST/facturasCrear un borrador de facturaescritura
POST/facturas/{id}/cobrarMarcar una factura como cobradaescritura
GET/clientesListar clienteslectura
GET/clientes/{id}Ver un clientelectura
POST/clientesCrear un clienteescritura
PATCH/clientes/{id}Cambiar un clienteescritura
GET/productosListar productoslectura
GET/productos/{id}Ver un productolectura
POST/productosCrear un productoescritura
PATCH/productos/{id}Cambiar un productoescritura
GET/presupuestosListar presupuestoslectura
GET/presupuestos/{id}Ver un presupuestolectura
POST/presupuestosCrear un presupuestoescritura
PATCH/presupuestos/{id}Cambiar un presupuestoescritura
GET/gastosListar gastos · módulo de gastoslectura
GET/gastos/{id}Ver un gasto · módulo de gastoslectura
POST/gastosCrear un gasto · módulo de gastosescritura
PATCH/gastos/{id}Cambiar un gasto · módulo de gastosescritura
GET/empresaVer la empresa de la clavelectura
POST/webhooksSuscribir una URL a eventosescritura
DELETE/webhooks/{id}Cancelar una suscripciónescritura

Para comprobar una clave, GET /empresa devuelve la empresa a la que da acceso y sus alcances.

La especificación completa (campos, tipos, filtros y errores de cada ruta) está en /api/v1/openapi.json (OpenAPI 3.1): impórtala en Postman, Insomnia o tu generador de clientes.

Crear un borrador de factura

curl -X POST https://www.faktu.org/api/v1/facturas \
  -H "Authorization: Bearer fk_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pedido-1042" \
  -d '{
    "cliente": { "nombre": "Talleres Martín SL", "nif": "B12345674", "pais": "ES" },
    "lineas": [
      { "concepto": "Revisión anual", "unidades": 1, "precio": 120, "iva": 21 }
    ]
  }'

Con cliente se usa el cliente que tenga ese NIF o se crea uno; con cliente_id, uno que ya exista. La respuesta (201) es la factura en estado borrador con un número provisional, que Faktu fija al emitirla.

Marcar una factura como cobrada

curl -X POST https://www.faktu.org/api/v1/facturas/{id}/cobrar \
  -H "Authorization: Bearer fk_live_…"

Solo facturas emitidas. Si ya estaba cobrada, devuelve la factura sin cambios.

Paginación y filtros

Los listados van del más reciente al más antiguo, 25 por página (limit hasta 100). Si has_more es true, pide la siguiente con cursor = next_cursor:

{
  "object": "list",
  "data": [ … ],
  "has_more": true,
  "next_cursor": "MjAyNi0xMC0xOVQxMzoyOTo…"
}

Filtros: facturas por estado, fecha_desde, fecha_hasta, cliente_id y cobrada; presupuestos por estado, fechas y cliente; gastos por fechas, proveedor_id y categoria; clientes por nif y email (sin distinguir mayúsculas), y productos por sku. Un parámetro desconocido devuelve 400.

Reintentos seguros

Envía Idempotency-Key en los POST (hasta 255 caracteres; un id de pedido o un UUID). Si repites la petición con la misma clave en las siguientes 24 horas, recibes la misma respuesta (con Idempotent-Replayed: true) y no se crea nada dos veces. La misma clave con otra petición devuelve 422.

Límites

120 peticiones por minuto y clave. Cada respuesta lleva X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset; al pasarte, 429 con Retry-After en segundos. El cuerpo de una petición no puede pasar de 256 KB.

Errores

Todos los errores tienen la misma forma. Comprueba code; el texto puede cambiar.

{
  "error": {
    "code": "invalid_request",
    "message": "El NIF no es válido. Revisa el número y la letra de control",
    "details": [{ "path": "cliente.nif", "message": "…" }]
  }
}
HTTPcodeSignifica
400invalid_requestLa petición no es válida.
401unauthorizedFalta la clave de API o no es válida.
402plan_requiredEsta función está incluida en el plan Pro.
403forbiddenLa clave no tiene permiso para esta operación.
404not_foundNo existe.
405method_not_allowedMétodo no permitido.
409conflictConflicto con el estado actual del recurso.
413payload_too_largeEl cuerpo de la petición es demasiado grande.
415unsupported_media_typeEl cuerpo debe ser JSON (Content-Type: application/json).
422idempotency_conflictEsa Idempotency-Key ya se usó con otra petición.
429rate_limitedDemasiadas peticiones. Espera un poco y vuelve a intentarlo.
500internal_errorError interno. Inténtalo de nuevo más tarde.
503not_configuredEsta integración no está configurada en este servidor.

Webhooks

En vez de preguntar cada poco, Faktu puede avisar a tu servidor cuando algo pasa. Se configuran en Ajustes → Integraciones → Webhooks (URL https y eventos). El cuerpo de cada aviso lleva el mismo objeto que devuelve la API.

EventoCuándo
factura.borrador_creadoBorrador de factura creado
factura.emitidaFactura emitida
factura.cobradaFactura cobrada
factura.anuladaFactura anulada
cliente.creadoCliente creado
cliente.actualizadoCliente actualizado
presupuesto.aceptadoPresupuesto aceptado
gasto.creadoGasto creado
producto.creadoProducto creado

Verificar la firma

Cada aviso lleva la cabecera Faktu-Signature: t=…,v1=…: t es la hora del envío (segundos Unix) y v1, el HMAC-SHA256 en hexadecimal de t + "." + cuerpo con el secreto whsec_… del endpoint. Calcúlalo sobre el cuerpo tal cual llega (sin volver a serializarlo), compáralo en tiempo constante y rechaza los avisos de hace más de 5 minutos.

import { createHmac, timingSafeEqual } from "node:crypto"

function firmaValida(cuerpo, cabecera, secreto) {
  const partes = Object.fromEntries(cabecera.split(",").map((p) => p.split("=")))
  const t = Number(partes.t)
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false
  const esperada = createHmac("sha256", secreto).update(`${t}.${cuerpo}`).digest("hex")
  const recibida = Buffer.from(partes.v1 ?? "", "utf8")
  return recibida.length === esperada.length &&
    timingSafeEqual(recibida, Buffer.from(esperada, "utf8"))
}

Cada aviso indica su origen (panel, api, shopify…): si tu integración crea datos por API, ignora los avisos con origen: api para no entrar en bucle.

Suscribirse por API

Una herramienta puede crear su propio endpoint sin pasar por el panel (es lo que hace Faktu para Zapier). La respuesta trae el secreto una sola vez; para darse de baja, DELETE /webhooks/{id}. Por API solo se borran los endpoints creados por API.

curl -X POST https://www.faktu.org/api/v1/webhooks \
  -H "Authorization: Bearer fk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://tu-dominio.com/webhooks/faktu", "eventos": ["factura.emitida"] }'