Factuarea APIDevelopers

Inicio rápido

Tu primera factura en 5 minutos — verifica tu key, consigue una serie y un impuesto, crea un contacto, emite una factura y envíala. Una sola secuencia de copiar y pegar contra una key fact_test_.

Esta guía te lleva de una API key recién creada a una factura real (en sandbox) en cinco pasos. Cada llamada de abajo es ejecutable copiando y pegando contra una key fact_test_ — sin emails reales, sin envío a la AEAT, sin consumir numeración de producción. Consulta Modo de prueba & sandbox para ver qué desactiva el modo "test".

¿Prefieres un SDK? Si trabajas con TypeScript/Node o PHP, los SDKs oficiales envuelven todo este flujo con reintentos integrados, idempotencia, paginación por cursor y errores tipados — npm install @factuarea/sdk o composer require factuarea/factuarea-php. Los pasos HTTP en crudo de abajo funcionan en cualquier lenguaje y muestran exactamente lo que el SDK envía por dentro.

Ejecuta todo primero con una key fact_test_. La superficie de la API es idéntica en producción y en test — cuando tu flujo funcione de principio a fin, cambia el prefijo a fact_live_ para pasar a producción. Consigue una key de test en Settings → Developers → API Keys.

Exporta tu key una vez para que cada snippet la recoja:

export FACTUAREA_API_KEY="fact_test_3pXnR2VbY7TcA9eFmN5z8KqW"

La base URL es https://api.factuarea.com/v1. Autentícate con Authorization: Bearer (o con el header equivalente X-API-Key). Los identificadores son valores id opacos (UUID v7); los copias de una respuesta a la siguiente.

Verifica tu key

GET /v1/me introspecciona la credencial. No necesita ningún scope: cualquier key válida puede consultarse a sí misma, y es la forma de conocer el id opaco de los NIF a los que llega la key. Ese id es el segmento {company} que lleva toda ruta de empresa.

curl https://api.factuarea.com/v1/me \
  -H "Authorization: Bearer $FACTUAREA_API_KEY"

Esta es la respuesta de una key live de un proveedor de cuentas, así que todos los bloques vienen rellenos. Con tu key fact_test_, api_key.prefix y api_key.environment dicen test.

{
  "data": {
    "object": "me",
    "account": {
      "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a00",
      "name": "Asesoría Martínez",
      "environment": "live",
      "is_provider": true,
      "privileged_scopes": [
        "accounts:read",
        "accounts:write"
      ]
    },
    "api_key": {
      "id": "019e81ef-e1d4-7208-a51d-94c2c2a54a84",
      "name": "Producción",
      "prefix": "fact_live_1OSf9KdP",
      "scopes": [
        "account:read",
        "series:read",
        "series:write"
      ],
      "tier": "scale",
      "environment": "live",
      "created_at": "2026-01-15T09:30:00Z",
      "last_used_at": "2026-05-25T18:00:00Z",
      "expires_at": null
    },
    "scope": [
      {
        "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01",
        "tax_id": "B12345674",
        "name": "Acme Corporation"
      },
      {
        "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a02",
        "tax_id": "B87654323",
        "name": "Acme Logística S.L."
      }
    ],
    "client_accounts": [
      {
        "id": "0199a1f0-1c22-7a10-b111-5a5b5c5d5e5f",
        "name": "Construcciones Ruiz",
        "origin": "granted",
        "access_level": "view",
        "status": "active",
        "paid_by_provider": false,
        "environment": "live"
      },
      {
        "id": "0199a1f0-1c22-7a10-b111-6a6b6c6d6e6f",
        "name": "Talleres García",
        "origin": "provisioned",
        "access_level": "operate",
        "status": "provisioned",
        "paid_by_provider": true,
        "environment": "live"
      }
    ],
    "client_accounts_visible": true,
    "client_accounts_has_more": false
  }
}

Un 200 aquí significa que la key es válida. Lee la respuesta así:

CampoQué te dice
accountLa cuenta de la key. id es el valor del segmento {account} de las rutas de cuenta, is_provider indica si la cuenta aprovisiona cuentas gestionadas para sus clientes y privileged_scopes lista los scopes privilegiados que Factuarea le concedió.
api_keyLa propia key. scopes son los scopes efectivos: un scope que no permite el plan de los NIF a los que llega no aparece.
scopeLos NIF a los que llega la key, cada uno con su id, su tax_id y su name. Siempre se enumeran, también cuando solo hay uno.
client_accountsLas cuentas de tus clientes que tu cuenta alcanza por una relación. origin es granted cuando te cedieron acceso y provisioned cuando las aprovisionaste y las sigues gestionando. Solo se rellena cuando la key lleva el scope privilegiado accounts:read; si no, vuelve vacía y client_accounts_visible es false.

Si recibes 401 invalid_api_key, revisa el valor. Si un paso posterior falla con 403 insufficient_scope, el array scopes de arriba te dice qué falta. La key de este ejemplo solo lleva account:read, series:read y series:write: cada paso de abajo indica el scope que necesita, así que compáralo con los scopes de tu propia key. Cuentas y NIF describe cada campo.

Elige el {company}

Los NIF a los que llega una key pueden ser de tres clases, y todos van en el mismo segmento {company}:

El NIF es…Dónde lees su id
Uno propioEn scope.
Cedido por otra cuentaEn scope, cuando el ámbito de la key lo nombra. La cuenta que lo cedió aparece en client_accounts con origin: "granted". Consulta acceso cedido.
De una cuenta gestionada que aprovisionasteEn scope, cuando el ámbito de la key lo nombra y tu nivel en la cuenta no es none. La cuenta aparece en client_accounts con origin: "provisioned", y companies[] de GET /v1/accounts/{account} lista sus NIF, con ese id como {account}. Consulta cuentas gestionadas.

Elijas la clase que elijas, las rutas de abajo son las mismas. Un NIF que tu key no alcanza responde 404 profile_not_found, igual que uno que no existe. En un NIF donde tu nivel es view puedes leer, pero cualquier escritura — el contacto y la factura de abajo, por ejemplo — responde 403 company_read_only. Con una key fact_test_, client_accounts solo lista las cuentas del entorno test; Probar cuentas gestionadas las recorre. Alcanzar un NIF por el segmento de empresa explica las tres clases, sus niveles y cada respuesta.

Exporta el NIF para el que vas a facturar — toda ruta de empresa de abajo lleva su id opaco en el path. Aquí, la primera entrada de scope:

export COMPANY_ID="01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01"

Consigue los ids que vas a necesitar

Una factura referencia una serie (su numeración) y cada línea referencia un tipo impositivo. Ambos son recursos existentes que listas una vez y reutilizas.

Un id de serie

GET /v1/companies/{company}/series devuelve tus series de numeración. Elige una cuyo document_type sea invoice e invoice_kind sea complete (la marcada como is_default es una opción segura). Necesita series:read.

