Factuarea APIDevelopers

Authentication

API keys with fact_live_ / fact_test_ prefixes, fine-grained scopes, grace-period rotation and IP allowlist.

The Factuarea API authenticates every request with an API key. Keys are opaque tokens generated in the developer dashboard (app.factuarea.com/settings/developers/api-keys) and bound to a specific company. Every request to https://api.factuarea.com/v1/* must include a valid key in one of the two supported formats.

API key format

fact_live_<24 alphanumeric characters>
fact_test_<24 alphanumeric characters>

Example:

fact_live_8KqW3pXnR2VbY7TcA9eFmN5z
fact_test_3pXnR2VbY7TcA9eFmN5z8KqW
  • Prefix: determines the environment. fact_live_ operates on your real company (production); fact_test_ operates on an isolated sandbox company with external effects (VeriFactu → AEAT, FACe, emails, webhooks) switched off. The prefix lets you identify the environment without decoding the key. See Test mode & sandbox.
  • Secret: 24 base62 characters → ~143 bits of entropy. Shown only once at creation in the dashboard. If you lose it, you must rotate.
  • DB hash: the backend stores only the bcrypt cost-12 hash of the secret. There's no way to recover it.

Every example in this guide uses a fact_live_ key, but the exact same request works with a fact_test_ key — just swap the prefix to operate on sandbox data. Build and validate your integration in test first. See Test mode & sandbox.

Sending the key on each request

The API accepts two equivalent formats. Pick the one that fits your client:

curl https://api.factuarea.com/v1/contacts \
  -H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z"

X-API-Key header

curl https://api.factuarea.com/v1/contacts \
  -H "X-API-Key: fact_live_8KqW3pXnR2VbY7TcA9eFmN5z"

Send only one of the two headers. If both are present, the Authorization: Bearer header takes precedence.

Examples per language

$client = new GuzzleHttp\Client([
    'base_uri' => 'https://api.factuarea.com/v1/',
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('FACTUAREA_API_KEY'),
        'Accept' => 'application/json',
    ],
]);

$response = $client->get('contacts?limit=10');
$body = json_decode((string) $response->getBody(), true);
const res = await fetch('https://api.factuarea.com/v1/contacts?limit=10', {
  headers: {
    Authorization: `Bearer ${process.env.FACTUAREA_API_KEY}`,
    Accept: 'application/json',
  },
});
const data = await res.json();
import os
import requests

resp = requests.get(
    'https://api.factuarea.com/v1/contacts',
    params={'limit': 10},
    headers={
        'Authorization': f"Bearer {os.environ['FACTUAREA_API_KEY']}",
        'Accept': 'application/json',
    },
)
resp.raise_for_status()
data = resp.json()

OAuth 2.1

For agent integrations and third-party apps that act on behalf of a Factuarea user, the API also supports the OAuth 2.1 authorization-code flow with PKCE (code_challenge_method=S256) as an alternative to a static API key. OAuth consent exposes 68 simple dotted scopes plus the factuarea.read, factuarea.write and factuarea.full macros; they are mapped to the fine-grained colon scopes that gate each endpoint and MCP tool. The rotation policy applies to OAuth client secrets too.

The consent catalog includes price_lists.read/write, facturae.read/write and tax_reports.read/write. The screen offers only the scopes requested by the client, never pre-selects sensitive scopes, and the issued token contains exactly the subset the user approved.

Discovery metadata (RFC 8414) is published at /.well-known/oauth-authorization-server, so OAuth clients can resolve the authorization and token endpoints automatically:

curl https://api.factuarea.com/.well-known/oauth-authorization-server

The OAuth2 security scheme — including the authorization and token URLs and the full scope list — is described in the API Reference.

Scopes

Each API key is created with one or more scopes that limit which endpoints it can invoke. Scopes are strings of the form <resource>:<action>. The catalog is closed: any scope outside the listed set raises invalid_scope when creating the key.

The valid catalog is append-only for existing credentials, while the subset assignable to a new key follows the currently released API surface. The four reserved GoCardless/MONEI scopes below remain recognizable for historical compatibility, but are omitted from CreateApiKeyV1Request and cannot be assigned until those providers are released. Plan and sandbox restrictions are then enforced separately with scope_not_allowed_by_plan and scope_not_allowed_in_sandbox.

Clients and catalog

ScopeAllows
contacts:readList and retrieve contacts.
contacts:writeCreate and update contacts, roles and profiles.
contacts:deleteArchive contacts and remove unreferenced roles.
products:readList and retrieve products.
products:writeCreate and update products.
products:deleteDelete products.
price_lists:readList price lists, their items and resolve catalog prices.
price_lists:writeCreate, update and delete price lists and their items.

