Factuarea APIDevelopers

Resumen de los SDKs

SDKs oficiales de TypeScript y PHP para la API de Factuarea — instala @factuarea/sdk o factuarea/factuarea-php y obtén reintentos, idempotencia, paginación por cursor, errores tipados y verificación de webhooks de serie.

Los SDK oficiales de Factuarea ofrecen reintentos automáticos, idempotencia, paginación por cursor, errores tipados, verificación de webhooks y descargas binarias para su catálogo de métodos publicado. REST expone actualmente 469 operaciones en 41 recursos; consulta la cobertura de versiones al final.

Pre‑GA (0.x). Ambos SDKs están en 0.x. La superficie pública de métodos es estable y sigue el contrato de nomenclatura de métodos del SDK, protegido por SemVer — pero mientras esté en 0.x, las versiones minor pueden incluir cambios incompatibles hasta 1.0.0, que coincide con la GA de la API. Cada release fija una Factuarea-Version y la envía en cada request, de modo que el comportamiento de la API se mantiene estable hasta que actualizas el SDK.

Solo en el servidor. Tu API key es un secreto. Nunca incluyas un SDK con una clave live en un navegador, app móvil o cualquier cliente público — usa el SDK desde tu backend.

Instalación

npm install @factuarea/sdk

Requiere Node 20 o superior. El SDK está construido sobre el estándar Web fetch, así que también funciona en Deno, Bun y Cloudflare Workers.

composer require factuarea/factuarea-php

Requiere PHP 8.2 o superior con las extensiones json y mbstring (ambas incluidas en las builds estándar de PHP).

Autenticación y entornos

Pasa tu API key. El prefijo de la clave selecciona el entorno — no hay un flag aparte: una clave fact_test_… siempre se ejecuta contra el sandbox aislado, y una clave fact_live_… contra producción.

import { Factuarea } from "@factuarea/sdk";

const factuarea = new Factuarea({ apiKey: process.env.FACTUAREA_API_KEY! });

factuarea.environment; // "test" or "live", derived from the key prefix

Configuración opcional:

new Factuarea({
  apiKey: "fact_live_…",                    // required
  baseUrl: "https://api.factuarea.com/v1",  // override for staging
  timeout: 60_000,                          // per-request ms (default 60s)
  maxRetries: 2,                            // attempts after the first try
  factuareaVersion: "2026-06-04",           // pinned API version header
  defaultHeaders: {},                       // extra headers on every request
});
<?php

require 'vendor/autoload.php';

use Factuarea\Sdk\Custom\FactuareaClient;

// The key prefix selects the environment:
//   fact_test_… → sandbox    fact_live_… → production
$factuarea = FactuareaClient::create(getenv('FACTUAREA_API_KEY'));

FactuareaClient::create() es el punto de entrada recomendado: conecta la autenticación Bearer y registra por ti el comportamiento automático de Idempotency-Key. Para configuración avanzada (cliente Guzzle personalizado, política de reintentos personalizada, base URL de staging) el builder generado sigue disponible:

use Factuarea\Sdk\Factuarea;
use Factuarea\Sdk\Models\Components\Security;

$factuarea = Factuarea::builder()
    ->setSecurity(new Security(bearerAuth: getenv('FACTUAREA_API_KEY')))
    ->setServerURL('https://api.factuarea.com/v1')
    ->build();

Inicio rápido

Crea un contacto con rol de cliente y una factura, y descarga su PDF con los métodos publicados del SDK que aparecen a continuación. Para otras operaciones, comprueba tu SDK instalado o usa el ejemplo HTTP de la referencia de la API.

La creación del contacto en PHP usa HTTP con Guzzle, que el SDK PHP ya requiere. Así funciona sin asumir que tu versión PHP instalada tiene métodos de contactos generados. El SDK de facturas conserva clientId: pasa el id del contacto con rol customer activo.

import { Factuarea } from "@factuarea/sdk";

const factuarea = new Factuarea({ apiKey: process.env.FACTUAREA_API_KEY! });

// Responses are the API's `{ data: … }` envelope — read the resource off `.data`.

// 1. Create a customer contact.
const { data: contact } = await factuarea.contacts.create({
  name: "Cliente Demo SL",
  kind: "company",
  roles: ["customer"],
  tax_id: "B12345674",
});

// 2. Create an invoice (the API computes the totals).
const { data: invoice } = await factuarea.invoices.create({
  client_id: contact.id,
  series_id: "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0e",
  issued_on: "2026-06-05",
  due_on: "2026-07-05",
  lines: [
    {
      description: "Consultoría — junio 2026",
      quantity: 10,
      unit_price: 100,
      tax_rate_id: "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0f",
    },
  ],
});