curl "https://api.factuarea.com/v1/companies/$COMPANY_ID/series?document_type=invoice" \
  -H "Authorization: Bearer $FACTUAREA_API_KEY"
{
  "data": [
    {
      "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0e",
      "object": "series",
      "code": "F-2026",
      "name": "Facturas 2026",
      "document_type": "invoice",
      "invoice_kind": "complete",
      "prefix": "F-2026-",
      "next_number": 46,
      "year_reset": true,
      "is_default": true,
      "is_active": true,
      "created_at": "2026-01-01T00:00:00Z",
      "updated_at": "2026-01-20T11:30:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Copia el id (01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0e) — ese es tu series_id. El campo es opcional al crear una factura: sin él, la factura va a tu serie por defecto del propósito correcto, que se crea en el momento si no tienes ninguna.

Un id de tipo impositivo

GET /v1/companies/{company}/taxes devuelve el catálogo de impuestos (impuestos globales del sistema + los tuyos personalizados). Para una línea de factura española estándar quieres el tipo de IVA al 21% — busca type: "vat" y rate: 21. Necesita taxes:read.

curl "https://api.factuarea.com/v1/companies/$COMPANY_ID/taxes?type=vat" \
  -H "Authorization: Bearer $FACTUAREA_API_KEY"
{
  "data": [
    {
      "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0f",
      "object": "tax",
      "name": "IVA general 21%",
      "code": "IVA21",
      "rate": 21,
      "type": "vat",
      "applies_to": "both",
      "country": "ES",
      "is_default": true,
      "is_active": true,
      "is_system": true
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Copia este id — en una línea de factura va en tax_rate_id.

tax_rate_id vs tax_rate. En una línea puedes referenciar un tipo del catálogo por tax_rate_id, o saltarte la búsqueda y pasar el porcentaje numérico directamente como tax_rate (p. ej. "tax_rate": 21). Usa uno u otro por línea — tax_rate_id mantiene la línea vinculada a tu catálogo, tax_rate es un override inline rápido.

Crea un contacto con rol de cliente

Crea un contacto con name, kind, roles e identidad fiscal válida. Para esta empresa española usa kind: "company", roles: ["customer"] y tax_id (NIF/CIF/NIE). Necesita contacts:write.

Esto es una escritura — envía una Idempotency-Key para que una petición reintentada nunca cree un contacto duplicado. Consulta Idempotencia.

curl -X POST https://api.factuarea.com/v1/companies/$COMPANY_ID/contacts \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "Cliente Demo SL",
    "kind": "company",
    "roles": ["customer"],
    "tax_id": "B76543214"
  }'
{
  "data": {
    "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a03",
    "object": "contact",
    "kind": "company",
    "name": "Cliente Demo SL",
    "business_name": null,
    "commercial_name": null,
    "tax_id": "B76543214",
    "vat_id": null,
    "alternative_id": null,
    "address": {
      "line1": null,
      "line2": null,
      "number": null,
      "floor": null,
      "door": null,
      "staircase": null,
      "city": null,
      "province": null,
      "postal_code": null,
      "country": "ES"
    },
    "contact_person": null,
    "email": null,
    "phone": null,
    "mobile": null,
    "fax": null,
    "website": null,
    "billing_emails": [],
    "tags": [],
    "coordinates": null,
    "notes": null,
    "metadata": {},
    "accumulate_347": true,
    "external_id": null,
    "roles": [
      {
        "role": "customer",
        "status": "active",
        "assigned_at": "2026-08-29T10:00:00+00:00",
        "deactivated_at": null
      }
    ],
    "bank_accounts": [],
    "customer_profile": null,
    "supplier_profile": null,
    "financial_summary": {
      "sales": {
        "invoiced_amount": "0.00",
        "collected_amount": "0.00",
        "receivable_amount": "0.00",
        "currency": "EUR"
      },
      "purchases": {
        "purchased_amount": "0.00",
        "paid_amount": "0.00",
        "payable_amount": "0.00",
        "currency": "EUR"
      }
    },
    "latest_activity": null,
    "is_archived": false,
    "archived_at": null,
    "created_at": "2026-08-29T10:00:00+00:00",
    "updated_at": "2026-08-29T10:00:00+00:00"
  }
}

(Los campos opcionales que no enviaste vuelven como null; address siempre es un objeto cuyas subclaves se rellenan a medida que las proporcionas.) Copia el id devuelto (01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a03) — ese es el contacto al que facturas, y el valor que la factura toma como client_id.

El campo del documento sigue siendo client_id: envía el id canónico del contacto con rol customer activo.

Crea la factura

Ahora combina los tres ids. POST /v1/companies/{company}/invoices requiere client_id, series_id, issued_on, due_on y al menos una línea. Cada línea necesita description, quantity y unit_price; añade tax_rate_id (o tax_rate) para aplicar IVA. Opcionales por línea: discount_percent y product_id. Necesita invoices:write.

curl -X POST https://api.factuarea.com/v1/companies/$COMPANY_ID/invoices \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "client_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a03",
    "series_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0e",
    "issued_on": "2026-06-02",
    "due_on": "2026-07-02",
    "lines": [
      {
        "description": "Consultoría — junio 2026",
        "quantity": 10,
        "unit_price": 100,
        "tax_rate_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0f",
        "discount_percent": 0
      }
    ]
  }'

La API calcula por ti los totales de la línea y del documento y devuelve el envoltorio de la factura. Una factura recién creada empieza como borrador: todavía sin number definitivo (is_number_assigned: false).

{
  "data": {
    "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a42",
    "object": "invoice",
    "number": null,
    "is_number_assigned": false,
    "type": "F1",
    "series": {
      "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0e",
      "code": "F-2026"
    },
    "client": {
      "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a03",
      "name": "Cliente Demo SL"
    },
    "status": "draft",
    "issued_on": "2026-06-02",
    "due_on": "2026-07-02",
    "subtotal": "1000.00",
    "taxes_total": "210.00",
    "total": "1210.00",
    "currency": "EUR",
    "notes": null,
    "lines": [
      {
        "object": "invoice_line",
        "description": "Consultoría — junio 2026",
        "product": null,
        "quantity": 10,
        "unit_price": "100.0000",
        "tax_rate": 21,
        "discount_percent": 0,
        "subtotal": "1000.00",
        "taxes": "210.00",
        "total": "1210.00"
      }
    ],
    "metadata": {},
    "operation_regime": "general",
    "verifactu_status": "not_applicable",
    "is_corrective": false,
    "corrective": null,
    "payment": null,
    "public_link": null,
    "substituted_by": null,
    "recurring": null,
    "paid_recorded_at": null,
    "paid_date": null,
    "sent_at": null,
    "voided_at": null,
    "void_reason": null,
    "created_at": "2026-06-02T10:31:00Z",
    "updated_at": "2026-06-02T10:31:00Z"
  }
}

Fíjate en los campos monetarios calculados: el subtotal de la línea (10 × 100 = 1000), sus taxes (21% de 1000 = 210) y el total (1210), agregados en el subtotal / taxes_total / total de la factura. Los importes vuelven como cadenas decimales — dos decimales en un total ("1000.00") y cuatro en el unit_price de una línea ("100.0000") — y el unit_price que envías puede ser un número o esa misma cadena. Copia el id de la factura (01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a42) para el siguiente paso.

Obtén el PDF y envíalo

Con el id de la factura puedes descargar su PDF y enviárselo por email al cliente.

GET /v1/companies/{company}/invoices/{id}/pdf transmite el PDF binario (application/pdf). Necesita pdfs:read. Guárdalo directamente a un fichero con la opción -o de curl:

curl https://api.factuarea.com/v1/companies/$COMPANY_ID/invoices/01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a42/pdf \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -o invoice.pdf

POST /v1/companies/{company}/invoices/{id}/send envía la factura por email al cliente. Sin body usa el email del cliente registrado en ficha; puedes sobreescribir el destinatario y la copia con to, cc, bcc, subject y body. Necesita invoices:send.

Como estás con una key fact_test_, el email no se entrega a ningún destinatario real (los efectos del sandbox están desactivados). La llamada igualmente tiene éxito y la factura transiciona como lo haría en producción — perfecto para montar tu flujo sin hacer spam a nadie.

curl -X POST https://api.factuarea.com/v1/companies/$COMPANY_ID/invoices/01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a42/send \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "to": "demo@example.com",
    "subject": "Tu factura de Acme Corporation"
  }'

La respuesta es el envoltorio actualizado de la factura (con la misma forma que arriba), ahora con sent_at poblado.

Y ya está

Verificaste una key, descubriste los ids que necesita, creaste un contacto, emitiste una factura con totales calculados en el servidor y la enviaste — todo contra un sandbox aislado. A partir de aquí, apunta el mismo código a una key fact_live_ para operar sobre tu empresa real.

En esta página

¿Te echamos una mano?Contactar con soporte