Sales documents

ScopeAllows
invoices:readList and retrieve invoices.
invoices:writeCreate and update invoices (includes duplicate and corrective).
invoices:deleteDelete invoice drafts.
invoices:sendSend invoice by email to the client.
invoices:voidVoid an issued invoice.
quotes:readList and retrieve quotes.
quotes:writeCreate and update quotes.
quotes:deleteDelete quotes.
quotes:sendSend quote by email.
quotes:transitionAccept, reject or convert quotes.
proformas:readList and retrieve pro-forma invoices.
proformas:writeCreate and update pro-forma invoices.
proformas:deleteDelete pro-forma invoices.
proformas:sendSend pro-forma invoice by email.
proformas:transitionConvert pro-forma invoice to invoice.
delivery_notes:readList and retrieve delivery notes.
delivery_notes:writeCreate and update delivery notes.
delivery_notes:deleteDelete delivery notes.
delivery_notes:transitionMark as delivered/cancelled, sign, convert.
delivery_notes:gdpr_forgetErase signature-audit PII (GDPR Art. 17).

Purchases and recurring

ScopeAllows
purchase_invoices:readList and retrieve vendor bills.
purchase_invoices:writeCreate and update vendor bills.
purchase_invoices:deleteDelete vendor bills.
purchase_invoices:transitionMark as paid, received, accounted.
recurring_invoices:readList and retrieve recurring templates.
recurring_invoices:writeCreate and update recurring templates.
recurring_invoices:deleteDelete recurring templates.
recurring_invoices:transitionPause, resume and emit manually.

Catalogs and export

ScopeAllows
taxes:readRead the (global) tax rates catalog.
taxes:writeCreate and update tax rates.
taxes:deleteDelete tax rates.
series:readList invoice numbering series.
series:writeCreate and update invoice numbering series.
pdfs:readDownload PDFs of any document with the matching :read scope.
tax_reports:readRead tax reports (Modelo 303/347, etc.).
tax_reports:writeGenerate tax reports.
account:readRead the authenticated account (GET /v1/account).
account:writeManage the account's own API keys (create, rotate, revoke) and update account personalization.

VeriFactu & FacturaE

ScopeAllows
verifactu:readRead VeriFactu records, events, certificates and config.
verifactu:writeManage VeriFactu certificates, settings and retries.
facturae:readDownload the FacturaE XML of an invoice and read its FACe submissions.
facturae:writeSubmit invoices to FACe and request submission cancellations.

Webhooks and events

ScopeAllows
webhooks:readList webhook endpoints and deliveries.
webhooks:writeCreate, update, rotate and ping webhook endpoints.
webhooks:deleteDelete webhook endpoints.
events:readRead the event catalog and individual events.

Integrations and observability

ScopeAllows
stripe_autoinvoicing:readRead Stripe auto-invoicing status, configuration and connected accounts.
stripe_autoinvoicing:writeConfigure Stripe auto-invoicing and connected accounts.
payouts:readRead Stripe payouts and reconciliation status.
integration_events:readInspect received payment-gateway events and typed discard reasons.
integration_events:writeReplay parked integration events.
developers:readInspect the authenticated company's API request log.
emails:readInspect sent-email status and history.
gocardless_autoinvoicing:readReserved for the unreleased GoCardless integration; no endpoint or tool currently requires it.
gocardless_autoinvoicing:writeReserved for the unreleased GoCardless integration; no endpoint or tool currently requires it.
monei_autoinvoicing:readReserved for the unreleased MONEI integration; no endpoint or tool currently requires it.
monei_autoinvoicing:writeReserved for the unreleased MONEI integration; no endpoint or tool currently requires it.

Workforce (control horario)

These scopes require the control_horario plan module. Some read scopes are OAuth-grantable; privileged writes and transitions remain API-key-only.

ScopeAllows
employees:readRead employees and invitation state.
employees:writeCreate, update and manage employees and employee seats.
employees:deletePermanently delete employees.
time_entries:readRead time entries, balances and monthly registers.
time_entries:writeClock, correct and close/reopen time registers.
absences:readRead absence types, policies, requests and balances.
absences:writeCreate and update absence configuration and requests.
absences:transitionApprove, reject and cancel absence requests.
work_schedules:readRead schedules and assignments.
work_schedules:writeCreate, update, assign and archive schedules.
presence:readRead live and daily presence.
holidays:readRead company and regional holidays.
payroll_exports:readRead generated payroll exports.
payroll_exports:writeGenerate payroll exports.

Managed companies (gestoría)