// 3. Download the PDF (a BinaryResponse, not JSON).
const pdf = await factuarea.invoices.pdf(invoice.id);
await import("node:fs/promises").then((fs) =>
  fs.writeFile("invoice.pdf", pdf.toBuffer()),
);
<?php

require 'vendor/autoload.php';

use Factuarea\Sdk\Custom\FactuareaClient;
use Factuarea\Sdk\Models\Components;
use Brick\DateTime\LocalDate;

$factuarea = FactuareaClient::create(getenv('FACTUAREA_API_KEY'));

// 1. Create a customer contact.
$http = new \GuzzleHttp\Client();
$response = $http->post('https://api.factuarea.com/v1/contacts', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('FACTUAREA_API_KEY'),
        'Idempotency-Key' => bin2hex(random_bytes(16)),
    ],
    'json' => [
        'name' => 'Cliente Demo SL',
        'kind' => 'company',
        'roles' => ['customer'],
        'tax_id' => 'B12345674',
    ],
]);
$contact = json_decode((string) $response->getBody(), true, 512, JSON_THROW_ON_ERROR)['data'];

// 2. Create an invoice (the API computes the totals).
$invoice = $factuarea->invoices->publicApiV1InvoicesCreate(
    new Components\CreateInvoiceRequest(
        clientId: $contact['id'],
        seriesId: '01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0e',
        issuedOn: LocalDate::parse('2026-06-05'),
        dueOn: LocalDate::parse('2026-07-05'),
        lines: [
            new Components\CreateInvoiceRequestLine(
                description: 'Consultoría — junio 2026',
                quantity: 10,
                unitPrice: 100,
                taxRateId: '01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0f',
            ),
        ],
    ),
);

// 3. Download the PDF.
$pdf = $factuarea->invoices->publicApiV1InvoicesPdf($invoice->object->data->id);
file_put_contents('invoice.pdf', $pdf->bytes ?? '');

Ejecuta todo primero con una clave fact_test_ — los efectos del sandbox (VeriFactu → AEAT, FACe, email, webhooks) están desactivados. Cuando tu flujo funcione de extremo a extremo, cambia el prefijo a fact_live_. La superficie de la API es idéntica en ambos. Consulta Test mode & sandbox.

Funciones en tiempo de ejecución

Ambos SDKs comparten el mismo runtime escrito a mano sobre la superficie tipada generada:

  • Reintentos automáticos — los fallos transitorios (429 y 5xx, además de errores de red en TypeScript) se reintentan con backoff exponencial y jitter, respetando el header Retry-After. Los errores de cliente deterministas (p. ej. validación 422) nunca se reintentan.
  • Idempotencia automática — cada mutación recibe una Idempotency-Key generada para que un request reintentado nunca cree un recurso por duplicado. Anúlala por llamada cuando quieras deduplicación a nivel de app. Consulta Idempotency.
  • Auto‑paginación por cursor — los métodos de listado devuelven un iterable que recorre todas las páginas por ti, gestionando next_cursor / has_more. Consulta Paginar con el SDK.
  • Errores tipados — el envoltorio de error de la API se mapea a una jerarquía de excepciones tipada que expone code, type, request_id y status. Tu API key nunca se incluye en ningún mensaje de error. Consulta Gestionar errores.
  • Verificación de webhooks — un verificador HMAC‑SHA256 de tiempo constante que respeta la ventana de gracia de rotación de secreto. Consulta Verificar webhooks.
  • Descargas binarias — los endpoints de PDF y de ficheros devuelven una respuesta binaria que conviertes en un Buffer / stream, no JSON.

Paginación con el SDK

Los métodos de listado devuelven un Page, que es en sí mismo un async iterable:

const page = await factuarea.invoices.list({ status: "paid", limit: 50 });

// (a) iterate every item across every page
for await (const invoice of page) {
  console.log(invoice.id);
}

// (b) page by page
page.data;                              // items on this page
page.hasMore;                           // boolean
page.nextCursor;                        // opaque cursor or null
const next = await page.getNextPage();  // Page | null

// (c) collect everything into an array
const all = await page.toArray();

El helper PageIterator transmite cada elemento a través de todas las páginas sin gestión manual del cursor:

use Factuarea\Sdk\Custom\Pagination\PageIterator;
use Factuarea\Sdk\Models\Operations\PublicApiV1InvoicesListRequest;

$pages = new PageIterator(
    fn (?string $cursor) => $factuarea->invoices->publicApiV1InvoicesList(
        new PublicApiV1InvoicesListRequest(startingAfter: $cursor),
    )->rawResponse,
);

// items() yields each item as a decoded associative array.
foreach ($pages->items() as $invoice) {
    echo $invoice['id'], PHP_EOL;
}

Consulta Pagination para conocer la semántica de cursor subyacente.

Gestión de errores

