← Volver al inicio
API Reference

API de Facturovia

Usa la API de Facturovia para integrar la facturación en tus aplicaciones, web shops o automatizaciones.

Autenticación

Todas las peticiones requieren una cabecera Authorization con tu clave de API:

curl -H "Authorization: Bearer fv_live_YOUR_KEY" \
     https://facturo-production-c176.up.railway.app/api/invoices
Genera una clave en Configuración

Endpoints disponibles

Todas las rutas devuelven JSON. Los IDs son UUID. La fecha del cuerpo se acepta en formato YYYY-MM-DD.

MétodoRutaDescripción
GET/invoicesList invoices. Supports ?external_reference=ORDER-2847 for exact match.
POST/invoicesCreate a draft invoice. Accepts external_reference (optional, ≤ 255 chars).
GET/invoices/:idGet a single invoice with line items
POST/invoices/:id/issueIssue a draft invoice (status → issued)
POST/invoices/:id/correctRectificative invoice that negates every original line
POST/invoices/:id/rectifyPartial rectificative — only the items you specify (positive qty, server negates)
GET/invoices/:id/pdfDownload PDF of an issued invoice
GET/clientsList clients
POST/clientsCreate a client
POST/clients/find-or-createFind existing client by tax_id or create new one (idempotent)
GET/quotesList quotes. Supports ?external_reference= filter.
POST/quotesCreate a quote. Accepts external_reference (optional).

Buscar o crear

Busca un cliente por NIF/CIF. Si no existe, lo crea automáticamente. Patrón típico para integraciones con tiendas online: el cliente del e-commerce llega con su NIF, este endpoint te devuelve un client_id usable para crear la factura.

// 1. Find or create a client by tax_id (idempotent)
const clientRes = await fetch('https://facturo-production-c176.up.railway.app/api/clients/find-or-create', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer fv_live_...', 'Content-Type': 'application/json' },
  body: JSON.stringify({
    tax_id: 'B12345674',
    name: 'Talleres García S.L.',
    email: 'info@talleresgarcia.es',
    address: 'Calle Mayor 1',
    city: 'Madrid', postal_code: '28001', country: 'ES',
  }),
});
const { client, created } = await clientRes.json();
// created === true if new, false if it already existed

// 2. Create the invoice using the returned client.id
const invoiceRes = await fetch('https://facturo-production-c176.up.railway.app/api/invoices', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer fv_live_...', 'Content-Type': 'application/json' },
  body: JSON.stringify({
    client_id: client.id,
    invoice_date: new Date().toISOString().split('T')[0],
    currency: 'EUR', exchange_rate: 1,
    items: [{
      description: 'Filtro de aceite Toyota Corolla',
      quantity: 2, unit_price: 15.99,
      vat_rate: 21, discount_percent: 0,
    }],
  }),
});
const invoice = await invoiceRes.json();

// 3. Issue the invoice and download the PDF
await fetch(`https://facturo-production-c176.up.railway.app/api/invoices/${invoice.id}/issue`, {
  method: 'POST',
  headers: { 'Authorization': 'Bearer fv_live_...' },
});
const pdfRes = await fetch(`https://facturo-production-c176.up.railway.app/api/invoices/${invoice.id}/pdf`, {
  headers: { 'Authorization': 'Bearer fv_live_...' },
});
const pdfBlob = await pdfRes.blob();

Devoluciones y rectificaciones con external_reference

Las facturas y presupuestos aceptan un campo opcional external_reference (≤ 255 caracteres) para guardar el ID de pedido de tu sistema. Después puedes filtrar por él (?external_reference=ORDER-2847) para encontrar la factura original sin tener que mantener una tabla de equivalencias en tu lado.

// E-commerce return / rectification workflow using external_reference

// 1. Storefront creates the original invoice with its order id
await fetch('https://facturo-production-c176.up.railway.app/api/invoices', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer fv_live_...', 'Content-Type': 'application/json' },
  body: JSON.stringify({
    external_reference: 'ORDER-2847',
    client_id: client.id,
    invoice_date: new Date().toISOString().split('T')[0],
    currency: 'EUR', exchange_rate: 1,
    items: [
      { description: 'Filtro aceite REF-001',      quantity: 1, unit_price: 15.99, vat_rate: 21, discount_percent: 0 },
      { description: 'Correa distribución REF-045', quantity: 2, unit_price: 89.50, vat_rate: 21, discount_percent: 0 },
    ],
  }),
});

// 2. When the customer initiates a return, look up the original by your
//    own order id — no need to store the Facturovia invoice id locally.
const r = await fetch('https://facturo-production-c176.up.railway.app/api/invoices?external_reference=ORDER-2847', {
  headers: { 'Authorization': 'Bearer fv_live_...' },
});
const { data: [original] } = await r.json();

// 3. Issue a partial rectificativa for ONLY the returned items.
//    Quantities are positive — the server negates them. Items must
//    match a line on the original (by reference if provided, else by
//    description) and quantity is capped to the original quantity.
await fetch(`https://facturo-production-c176.up.railway.app/api/invoices/${original.id}/rectify`, {
  method: 'POST',
  headers: { 'Authorization': 'Bearer fv_live_...', 'Content-Type': 'application/json' },
  body: JSON.stringify({
    items: [
      { description: 'Filtro aceite REF-001',      reference: 'REF-001', quantity: 1, unit_price: 15.99, vat_rate: 21, discount_percent: 0 },
      { description: 'Correa distribución REF-045', reference: 'REF-045', quantity: 2, unit_price: 89.50, vat_rate: 21, discount_percent: 0 },
    ],
    notes: 'Devolución parcial — piezas defectuosas',
    causa_rectificacion: 'R1',
  }),
});
// → 201 with { invoice: { id, status: "draft", is_corrective: true,
//                          original_invoice_id, total: <negative>, ... } }

Permisos

Cada clave puede tener uno de los siguientes niveles de acceso:

  • full: Acceso completo. Puede crear, leer y modificar.
  • read: Solo lectura. Cualquier petición que no sea GET será rechazada con 403.

Códigos de error

La API devuelve códigos HTTP estándar:

  • 401Clave inválida o ausente.
  • 402Plan agotado o no permitido. Considera actualizar a Profesional.
  • 403La clave es de solo lectura o el rol no tiene permiso.
  • 422Datos inválidos. La respuesta incluye los campos con error.