Fine-grained scopes for the gestoría model, where a master account manages child sub-companies and their API keys. Reachable only with an API key (no OAuth consent equivalent); companies:* also requires the gestoría plan module.

ScopeAllows
companies:readList and retrieve managed companies (child sub-accounts).
companies:writeCreate, update, activate and deactivate managed companies.
companies:deleteArchive managed companies.
api_keys:readList and retrieve API keys of managed companies.
api_keys:writeCreate, rotate and revoke API keys of managed companies.
api_keys:deletePermanently delete API keys of managed companies.

Super-scope

ScopeAllows
*Full access — equivalent to having every other scope above. Reserved for owner keys / one-off migrations. Avoid using in production integrations.

If a request uses an endpoint that requires a scope not granted to the key, the response is 403 with type: authorization_error and code: insufficient_scope.

{
  "error": {
    "type": "authorization_error",
    "code": "insufficient_scope",
    "message": "La API key no tiene el scope requerido para esta operación.",
    "request_id": "req_01JBVH7..."
  }
}

Key management

API keys can be managed from the developer dashboard (app.factuarea.com/settings/developers/api-keys) or through the self-service v1 endpoints. Both surfaces let you create, rotate and revoke keys; the dashboard also configures the IP allowlist.

The authenticated key's metadata (id, name, prefix, scopes, tier, last_used_at, expires_at) is readable via GET /v1/account — but the secret is never returned.

There is no endpoint to "view" the secret. It is shown only once at creation. If you lose the value you must rotate the key in the dashboard and redeploy the new secret. This is deliberate: it minimizes the exposure window.

Rotation policy

API keys and OAuth client secrets are long-lived credentials and must be rotated on a schedule and immediately after any suspected leak.

  • Prefixes are the source of truth for the environment: fact_live_ (production) and fact_test_ (sandbox). Never mix them across environments.
  • Rotate from the dashboard (or via the self-service account:write endpoints) to issue a brand-new secret. The new secret is returned once — store it immediately, it is never shown again.
  • Grace window (dual-secret). After a rotation the previous secret keeps working for a 24-hour grace window, so you can roll out the new secret with zero downtime. During that window both the new and the previous secret are accepted; once the window expires the previous secret is rejected and purged. A request still using the previous secret receives a 199 Warning header telling you how many hours remain before it stops working.
  • When to rotate: on a regular schedule (e.g. every 90 days), whenever a teammate with access leaves, and immediately if a secret is ever exposed in logs, source control or a public client.
  • Revoke to invalidate a key permanently. Any subsequent request with it fails with 401. Revocation has no grace window — it is instant and irreversible.

Secrets are bound to a single company (tenant) and must never be embedded in browsers, mobile apps or any public client — keep them server-side only.

IP allowlist

Each API key can be restricted to a list of IPs or CIDR ranges from the dashboard. If the request comes from an IP outside the allowlist, the response is 401 and the incident is recorded in the audit log. Leave the allowlist empty to allow any IP.

Authentication errors

Failures related to the API key respond with HTTP 401 (or 403 for insufficient_scope) and the standard error envelope. The code field distinguishes the case:

codeHTTPCause
missing_api_key401No authentication header sent.
invalid_api_key401The key does not exist, has the wrong format, is revoked/expired, or the secret doesn't match the stored hash.
too_many_auth_failures429Too many failed authentication attempts; back off.
insufficient_scope403The key lacks the scope the endpoint requires.

Every response includes a unique request_id (also in the X-Request-Id header) you can pass to support when investigating.

{
  "error": {
    "type": "authentication_error",
    "code": "invalid_api_key",
    "message": "La API key proporcionada no es válida.",
    "request_id": "req_01JBVH7K9Y4N3CDQ2EHJB1AGSV",
    "doc_url": "https://docs.factuarea.com/guides/errors#invalid_api_key"
  }
}

Best practices

  • Never commit API keys to repositories — use environment variables or a secret manager (AWS Secrets Manager, Doppler, 1Password Service Accounts).
  • Create one key per integration: makes rotating and auditing access easier without affecting the rest.
  • Limit scopes to the minimum required. An export script only needs specific :read scopes.
  • Enable IP allowlist for server-to-server integrations with stable IPs.
  • Configure expires_at for temporary keys (e.g. consultancies, demos).
  • Audit usage from the dashboard: Developers > API Keys > Activity shows IPs, paths and errors per key.

Credentials for integrations

Store REST and MCP operations require an API key with the store scopes. OAuth consent does not grant these scopes. Authorize the provider connection separately: a Factuarea key is not a WooCommerce or Shopify credential. MCP authentication also requires a client compatible with revision 2026-07-28.

On this page

Need a hand?Contact support