import {
  FactuareaError,
  ValidationError,
  RateLimitError,
} from "@factuarea/sdk";

try {
  await factuarea.invoices.create(body);
} catch (error) {
  if (error instanceof ValidationError) {
    console.error(error.fields);     // { tax_id: ["NIF inválido"], … }
  } else if (error instanceof RateLimitError) {
    console.error(error.retryAfter); // seconds to wait
  } else if (error instanceof FactuareaError) {
    console.error(error.code, error.requestId);
  }
}
use Factuarea\Sdk\Models\Errors\ErrorThrowable;

try {
    $factuarea->invoices->publicApiV1InvoicesCreate($body);
} catch (ErrorThrowable $e) {
    $error = $e->container->error;
    echo $error->type->value;  // e.g. "invalid_request_error"
    echo $error->code;         // e.g. "parameter_invalid"
    echo $error->param;        // e.g. "client_id"
    echo $error->requestId;    // quote this to support
}

Ramifica según el code estable, nunca según el message en español orientado a personas. El catálogo completo está en Errors.

Verificación de webhooks

Pasa el cuerpo crudo del request (no un objeto re‑serializado), el header Factuarea-Signature y el secreto del endpoint:

import { Factuarea, WebhookSignatureError, SIGNATURE_HEADER } from "@factuarea/sdk";

const factuarea = new Factuarea({ apiKey: process.env.FACTUAREA_API_KEY! });

// Express, with express.raw({ type: "application/json" }) on the route:
app.post("/webhooks/factuarea", (req, res) => {
  try {
    const event = factuarea.webhooks.verify(
      req.body.toString("utf8"),
      req.headers[SIGNATURE_HEADER.toLowerCase()] as string,
      process.env.FACTUAREA_WEBHOOK_SECRET!,
    );
    if (event.type === "invoice.paid") { /* … */ }
    res.sendStatus(200);
  } catch (e) {
    if (e instanceof WebhookSignatureError) return res.sendStatus(400);
    throw e;
  }
});
use Factuarea\Sdk\Custom\Webhooks\WebhookVerifier;
use Factuarea\Sdk\Custom\Webhooks\WebhookSignatureException;

$verifier = new WebhookVerifier();
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_FACTUAREA_SIGNATURE'] ?? '';

try {
    $event = $verifier->verify($rawBody, $signature, getenv('FACTUAREA_WEBHOOK_SECRET'));
    // $event is the decoded, authenticated payload
} catch (WebhookSignatureException $e) {
    http_response_code(400);
}

La verificación usa HMAC‑SHA256 con una comparación de tiempo constante y una tolerancia de timestamp configurable (5 minutos por defecto) para rechazar replays, y acepta ambas firmas durante una ventana de gracia de rotación de secreto. Consulta Webhooks.

Snippets por endpoint

Cada página de la referencia de la API ofrece ejemplos HTTP en TypeScript, PHP y cURL generados desde el contrato OpenAPI documentado. Cubren la petición, sus cabeceras necesarias y el formato de respuesta sin depender de la cobertura de una versión del SDK.

Genera tu propio cliente

Si tu lenguaje todavía no está cubierto, o prefieres un cliente que tú controles y guardes en tu repo, el contrato canónico legible por máquina es el spec OpenAPI 3.1 — apunta cualquier generador a él.

Consulta el contrato OpenAPI documentado, exportado del backend y versionado con este portal.

npx openapi-typescript https://docs.factuarea.com/api/openapi \
  -o src/factuarea.d.ts
openapi-python-client generate \
  --url https://docs.factuarea.com/api/openapi
openapi-generator-cli generate \
  -i https://docs.factuarea.com/api/openapi \
  -g <language> -o ./factuarea-client

<language> puede ser cualquier generador soportado — Go, Java, C#, Ruby, Rust y más.

Un cliente generado no incluirá el runtime del SDK oficial (reintentos, idempotencia, paginación, verificación de webhooks) — eso lo conectas tú mismo siguiendo las guías de conceptos clave.

¿Construyes con un asistente de IA?

Si quieres que un agente de IA opere Factuarea directamente en lugar de generar código de cliente, conéctalo al servidor MCP — la API pública expuesta como tools, con autenticación OAuth y por API key. Para Claude Code, el plugin oficial factuarea-mcp lo configura en dos comandos.

Cobertura de las versiones

La referencia de septiembre de 2026 es 0.2.0 para ambos SDK. Los specs de sus ramas principales contienen 413 entradas y todavía les faltan 39 endpoints REST. Es necesario actualizar el paquete para disponer de nuevos métodos generados. La referencia de la API ofrece ejemplos HTTP completos con fetch de TypeScript, cURL de PHP y terminal para el contrato documentado; estos ejemplos no presuponen que exista un método del SDK.

En esta página

¿Te echamos una mano?Contactar con soporte