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í:
| Campo | Qué te dice |
|---|---|
account | La 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_key | La propia key. scopes son los scopes efectivos: un scope que no permite el plan de los NIF a los que llega no aparece. |
scope | Los 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_accounts | Las 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 propio | En scope. |
| Cedido por otra cuenta | En 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 aprovisionaste | En 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.pdfPOST /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.
SDKs oficiales
Ejecuta este mismo flujo con @factuarea/sdk (TypeScript) o factuarea/factuarea-php — reintentos, idempotencia, paginación y errores tipados incluidos.
Modo de prueba y sandbox
Qué desactiva una key fact_test_, cómo se aísla la empresa sandbox, y cómo promocionar tu integración a producción.