# Factuarea API — full documentation
> Concatenated Markdown export of every page at https://docs.factuarea.com. Generated on demand from the source MDX (no UI components, no layout chrome). Each page begins with its title and canonical URL so LLMs can cite back to the live documentation. The API reference is rendered from the OpenAPI spec and, being identical in every language, appears once per operation. See `/llms.txt` for a curated index.
Total pages: 1565.
---
# Factuarea API (/)
The Factuarea REST API exposes invoicing resources (clients, products,
invoices, quotes, pro-forma invoices, delivery notes, recurring invoices,
purchase invoices) over HTTPS with **API key** authentication. The entire
public surface lives at
[`https://api.factuarea.com/v1`](https://api.factuarea.com/v1) and returns
JSON. Every resource is identified by an opaque `id` (a UUID v7 string).
One copy-paste sequence against a `fact_test_` key: verify your key,
grab a series and a tax, create a client, issue an invoice and send it.
## Quick start [#quick-start]
**The API comes with your plan**
The public API is **included in every Factuarea plan** — no beta
program, no separate add-on. During the 10-day trial you already get
API access on the `free` tier; paid plans raise the rate-limit tier.
See [Rate limits](/guides/rate-limits).
**Create your first API key**
Open
[Dashboard → Developers → API Keys](https://app.factuarea.com/settings/developers/api-keys)
and create a key with the scopes you need (for example
`invoices:read,clients:read` to start). Copy the secret **only
once** — you won't be able to see it again.
Pick the **Test** environment to get a `fact_test_` key that operates
on an isolated sandbox with no real-world effects. Build against it
first, then create a `fact_live_` key to go to production. See
[Test mode & sandbox](/guides/test-mode).
**Verify your key**
Before anything else, confirm the key works. `GET /v1/account`
introspects the credential — it returns the company it belongs to, the
plan, and the **scopes** and rate-limit **tier** of the key itself
(needs `account:read`):
```bash
curl https://api.factuarea.com/v1/account \
-H "Authorization: Bearer fact_test_xxxxxxxxxxxxxxxxxxxxxxxx"
```
✅ You should see a `200` with an `account` snapshot:
```json
{
"data": {
"object": "account",
"company": {
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01",
"name": "Acme Soluciones SL",
"tax_id": "B12345678"
},
"plan": { "slug": "empresario", "name": "Empresario" },
"api_key": {
"id": "01931b3e-7c4a-7f2e-9a8b-4d6e7f8a9b0c",
"name": "Sandbox integration",
"prefix": "fact_test_3pXnR2Vb",
"scopes": ["account:read", "clients:read", "invoices:read"],
"tier": "starter"
}
}
}
```
If you get `401 invalid_api_key`, re-check the value. The `scopes`
array tells you exactly what this key can do — a later call that fails
with `403 insufficient_scope` is missing one of them.
**Make your first data request**
Now list a real resource. `GET /v1/clients` returns a standard envelope
with `data` (results), `has_more` and `next_cursor`
([cursor pagination](/guides/pagination)):
```bash
curl https://api.factuarea.com/v1/clients \
-H "Authorization: Bearer fact_test_xxxxxxxxxxxxxxxxxxxxxxxx"
```
Ready to issue your first invoice end-to-end? Follow the
[Quickstart](/guides/quickstart). If you receive an error, look it up in
[Errors](/guides/errors) by the returned `code`.
**Configure webhooks (optional)**
If your integration needs to react to events (invoice paid, quote
accepted, etc.), configure a webhook endpoint signed with
HMAC SHA256. See [Webhooks](/guides/webhooks).
## What the API covers [#what-the-api-covers]
Full CRUD, search by tax ID, VIES validation.
Products with prices, stock, SKU and tax rates.
Invoices, quotes, pro-forma invoices, delivery notes, recurring
invoices — with lines, retentions and equivalence surcharge.
Send by email, mark as paid/accepted, generate PDF, void, create
corrective invoice, convert between types.
Vendor bills with PDF upload, mark\_paid, mark\_received.
Legal numbering series per document type (read-only via API to
guarantee tax continuity).
FacturaE 3.2.2 XML download and FACe submissions — submit, track the
processing status and request cancellations.
Employees, work schedules, the time-clock ledger, monthly closes, absences,
presence and public holidays — the RD-ley 8/2019 working-time register.
The whole API as Model Context Protocol tools, with OAuth 2.1 and
API-key auth — connect Claude and other agents in seconds.
## Contract design [#contract-design]
The API follows the patterns you would expect from a modern provider:
* **Opaque identifiers** — the `id` key carries a UUID v7 string instead
of an incremental integer. See [Pagination](/guides/pagination) for
cursor semantics.
* **Normalized errors** — every error returns an envelope with `type`,
`code`, `message`, `param`, `doc_url` and `request_id`. See
[Errors](/guides/errors).
* **Idempotency keys** — supported on every `POST` to prevent duplicates
on retries. See [Idempotency](/guides/idempotency).
* **Rate limits per tier** — per-minute and monthly quotas, with
`X-RateLimit-*` headers on every response. See
[Rate limits](/guides/rate-limits).
* **URL versioning** — `/v1/*`. Breaking changes trigger `/v2/*` with a
documented deprecation policy. See [Versioning](/guides/versioning).
* **Webhooks with dual-secret rotation** — HMAC SHA256, exponential retry
with up to 8 attempts. See [Webhooks](/guides/webhooks).
## SDKs [#sdks]
We ship official [TypeScript and PHP SDKs](/sdks) (`@factuarea/sdk` and
`factuarea/factuarea-php`) with retries, idempotency, cursor pagination, typed
errors and webhook verification built in. If your language isn't covered, any
standard HTTP client (curl, Postman, axios, requests, Guzzle) works — the API is
plain REST over JSON.
The public REST API complements the Factuarea web client
([`app.factuarea.com`](https://app.factuarea.com)) — it doesn't replace
it. Operations not exposed by the API (plan management, branding,
global company tax configuration) still live in the app.
---
# GET /v1/absence-balances — List all absence balances
- **Operation ID**: `public-api.v1.absence-balances.list`
- **Tag**: Absence Balances
- **Required scope**: `absences:read` — Read absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-balances/public-api.v1.absence-balances.list
List your company’s absence balances with cursor-based pagination. Each balance is the accrued, carried-over and consumed days of one employee for one absence type in a given year, with the resulting `available_days`. Supports filtering by `employee_id` (UUID v7), `absence_type_id` (UUID v7) and `year`. Day amounts are exact decimal strings.
## Query parameters
- `employee_id` (string | null, optional, format: uuid) — Employee ID (UUID v7) to filter balances by.
- `absence_type_id` (string | null, optional, format: uuid) — Absence type ID (UUID v7) to filter balances by.
- `year` (integer | null, optional, min 2000, max 2100) — Accrual year to filter balances by.
- `limit` (integer, optional, min 1, max 100, default: `25`) — Number of objects to return.
- `starting_after` (string, optional, format: uuid) — Cursor for forward pagination.
- `ending_before` (string, optional, format: uuid) — Cursor for backward pagination.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (array, required)
- `id` (string, required, format: uuid) — Opaque identifier of the balance, a UUID v7.
- `object` (string, required, enum: `absence_balance`) — Always `absence_balance`.
- `employee_id` (string, required, format: uuid) — UUID v7 of the employee the balance belongs to.
- `absence_type_id` (string, required, format: uuid) — UUID v7 of the absence type the balance tracks.
- `absence_policy_id` (string, required, format: uuid) — UUID v7 of the absence policy the balance accrues under.
- `year` (integer, required) — Accrual year the balance belongs to (e.g. `2026`).
- `accrued_days` (string, required) — Days accrued so far this year, as an exact decimal string.
- `carried_over_days` (string, required) — Days carried over from the previous year (capped by the policy), as an exact decimal string.
- `carryover_expires_on` (string | null, required, format: date) — Date on which the carried-over days expire (`YYYY-MM-DD`), or `null` when they do not expire or there is no carryover.
- `consumed_days` (string, required) — Days already consumed against this balance, as an exact decimal string.
- `available_days` (string, required) — Usable balance (accrued + non-expired carried-over − consumed), as an exact decimal string.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit in which the balance is measured: `days` or `hours`.
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- `has_more` (boolean, required) — Whether more pages are available after this one.
- `next_cursor` (string | null, required) — Opaque cursor for the next page, or `null` when `has_more` is `false`. The example below shows the shape used by most listings; others return a different one, so pass the value back verbatim instead of validating it.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/absence-balances/{absence_balance} — Retrieve an absence balance
- **Operation ID**: `public-api.v1.absence-balances.show`
- **Tag**: Absence Balances
- **Required scope**: `absences:read` — Read absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-balances/public-api.v1.absence-balances.show
Retrieve a single absence balance by its `id` (UUID v7), including its accrued, carried-over, consumed and available days for the employee, absence type and year. A balance belonging to another company returns 404 `absence_balance_not_found` (anti-enumeration).
## Path parameters
- `absence_balance` (string, required)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsenceBalance), required) — An absence balance for the Control Horario (time tracking) module: the accrued, carried-over, consumed and available days of one employee for one absence type in a given year. Day amounts are exact decimal strings. `available_days` is the usable balance (accrued + non-expired carried-over − consumed).
- `id` (string, required, format: uuid) — Opaque identifier of the balance, a UUID v7.
- `object` (string, required, enum: `absence_balance`) — Always `absence_balance`.
- `employee_id` (string, required, format: uuid) — UUID v7 of the employee the balance belongs to.
- `absence_type_id` (string, required, format: uuid) — UUID v7 of the absence type the balance tracks.
- `absence_policy_id` (string, required, format: uuid) — UUID v7 of the absence policy the balance accrues under.
- `year` (integer, required) — Accrual year the balance belongs to (e.g. `2026`).
- `accrued_days` (string, required) — Days accrued so far this year, as an exact decimal string.
- `carried_over_days` (string, required) — Days carried over from the previous year (capped by the policy), as an exact decimal string.
- `carryover_expires_on` (string | null, required, format: date) — Date on which the carried-over days expire (`YYYY-MM-DD`), or `null` when they do not expire or there is no carryover.
- `consumed_days` (string, required) — Days already consumed against this balance, as an exact decimal string.
- `available_days` (string, required) — Usable balance (accrued + non-expired carried-over − consumed), as an exact decimal string.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit in which the balance is measured: `days` or `hours`.
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/absence-calendar — Get the team absence calendar
- **Operation ID**: `public-api.v1.absence-calendar.show`
- **Tag**: Absence Calendar
- **Required scope**: `absences:read` — Read absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-calendar/public-api.v1.absence-calendar.show
Return the monthly absence calendar of your team for a given `year` and `month`: every active employee with their approved absences of that month (each coloured by its absence type) and the public holidays that apply, kept separate from the absences. Optionally scoped to a single `employee_id` (UUID v7). A computed resource: it exposes `employee_id` per member, never an `id`.
## Query parameters
- `year` (integer, required, min 2000, max 2100) — Calendar year (4 digits).
- `month` (integer, required, min 1, max 12) — Calendar month (1-12).
- `employee_id` (string | null, optional, format: uuid) — Employee ID (UUID v7) to limit the calendar to a single employee (optional).
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (TeamAbsenceCalendar), required) — The monthly team absence calendar for the Control Horario (time tracking) module: for a given year and month, every active employee with their approved absences of that month (each coloured by its absence type) plus the public holidays that apply, kept separate from the absences. A computed resource with no entity identity: it is keyed by company + year/month and never exposes an `id`.
- `year` (integer, required) — Year of the calendar.
- `month` (integer, required) — Month of the calendar (1-12).
- `members` (array, required) — Active employees of the team, each with their approved absences overlapping the month.
- `holidays` (array, required) — Public holidays that apply to the team during the month.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/absence-policies/{absence_policy}/archive — Archive an absence policy
- **Operation ID**: `public-api.v1.absence-policies.archive`
- **Tag**: Absence Policies
- **Required scope**: `absences:write` — Create and update absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-policies/public-api.v1.absence-policies.archive
Archive an absence policy (transition `active` → `archived`), retiring it from use while preserving it. No request body. Returns 422 if it is already archived. Reversible via unarchive.
## Path parameters
- `absence_policy` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsencePolicy), required) — An absence policy for the Control Horario (time tracking) module: how many days per year an employee accrues for a set of absence types, how those days accrue, and which employees it applies to. `allowance_type` decides whether the yearly allowance is capped (`limited`, with `allowance_days`) or `unlimited`.
- `id` (string, required, format: uuid) — Opaque identifier of the policy, a UUID v7.
- `object` (string, required, enum: `absence_policy`) — Always `absence_policy`.
- `name` (string, required) — Human-readable name of the policy (e.g. `Vacaciones estándar`).
- `allowance_type` (string, required, enum: `limited`, `unlimited`) — Whether the yearly allowance is capped (`limited`) or `unlimited`.
- `allowance_days` (integer | null, required) — Days accrued per year when `allowance_type` is `limited`; `null` when `unlimited`.
- `accrual_method` (string, required, enum: `annual`, `monthly`) — How the allowance accrues: `annual` (all at once) or `monthly` (prorated).
- `absence_type_ids` (array, required) — UUID v7 of each absence type this policy covers.
- `assigned_employee_count` (integer, required) — Number of employees currently assigned to the policy.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `carryover` (object, required) — How much unused balance carries over at year-end under this policy.
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/absence-policies/{absence_policy}/assign — Assign a policy to employees
- **Operation ID**: `public-api.v1.absence-policies.assign`
- **Tag**: Absence Policies
- **Required scope**: `absences:write` — Create and update absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-policies/public-api.v1.absence-policies.assign
Assign the absence policy to one or more employees. `employee_ids` (a non-empty list of UUID v7, each belonging to your company) is required; an unknown employee returns 422. Returns the policy with its updated assigned-employee count.
## Path parameters
- `absence_policy` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 1 property; 1 required: `employee_ids`.
- `employee_ids` (array, required) — Employee IDs (UUID v7) the policy is assigned to.
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsencePolicy), required) — An absence policy for the Control Horario (time tracking) module: how many days per year an employee accrues for a set of absence types, how those days accrue, and which employees it applies to. `allowance_type` decides whether the yearly allowance is capped (`limited`, with `allowance_days`) or `unlimited`.
- `id` (string, required, format: uuid) — Opaque identifier of the policy, a UUID v7.
- `object` (string, required, enum: `absence_policy`) — Always `absence_policy`.
- `name` (string, required) — Human-readable name of the policy (e.g. `Vacaciones estándar`).
- `allowance_type` (string, required, enum: `limited`, `unlimited`) — Whether the yearly allowance is capped (`limited`) or `unlimited`.
- `allowance_days` (integer | null, required) — Days accrued per year when `allowance_type` is `limited`; `null` when `unlimited`.
- `accrual_method` (string, required, enum: `annual`, `monthly`) — How the allowance accrues: `annual` (all at once) or `monthly` (prorated).
- `absence_type_ids` (array, required) — UUID v7 of each absence type this policy covers.
- `assigned_employee_count` (integer, required) — Number of employees currently assigned to the policy.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `carryover` (object, required) — How much unused balance carries over at year-end under this policy.
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/absence-policies/{absence_policy}/assignments — List a policy’s assigned employees
- **Operation ID**: `public-api.v1.absence-policies.assignments`
- **Tag**: Absence Policies
- **Required scope**: `absences:read` — Read absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-policies/public-api.v1.absence-policies.assignments
List the employees assigned to this absence policy (their `employee_id` UUID v7 and name), as a flat list under `{ "data": [ … ] }`.
## Path parameters
- `absence_policy` (string, required)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (array, required)
- `object` (string, required, enum: `absence_policy_assignment`) — Always `absence_policy_assignment`.
- `employee_id` (string, required, format: uuid) — UUID v7 of the assigned employee.
- `employee_name` (string, required) — Full name of the assigned employee.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/absence-policies/{absence_policy}/carryover — Configure a policy’s year-end carryover
- **Operation ID**: `public-api.v1.absence-policies.carryover`
- **Tag**: Absence Policies
- **Required scope**: `absences:write` — Create and update absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-policies/public-api.v1.absence-policies.carryover
Configure how much unused balance carries over at year-end for this absence policy. `carryover_type` (`none`/`capped`/`unlimited`) is required; `carryover_max_days` is required and positive only when `carryover_type` is `capped`. Optional `carryover_expiry_month` (1..12) and `carryover_expiry_day` set when the carried-over balance expires. A policy belonging to another company returns 404 `absence_policy_not_found` (anti-enumeration). Returns the updated policy.
## Path parameters
- `absence_policy` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 4 properties; 1 required: `carryover_type`.
- `carryover_type` (string, required, enum: `none`, `capped`, `unlimited`) — Carryover mode: `none` (no carryover), `capped` (with a cap) or `unlimited` (no cap).
- `carryover_max_days` (integer | null, optional, min 1) — Cap of carryover days; required and positive when the mode is `capped`.
- `carryover_expiry_month` (integer | null, optional, min 1, max 12) — Carryover expiry month (1-12); when provided, the day is also required.
- `carryover_expiry_day` (integer | null, optional, min 1, max 31) — Carryover expiry day (1-31); when provided, the month is also required.
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsencePolicy), required) — An absence policy for the Control Horario (time tracking) module: how many days per year an employee accrues for a set of absence types, how those days accrue, and which employees it applies to. `allowance_type` decides whether the yearly allowance is capped (`limited`, with `allowance_days`) or `unlimited`.
- `id` (string, required, format: uuid) — Opaque identifier of the policy, a UUID v7.
- `object` (string, required, enum: `absence_policy`) — Always `absence_policy`.
- `name` (string, required) — Human-readable name of the policy (e.g. `Vacaciones estándar`).
- `allowance_type` (string, required, enum: `limited`, `unlimited`) — Whether the yearly allowance is capped (`limited`) or `unlimited`.
- `allowance_days` (integer | null, required) — Days accrued per year when `allowance_type` is `limited`; `null` when `unlimited`.
- `accrual_method` (string, required, enum: `annual`, `monthly`) — How the allowance accrues: `annual` (all at once) or `monthly` (prorated).
- `absence_type_ids` (array, required) — UUID v7 of each absence type this policy covers.
- `assigned_employee_count` (integer, required) — Number of employees currently assigned to the policy.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `carryover` (object, required) — How much unused balance carries over at year-end under this policy.
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/absence-policies — Create an absence policy
- **Operation ID**: `public-api.v1.absence-policies.create`
- **Tag**: Absence Policies
- **Required scope**: `absences:write` — Create and update absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-policies/public-api.v1.absence-policies.create
Create an absence policy for the authenticated company (resolved from the API key, never from the payload). `name`, `allowance_type` (`limited`/`unlimited`) and `accrual_method` (`annual`/`monthly`) are required; `allowance_days` is required and positive only when `allowance_type` is `limited`. `absence_type_ids` is the list of absence type UUIDs (v7) the policy covers (may be empty); a type belonging to another company returns 422. Returns the created policy with its generated `id` (UUID v7).
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 5 properties; 4 required: `name`, `allowance_type`, `accrual_method`, `absence_type_ids`.
- `name` (string, required, maxLength 120) — Absence policy name.
- `allowance_type` (string, required, enum: `limited`, `unlimited`) — Day allowance type: `limited` or `unlimited`.
- `allowance_days` (integer | null, optional, min 1) — Days allotted per year (required and positive only when the allowance is `limited`).
- `accrual_method` (string, required, enum: `annual`, `monthly`) — Day accrual method: `annual` or `monthly`.
- `absence_type_ids` (array, required) — Absence type IDs (UUID v7) associated with the policy (may be empty).
## Responses
- **201**
- Body (`application/json`):
- `data` (object (AbsencePolicy), required) — An absence policy for the Control Horario (time tracking) module: how many days per year an employee accrues for a set of absence types, how those days accrue, and which employees it applies to. `allowance_type` decides whether the yearly allowance is capped (`limited`, with `allowance_days`) or `unlimited`.
- `id` (string, required, format: uuid) — Opaque identifier of the policy, a UUID v7.
- `object` (string, required, enum: `absence_policy`) — Always `absence_policy`.
- `name` (string, required) — Human-readable name of the policy (e.g. `Vacaciones estándar`).
- `allowance_type` (string, required, enum: `limited`, `unlimited`) — Whether the yearly allowance is capped (`limited`) or `unlimited`.
- `allowance_days` (integer | null, required) — Days accrued per year when `allowance_type` is `limited`; `null` when `unlimited`.
- `accrual_method` (string, required, enum: `annual`, `monthly`) — How the allowance accrues: `annual` (all at once) or `monthly` (prorated).
- `absence_type_ids` (array, required) — UUID v7 of each absence type this policy covers.
- `assigned_employee_count` (integer, required) — Number of employees currently assigned to the policy.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `carryover` (object, required) — How much unused balance carries over at year-end under this policy.
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/absence-policies — List all absence policies
- **Operation ID**: `public-api.v1.absence-policies.list`
- **Tag**: Absence Policies
- **Required scope**: `absences:read` — Read absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-policies/public-api.v1.absence-policies.list
List your company’s absence policies with cursor-based pagination. Supports filtering by `status` (`active`/`archived`) and `accrual_method` (`annual`/`monthly`), plus free-text `search` over the policy name.
## Query parameters
- `limit` (integer, optional, min 1, max 100, default: `25`) — Number of objects to return.
- `starting_after` (string, optional, format: uuid) — Cursor for forward pagination.
- `ending_before` (string, optional, format: uuid) — Cursor for backward pagination.
- `status` (string, optional, enum: `active`, `archived`) — Lifecycle status of the absence policy.
- `status[in]` (string, optional) — Lifecycle status of the absence policy.
- `accrual_method` (string, optional, enum: `annual`, `monthly`) — Accrual method used to grant the absence balance.
- `accrual_method[in]` (string, optional) — Accrual method used to grant the absence balance.
- `search` (string, optional, maxLength 80) — Free-text search.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (array, required)
- `id` (string, required, format: uuid) — Opaque identifier of the policy, a UUID v7.
- `object` (string, required, enum: `absence_policy`) — Always `absence_policy`.
- `name` (string, required) — Human-readable name of the policy (e.g. `Vacaciones estándar`).
- `allowance_type` (string, required, enum: `limited`, `unlimited`) — Whether the yearly allowance is capped (`limited`) or `unlimited`.
- `allowance_days` (integer | null, required) — Days accrued per year when `allowance_type` is `limited`; `null` when `unlimited`.
- `accrual_method` (string, required, enum: `annual`, `monthly`) — How the allowance accrues: `annual` (all at once) or `monthly` (prorated).
- `absence_type_ids` (array, required) — UUID v7 of each absence type this policy covers.
- `assigned_employee_count` (integer, required) — Number of employees currently assigned to the policy.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `carryover` (object, required) — How much unused balance carries over at year-end under this policy.
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- `has_more` (boolean, required) — Whether more pages are available after this one.
- `next_cursor` (string | null, required) — Opaque cursor for the next page, or `null` when `has_more` is `false`. The example below shows the shape used by most listings; others return a different one, so pass the value back verbatim instead of validating it.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/absence-policies/{absence_policy} — Retrieve an absence policy
- **Operation ID**: `public-api.v1.absence-policies.show`
- **Tag**: Absence Policies
- **Required scope**: `absences:read` — Read absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-policies/public-api.v1.absence-policies.show
Retrieve a single absence policy by its `id` (UUID v7), including the UUIDs of its associated absence types and the count of assigned employees. A policy belonging to another company returns 404 `absence_policy_not_found` (anti-enumeration).
## Path parameters
- `absence_policy` (string, required)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsencePolicy), required) — An absence policy for the Control Horario (time tracking) module: how many days per year an employee accrues for a set of absence types, how those days accrue, and which employees it applies to. `allowance_type` decides whether the yearly allowance is capped (`limited`, with `allowance_days`) or `unlimited`.
- `id` (string, required, format: uuid) — Opaque identifier of the policy, a UUID v7.
- `object` (string, required, enum: `absence_policy`) — Always `absence_policy`.
- `name` (string, required) — Human-readable name of the policy (e.g. `Vacaciones estándar`).
- `allowance_type` (string, required, enum: `limited`, `unlimited`) — Whether the yearly allowance is capped (`limited`) or `unlimited`.
- `allowance_days` (integer | null, required) — Days accrued per year when `allowance_type` is `limited`; `null` when `unlimited`.
- `accrual_method` (string, required, enum: `annual`, `monthly`) — How the allowance accrues: `annual` (all at once) or `monthly` (prorated).
- `absence_type_ids` (array, required) — UUID v7 of each absence type this policy covers.
- `assigned_employee_count` (integer, required) — Number of employees currently assigned to the policy.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `carryover` (object, required) — How much unused balance carries over at year-end under this policy.
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/absence-policies/{absence_policy}/unarchive — Unarchive an absence policy
- **Operation ID**: `public-api.v1.absence-policies.unarchive`
- **Tag**: Absence Policies
- **Required scope**: `absences:write` — Create and update absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-policies/public-api.v1.absence-policies.unarchive
Unarchive an absence policy (transition `archived` → `active`), returning it to use. No request body. Returns 422 if it is already active.
## Path parameters
- `absence_policy` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsencePolicy), required) — An absence policy for the Control Horario (time tracking) module: how many days per year an employee accrues for a set of absence types, how those days accrue, and which employees it applies to. `allowance_type` decides whether the yearly allowance is capped (`limited`, with `allowance_days`) or `unlimited`.
- `id` (string, required, format: uuid) — Opaque identifier of the policy, a UUID v7.
- `object` (string, required, enum: `absence_policy`) — Always `absence_policy`.
- `name` (string, required) — Human-readable name of the policy (e.g. `Vacaciones estándar`).
- `allowance_type` (string, required, enum: `limited`, `unlimited`) — Whether the yearly allowance is capped (`limited`) or `unlimited`.
- `allowance_days` (integer | null, required) — Days accrued per year when `allowance_type` is `limited`; `null` when `unlimited`.
- `accrual_method` (string, required, enum: `annual`, `monthly`) — How the allowance accrues: `annual` (all at once) or `monthly` (prorated).
- `absence_type_ids` (array, required) — UUID v7 of each absence type this policy covers.
- `assigned_employee_count` (integer, required) — Number of employees currently assigned to the policy.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `carryover` (object, required) — How much unused balance carries over at year-end under this policy.
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/absence-policies/{absence_policy}/unassign — Unassign a policy from employees
- **Operation ID**: `public-api.v1.absence-policies.unassign`
- **Tag**: Absence Policies
- **Required scope**: `absences:write` — Create and update absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-policies/public-api.v1.absence-policies.unassign
Remove the assignment of the absence policy from one or more employees. `employee_ids` (a non-empty list of UUID v7) is required; removing an assignment that does not exist is a no-op. Returns the policy with its updated assigned-employee count.
## Path parameters
- `absence_policy` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 1 property; 1 required: `employee_ids`.
- `employee_ids` (array, required) — Employee IDs (UUID v7) whose policy assignment is removed.
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsencePolicy), required) — An absence policy for the Control Horario (time tracking) module: how many days per year an employee accrues for a set of absence types, how those days accrue, and which employees it applies to. `allowance_type` decides whether the yearly allowance is capped (`limited`, with `allowance_days`) or `unlimited`.
- `id` (string, required, format: uuid) — Opaque identifier of the policy, a UUID v7.
- `object` (string, required, enum: `absence_policy`) — Always `absence_policy`.
- `name` (string, required) — Human-readable name of the policy (e.g. `Vacaciones estándar`).
- `allowance_type` (string, required, enum: `limited`, `unlimited`) — Whether the yearly allowance is capped (`limited`) or `unlimited`.
- `allowance_days` (integer | null, required) — Days accrued per year when `allowance_type` is `limited`; `null` when `unlimited`.
- `accrual_method` (string, required, enum: `annual`, `monthly`) — How the allowance accrues: `annual` (all at once) or `monthly` (prorated).
- `absence_type_ids` (array, required) — UUID v7 of each absence type this policy covers.
- `assigned_employee_count` (integer, required) — Number of employees currently assigned to the policy.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `carryover` (object, required) — How much unused balance carries over at year-end under this policy.
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# PUT /v1/absence-policies/{absence_policy} — Update an absence policy
- **Operation ID**: `public-api.v1.absence-policies.update`
- **Tag**: Absence Policies
- **Required scope**: `absences:write` — Create and update absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-policies/public-api.v1.absence-policies.update
Partially update an absence policy: only the fields present in the payload are changed; omitted fields keep their current value. When `absence_type_ids` is provided it fully replaces the associated types. Returns the updated policy.
## Path parameters
- `absence_policy` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, optional. 5 properties; none of them required.
- `name` (string | null, optional, maxLength 120) — Absence policy name.
- `allowance_type` (string | null, optional, enum: `limited`, `unlimited`) — Day allowance type: `limited` or `unlimited`.
- `allowance_days` (integer | null, optional, min 1) — Days allotted per year (positive; the domain requires a value when the allowance is `limited`).
- `accrual_method` (string | null, optional, enum: `annual`, `monthly`) — Day accrual method: `annual` or `monthly`.
- `absence_type_ids` (array, optional) — Absence type IDs (UUID v7) associated (when provided, replaces the association).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsencePolicy), required) — An absence policy for the Control Horario (time tracking) module: how many days per year an employee accrues for a set of absence types, how those days accrue, and which employees it applies to. `allowance_type` decides whether the yearly allowance is capped (`limited`, with `allowance_days`) or `unlimited`.
- `id` (string, required, format: uuid) — Opaque identifier of the policy, a UUID v7.
- `object` (string, required, enum: `absence_policy`) — Always `absence_policy`.
- `name` (string, required) — Human-readable name of the policy (e.g. `Vacaciones estándar`).
- `allowance_type` (string, required, enum: `limited`, `unlimited`) — Whether the yearly allowance is capped (`limited`) or `unlimited`.
- `allowance_days` (integer | null, required) — Days accrued per year when `allowance_type` is `limited`; `null` when `unlimited`.
- `accrual_method` (string, required, enum: `annual`, `monthly`) — How the allowance accrues: `annual` (all at once) or `monthly` (prorated).
- `absence_type_ids` (array, required) — UUID v7 of each absence type this policy covers.
- `assigned_employee_count` (integer, required) — Number of employees currently assigned to the policy.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `carryover` (object, required) — How much unused balance carries over at year-end under this policy.
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/absence-requests/{absence_request}/approve — Approve an absence request
- **Operation ID**: `public-api.v1.absence-requests.approve`
- **Tag**: Absence Requests
- **Required scope**: `absences:transition` — Change the lifecycle status of absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-requests/public-api.v1.absence-requests.approve
Approve a pending absence request (transition `pending` → `approved`), consuming the employee’s balance. No request body (an optional `note` is accepted). A reviewer cannot approve the request they themselves created (422). Returns the updated request.
## Path parameters
- `absence_request` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, optional. 1 property; none of them required.
- `note` (string | null, optional, maxLength 1000) — Approval note (optional).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsenceRequest), required) — An absence request for the Control Horario (time tracking) module: an employee’s request to be off for a date range under an absence type. `status` walks `pending → approved | rejected | cancelled`. `day_amount` is the requested amount as an exact decimal string, measured in `measurement_unit` (days or hours). Review fields (`reviewer_user_id`, `review_note`, `reviewed_at`) are `null` while the request is still pending.
- `id` (string, required, format: uuid) — Opaque identifier of the absence request, a UUID v7.
- `object` (string, required, enum: `absence_request`) — Always `absence_request`.
- `employee_id` (string, required, format: uuid) — UUID v7 of the employee the request belongs to.
- `absence_type_id` (string, required, format: uuid) — UUID v7 of the requested absence type.
- `absence_type_name` (string, required) — Human-readable name of the absence type (e.g. `Vacaciones`).
- `color` (string, required) — Hex color `#RRGGBB` of the absence type, used to render the request on the calendar.
- `status` (string, required, enum: `pending`, `approved`, `rejected`, `cancelled`) — Lifecycle status of the request: `pending`, `approved`, `rejected` or `cancelled`.
- `start_date` (string, required, format: date) — First day of the absence (`YYYY-MM-DD`).
- `end_date` (string, required, format: date) — Last day of the absence (`YYYY-MM-DD`).
- `day_amount` (string, required) — Requested amount (working days or hours) as an exact decimal string.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit the request is measured in: `days` or `hours`.
- `note` (string | null, required) — Optional note the employee attached when requesting.
- `reviewer_user_id` (string | null, required, format: uuid) — UUID v7 of the user who approved or rejected the request; `null` while pending.
- `review_note` (string | null, required) — Reason given on rejection (or note on approval); `null` while pending.
- `reviewed_at` (string | null, required, format: date-time) — Timestamp of the approval/rejection (ISO 8601); `null` while pending.
- `created_at` (string, required, format: date-time) — Creation timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/absence-requests/{absence_request}/cancel — Cancel an absence request
- **Operation ID**: `public-api.v1.absence-requests.cancel`
- **Tag**: Absence Requests
- **Required scope**: `absences:transition` — Change the lifecycle status of absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-requests/public-api.v1.absence-requests.cancel
Cancel an absence request. If it was approved, the consumed balance is released back. No request body. A request belonging to another company returns 404 `absence_request_not_found` (anti-enumeration). Returns the updated request.
## Path parameters
- `absence_request` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsenceRequest), required) — An absence request for the Control Horario (time tracking) module: an employee’s request to be off for a date range under an absence type. `status` walks `pending → approved | rejected | cancelled`. `day_amount` is the requested amount as an exact decimal string, measured in `measurement_unit` (days or hours). Review fields (`reviewer_user_id`, `review_note`, `reviewed_at`) are `null` while the request is still pending.
- `id` (string, required, format: uuid) — Opaque identifier of the absence request, a UUID v7.
- `object` (string, required, enum: `absence_request`) — Always `absence_request`.
- `employee_id` (string, required, format: uuid) — UUID v7 of the employee the request belongs to.
- `absence_type_id` (string, required, format: uuid) — UUID v7 of the requested absence type.
- `absence_type_name` (string, required) — Human-readable name of the absence type (e.g. `Vacaciones`).
- `color` (string, required) — Hex color `#RRGGBB` of the absence type, used to render the request on the calendar.
- `status` (string, required, enum: `pending`, `approved`, `rejected`, `cancelled`) — Lifecycle status of the request: `pending`, `approved`, `rejected` or `cancelled`.
- `start_date` (string, required, format: date) — First day of the absence (`YYYY-MM-DD`).
- `end_date` (string, required, format: date) — Last day of the absence (`YYYY-MM-DD`).
- `day_amount` (string, required) — Requested amount (working days or hours) as an exact decimal string.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit the request is measured in: `days` or `hours`.
- `note` (string | null, required) — Optional note the employee attached when requesting.
- `reviewer_user_id` (string | null, required, format: uuid) — UUID v7 of the user who approved or rejected the request; `null` while pending.
- `review_note` (string | null, required) — Reason given on rejection (or note on approval); `null` while pending.
- `reviewed_at` (string | null, required, format: date-time) — Timestamp of the approval/rejection (ISO 8601); `null` while pending.
- `created_at` (string, required, format: date-time) — Creation timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/absence-requests — Create an absence request
- **Operation ID**: `public-api.v1.absence-requests.create`
- **Tag**: Absence Requests
- **Required scope**: `absences:write` — Create and update absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-requests/public-api.v1.absence-requests.create
Create an absence request for the authenticated company (resolved from the API key, never from the payload). `employee_id` (UUID v7) is required — an API key acts as a system, so the target employee must be given. `absence_type_id` (UUID v7) and the `start_date`/`end_date` range (`YYYY-MM-DD`, end on or after start) are required; `note` is optional. The requested amount is computed in working days minus the applicable public holidays. If the absence type does not require approval it is auto-approved and consumes the balance. Returns the created request with its generated `id` (UUID v7).
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 5 properties; 4 required: `employee_id`, `absence_type_id`, `start_date`, `end_date`.
- `employee_id` (string, required, format: uuid) — Employee ID (UUID v7) requesting the absence.
- `absence_type_id` (string, required, format: uuid) — Requested absence type ID (UUID v7).
- `start_date` (string, required, format: date) — Absence start date, in `Y-m-d`.
- `end_date` (string, required, format: date) — Absence end date, in `Y-m-d`, equal to or after the start.
- `note` (string | null, optional, maxLength 1000) — Optional note for the request.
## Responses
- **201**
- Body (`application/json`):
- `data` (object (AbsenceRequest), required) — An absence request for the Control Horario (time tracking) module: an employee’s request to be off for a date range under an absence type. `status` walks `pending → approved | rejected | cancelled`. `day_amount` is the requested amount as an exact decimal string, measured in `measurement_unit` (days or hours). Review fields (`reviewer_user_id`, `review_note`, `reviewed_at`) are `null` while the request is still pending.
- `id` (string, required, format: uuid) — Opaque identifier of the absence request, a UUID v7.
- `object` (string, required, enum: `absence_request`) — Always `absence_request`.
- `employee_id` (string, required, format: uuid) — UUID v7 of the employee the request belongs to.
- `absence_type_id` (string, required, format: uuid) — UUID v7 of the requested absence type.
- `absence_type_name` (string, required) — Human-readable name of the absence type (e.g. `Vacaciones`).
- `color` (string, required) — Hex color `#RRGGBB` of the absence type, used to render the request on the calendar.
- `status` (string, required, enum: `pending`, `approved`, `rejected`, `cancelled`) — Lifecycle status of the request: `pending`, `approved`, `rejected` or `cancelled`.
- `start_date` (string, required, format: date) — First day of the absence (`YYYY-MM-DD`).
- `end_date` (string, required, format: date) — Last day of the absence (`YYYY-MM-DD`).
- `day_amount` (string, required) — Requested amount (working days or hours) as an exact decimal string.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit the request is measured in: `days` or `hours`.
- `note` (string | null, required) — Optional note the employee attached when requesting.
- `reviewer_user_id` (string | null, required, format: uuid) — UUID v7 of the user who approved or rejected the request; `null` while pending.
- `review_note` (string | null, required) — Reason given on rejection (or note on approval); `null` while pending.
- `reviewed_at` (string | null, required, format: date-time) — Timestamp of the approval/rejection (ISO 8601); `null` while pending.
- `created_at` (string, required, format: date-time) — Creation timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/absence-requests — List all absence requests
- **Operation ID**: `public-api.v1.absence-requests.list`
- **Tag**: Absence Requests
- **Required scope**: `absences:read` — Read absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-requests/public-api.v1.absence-requests.list
List your company’s absence requests with cursor-based pagination. Supports filtering by `employee_id` (UUID v7), `absence_type_id` (UUID v7), `status` (`pending`/`approved`/`rejected`/`cancelled`) and by date range (`from`/`to`, `YYYY-MM-DD`).
## Query parameters
- `employee_id` (string | null, optional, format: uuid) — Employee ID (UUID v7) to filter requests by.
- `absence_type_id` (string | null, optional, format: uuid) — Absence type ID (UUID v7) to filter requests by.
- `status` (string | null, optional, enum: `pending`, `approved`, `rejected`, `cancelled`) — Lifecycle status to filter by (pending/approved/rejected/cancelled).
- `from` (string | null, optional, format: date) — Start date (Y-m-d) to filter the request range by.
- `to` (string | null, optional, format: date) — End date (Y-m-d) to filter the request range by.
- `limit` (integer, optional, min 1, max 100, default: `25`) — Number of objects to return.
- `starting_after` (string, optional, format: uuid) — Cursor for forward pagination.
- `ending_before` (string, optional, format: uuid) — Cursor for backward pagination.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (array, required)
- `id` (string, required, format: uuid) — Opaque identifier of the absence request, a UUID v7.
- `object` (string, required, enum: `absence_request`) — Always `absence_request`.
- `employee_id` (string, required, format: uuid) — UUID v7 of the employee the request belongs to.
- `absence_type_id` (string, required, format: uuid) — UUID v7 of the requested absence type.
- `absence_type_name` (string, required) — Human-readable name of the absence type (e.g. `Vacaciones`).
- `color` (string, required) — Hex color `#RRGGBB` of the absence type, used to render the request on the calendar.
- `status` (string, required, enum: `pending`, `approved`, `rejected`, `cancelled`) — Lifecycle status of the request: `pending`, `approved`, `rejected` or `cancelled`.
- `start_date` (string, required, format: date) — First day of the absence (`YYYY-MM-DD`).
- `end_date` (string, required, format: date) — Last day of the absence (`YYYY-MM-DD`).
- `day_amount` (string, required) — Requested amount (working days or hours) as an exact decimal string.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit the request is measured in: `days` or `hours`.
- `note` (string | null, required) — Optional note the employee attached when requesting.
- `reviewer_user_id` (string | null, required, format: uuid) — UUID v7 of the user who approved or rejected the request; `null` while pending.
- `review_note` (string | null, required) — Reason given on rejection (or note on approval); `null` while pending.
- `reviewed_at` (string | null, required, format: date-time) — Timestamp of the approval/rejection (ISO 8601); `null` while pending.
- `created_at` (string, required, format: date-time) — Creation timestamp (ISO 8601).
- `has_more` (boolean, required) — Whether more pages are available after this one.
- `next_cursor` (string | null, required) — Opaque cursor for the next page, or `null` when `has_more` is `false`. The example below shows the shape used by most listings; others return a different one, so pass the value back verbatim instead of validating it.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/absence-requests/{absence_request}/reject — Reject an absence request
- **Operation ID**: `public-api.v1.absence-requests.reject`
- **Tag**: Absence Requests
- **Required scope**: `absences:transition` — Change the lifecycle status of absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-requests/public-api.v1.absence-requests.reject
Reject a pending absence request (transition `pending` → `rejected`). A `reason` is required (422 without it); rejecting neither consumes nor releases balance. Returns the updated request.
## Path parameters
- `absence_request` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 1 property; 1 required: `reason`.
- `reason` (string, required, maxLength 1000) — Rejection reason (required).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsenceRequest), required) — An absence request for the Control Horario (time tracking) module: an employee’s request to be off for a date range under an absence type. `status` walks `pending → approved | rejected | cancelled`. `day_amount` is the requested amount as an exact decimal string, measured in `measurement_unit` (days or hours). Review fields (`reviewer_user_id`, `review_note`, `reviewed_at`) are `null` while the request is still pending.
- `id` (string, required, format: uuid) — Opaque identifier of the absence request, a UUID v7.
- `object` (string, required, enum: `absence_request`) — Always `absence_request`.
- `employee_id` (string, required, format: uuid) — UUID v7 of the employee the request belongs to.
- `absence_type_id` (string, required, format: uuid) — UUID v7 of the requested absence type.
- `absence_type_name` (string, required) — Human-readable name of the absence type (e.g. `Vacaciones`).
- `color` (string, required) — Hex color `#RRGGBB` of the absence type, used to render the request on the calendar.
- `status` (string, required, enum: `pending`, `approved`, `rejected`, `cancelled`) — Lifecycle status of the request: `pending`, `approved`, `rejected` or `cancelled`.
- `start_date` (string, required, format: date) — First day of the absence (`YYYY-MM-DD`).
- `end_date` (string, required, format: date) — Last day of the absence (`YYYY-MM-DD`).
- `day_amount` (string, required) — Requested amount (working days or hours) as an exact decimal string.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit the request is measured in: `days` or `hours`.
- `note` (string | null, required) — Optional note the employee attached when requesting.
- `reviewer_user_id` (string | null, required, format: uuid) — UUID v7 of the user who approved or rejected the request; `null` while pending.
- `review_note` (string | null, required) — Reason given on rejection (or note on approval); `null` while pending.
- `reviewed_at` (string | null, required, format: date-time) — Timestamp of the approval/rejection (ISO 8601); `null` while pending.
- `created_at` (string, required, format: date-time) — Creation timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/absence-requests/{absence_request} — Retrieve an absence request
- **Operation ID**: `public-api.v1.absence-requests.show`
- **Tag**: Absence Requests
- **Required scope**: `absences:read` — Read absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-requests/public-api.v1.absence-requests.show
Retrieve a single absence request by its `id` (UUID v7), including its type, date range, requested amount, lifecycle status and review fields. A request belonging to another company returns 404 `absence_request_not_found` (anti-enumeration).
## Path parameters
- `absence_request` (string, required)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsenceRequest), required) — An absence request for the Control Horario (time tracking) module: an employee’s request to be off for a date range under an absence type. `status` walks `pending → approved | rejected | cancelled`. `day_amount` is the requested amount as an exact decimal string, measured in `measurement_unit` (days or hours). Review fields (`reviewer_user_id`, `review_note`, `reviewed_at`) are `null` while the request is still pending.
- `id` (string, required, format: uuid) — Opaque identifier of the absence request, a UUID v7.
- `object` (string, required, enum: `absence_request`) — Always `absence_request`.
- `employee_id` (string, required, format: uuid) — UUID v7 of the employee the request belongs to.
- `absence_type_id` (string, required, format: uuid) — UUID v7 of the requested absence type.
- `absence_type_name` (string, required) — Human-readable name of the absence type (e.g. `Vacaciones`).
- `color` (string, required) — Hex color `#RRGGBB` of the absence type, used to render the request on the calendar.
- `status` (string, required, enum: `pending`, `approved`, `rejected`, `cancelled`) — Lifecycle status of the request: `pending`, `approved`, `rejected` or `cancelled`.
- `start_date` (string, required, format: date) — First day of the absence (`YYYY-MM-DD`).
- `end_date` (string, required, format: date) — Last day of the absence (`YYYY-MM-DD`).
- `day_amount` (string, required) — Requested amount (working days or hours) as an exact decimal string.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit the request is measured in: `days` or `hours`.
- `note` (string | null, required) — Optional note the employee attached when requesting.
- `reviewer_user_id` (string | null, required, format: uuid) — UUID v7 of the user who approved or rejected the request; `null` while pending.
- `review_note` (string | null, required) — Reason given on rejection (or note on approval); `null` while pending.
- `reviewed_at` (string | null, required, format: date-time) — Timestamp of the approval/rejection (ISO 8601); `null` while pending.
- `created_at` (string, required, format: date-time) — Creation timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/absence-types/{absence_type}/archive — Archive an absence type
- **Operation ID**: `public-api.v1.absence-types.archive`
- **Tag**: Absence Types
- **Required scope**: `absences:write` — Create and update absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-types/public-api.v1.absence-types.archive
Archive an absence type (transition `active` → `archived`), retiring it from use while preserving it. No request body. Returns 422 if it is already archived. Reversible via unarchive.
## Path parameters
- `absence_type` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsenceType), required) — A configurable absence type for the Control Horario (time tracking) module — what an employee can request (holidays, sick leave, paid leave, …). Its flags decide whether the period is paid (`is_paid`) and whether it needs manager approval (`requires_approval`); `measurement_unit` fixes whether balances are tracked in days or hours.
- `id` (string, required, format: uuid) — Opaque identifier of the absence type, a UUID v7 (e.g. `01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a9b`).
- `object` (string, required, enum: `absence_type`) — Always `absence_type`.
- `name` (string, required) — Human-readable name of the absence type (e.g. `Vacaciones`).
- `is_paid` (boolean, required) — Whether the absence is paid (the employee is compensated for the period).
- `requires_approval` (boolean, required) — Whether requesting this absence requires a manager’s approval.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit in which the absence is measured: `days` or `hours`.
- `color` (string, required) — Hex color `#RRGGBB` used to render the type on the calendar.
- `visibility` (string, required, enum: `everyone`, `managers_only`) — Who can see the type: `everyone` or `managers_only`.
- `is_system` (boolean, required) — Whether it is a default system type seeded for every company.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/absence-types — Create an absence type
- **Operation ID**: `public-api.v1.absence-types.create`
- **Tag**: Absence Types
- **Required scope**: `absences:write` — Create and update absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-types/public-api.v1.absence-types.create
Create an absence type for the authenticated company (resolved from the API key, never from the payload). `name`, `is_paid`, `requires_approval`, `measurement_unit` (`days`/`hours`), `color` (hex `#RRGGBB`) and `visibility` (`everyone`/`managers_only`) are all required. Returns the created type with its generated `id` (UUID v7).
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 6 properties; 6 required: `name`, `is_paid`, `requires_approval`, `measurement_unit`, `color`, `visibility`.
- `name` (string, required, maxLength 120) — Absence type name.
- `is_paid` (boolean, required) — Whether the absence is paid (the employee is compensated for the period).
- `requires_approval` (boolean, required) — Whether requesting this absence requires a manager's approval.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit in which the absence is measured: `days` or `hours`.
- `color` (string, required, pattern: `^#[0-9A-Fa-f]{6}$`) — Type color for the calendar, in `#RRGGBB` hexadecimal format.
- `visibility` (string, required, enum: `everyone`, `managers_only`) — Who can see the type: `everyone` or `managers_only`.
## Responses
- **201**
- Body (`application/json`):
- `data` (object (AbsenceType), required) — A configurable absence type for the Control Horario (time tracking) module — what an employee can request (holidays, sick leave, paid leave, …). Its flags decide whether the period is paid (`is_paid`) and whether it needs manager approval (`requires_approval`); `measurement_unit` fixes whether balances are tracked in days or hours.
- `id` (string, required, format: uuid) — Opaque identifier of the absence type, a UUID v7 (e.g. `01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a9b`).
- `object` (string, required, enum: `absence_type`) — Always `absence_type`.
- `name` (string, required) — Human-readable name of the absence type (e.g. `Vacaciones`).
- `is_paid` (boolean, required) — Whether the absence is paid (the employee is compensated for the period).
- `requires_approval` (boolean, required) — Whether requesting this absence requires a manager’s approval.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit in which the absence is measured: `days` or `hours`.
- `color` (string, required) — Hex color `#RRGGBB` used to render the type on the calendar.
- `visibility` (string, required, enum: `everyone`, `managers_only`) — Who can see the type: `everyone` or `managers_only`.
- `is_system` (boolean, required) — Whether it is a default system type seeded for every company.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/absence-types — List all absence types
- **Operation ID**: `public-api.v1.absence-types.list`
- **Tag**: Absence Types
- **Required scope**: `absences:read` — Read absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-types/public-api.v1.absence-types.list
List your company’s absence types with cursor-based pagination. Supports filtering by `status` (`active`/`archived`) and `measurement_unit` (`days`/`hours`), plus free-text `search` over the type name.
## Query parameters
- `limit` (integer, optional, min 1, max 100, default: `25`) — Number of objects to return.
- `starting_after` (string, optional, format: uuid) — Cursor for forward pagination.
- `ending_before` (string, optional, format: uuid) — Cursor for backward pagination.
- `status` (string, optional, enum: `active`, `archived`) — Lifecycle status of the absence type.
- `status[in]` (string, optional) — Lifecycle status of the absence type.
- `measurement_unit` (string, optional, enum: `days`, `hours`) — Measurement unit used to quantify the absence type.
- `measurement_unit[in]` (string, optional) — Measurement unit used to quantify the absence type.
- `search` (string, optional, maxLength 80) — Free-text search.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (array, required)
- `id` (string, required, format: uuid) — Opaque identifier of the absence type, a UUID v7 (e.g. `01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a9b`).
- `object` (string, required, enum: `absence_type`) — Always `absence_type`.
- `name` (string, required) — Human-readable name of the absence type (e.g. `Vacaciones`).
- `is_paid` (boolean, required) — Whether the absence is paid (the employee is compensated for the period).
- `requires_approval` (boolean, required) — Whether requesting this absence requires a manager’s approval.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit in which the absence is measured: `days` or `hours`.
- `color` (string, required) — Hex color `#RRGGBB` used to render the type on the calendar.
- `visibility` (string, required, enum: `everyone`, `managers_only`) — Who can see the type: `everyone` or `managers_only`.
- `is_system` (boolean, required) — Whether it is a default system type seeded for every company.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- `has_more` (boolean, required) — Whether more pages are available after this one.
- `next_cursor` (string | null, required) — Opaque cursor for the next page, or `null` when `has_more` is `false`. The example below shows the shape used by most listings; others return a different one, so pass the value back verbatim instead of validating it.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/absence-types/{absence_type} — Retrieve an absence type
- **Operation ID**: `public-api.v1.absence-types.show`
- **Tag**: Absence Types
- **Required scope**: `absences:read` — Read absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-types/public-api.v1.absence-types.show
Retrieve a single absence type by its `id` (UUID v7). A type belonging to another company returns 404 `absence_type_not_found` (anti-enumeration).
## Path parameters
- `absence_type` (string, required)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsenceType), required) — A configurable absence type for the Control Horario (time tracking) module — what an employee can request (holidays, sick leave, paid leave, …). Its flags decide whether the period is paid (`is_paid`) and whether it needs manager approval (`requires_approval`); `measurement_unit` fixes whether balances are tracked in days or hours.
- `id` (string, required, format: uuid) — Opaque identifier of the absence type, a UUID v7 (e.g. `01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a9b`).
- `object` (string, required, enum: `absence_type`) — Always `absence_type`.
- `name` (string, required) — Human-readable name of the absence type (e.g. `Vacaciones`).
- `is_paid` (boolean, required) — Whether the absence is paid (the employee is compensated for the period).
- `requires_approval` (boolean, required) — Whether requesting this absence requires a manager’s approval.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit in which the absence is measured: `days` or `hours`.
- `color` (string, required) — Hex color `#RRGGBB` used to render the type on the calendar.
- `visibility` (string, required, enum: `everyone`, `managers_only`) — Who can see the type: `everyone` or `managers_only`.
- `is_system` (boolean, required) — Whether it is a default system type seeded for every company.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/absence-types/{absence_type}/unarchive — Unarchive an absence type
- **Operation ID**: `public-api.v1.absence-types.unarchive`
- **Tag**: Absence Types
- **Required scope**: `absences:write` — Create and update absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-types/public-api.v1.absence-types.unarchive
Unarchive an absence type (transition `archived` → `active`), returning it to use. No request body. Returns 422 if it is already active.
## Path parameters
- `absence_type` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsenceType), required) — A configurable absence type for the Control Horario (time tracking) module — what an employee can request (holidays, sick leave, paid leave, …). Its flags decide whether the period is paid (`is_paid`) and whether it needs manager approval (`requires_approval`); `measurement_unit` fixes whether balances are tracked in days or hours.
- `id` (string, required, format: uuid) — Opaque identifier of the absence type, a UUID v7 (e.g. `01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a9b`).
- `object` (string, required, enum: `absence_type`) — Always `absence_type`.
- `name` (string, required) — Human-readable name of the absence type (e.g. `Vacaciones`).
- `is_paid` (boolean, required) — Whether the absence is paid (the employee is compensated for the period).
- `requires_approval` (boolean, required) — Whether requesting this absence requires a manager’s approval.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit in which the absence is measured: `days` or `hours`.
- `color` (string, required) — Hex color `#RRGGBB` used to render the type on the calendar.
- `visibility` (string, required, enum: `everyone`, `managers_only`) — Who can see the type: `everyone` or `managers_only`.
- `is_system` (boolean, required) — Whether it is a default system type seeded for every company.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# PUT /v1/absence-types/{absence_type} — Update an absence type
- **Operation ID**: `public-api.v1.absence-types.update`
- **Tag**: Absence Types
- **Required scope**: `absences:write` — Create and update absences.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/absence-types/public-api.v1.absence-types.update
Partially update an absence type: only the fields present in the payload are changed; omitted fields keep their current value. Returns the updated type.
## Path parameters
- `absence_type` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, optional. 6 properties; none of them required.
- `name` (string | null, optional, maxLength 120) — Absence type name.
- `is_paid` (boolean | null, optional) — Whether the absence is paid (the employee is compensated for the period).
- `requires_approval` (boolean | null, optional) — Whether requesting this absence requires a manager's approval.
- `measurement_unit` (string | null, optional, enum: `days`, `hours`) — Unit in which the absence is measured: `days` or `hours`.
- `color` (string | null, optional, pattern: `^#[0-9A-Fa-f]{6}$`) — Type color for the calendar, in `#RRGGBB` hexadecimal format.
- `visibility` (string | null, optional, enum: `everyone`, `managers_only`) — Who can see the type: `everyone` or `managers_only`.
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AbsenceType), required) — A configurable absence type for the Control Horario (time tracking) module — what an employee can request (holidays, sick leave, paid leave, …). Its flags decide whether the period is paid (`is_paid`) and whether it needs manager approval (`requires_approval`); `measurement_unit` fixes whether balances are tracked in days or hours.
- `id` (string, required, format: uuid) — Opaque identifier of the absence type, a UUID v7 (e.g. `01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a9b`).
- `object` (string, required, enum: `absence_type`) — Always `absence_type`.
- `name` (string, required) — Human-readable name of the absence type (e.g. `Vacaciones`).
- `is_paid` (boolean, required) — Whether the absence is paid (the employee is compensated for the period).
- `requires_approval` (boolean, required) — Whether requesting this absence requires a manager’s approval.
- `measurement_unit` (string, required, enum: `days`, `hours`) — Unit in which the absence is measured: `days` or `hours`.
- `color` (string, required) — Hex color `#RRGGBB` used to render the type on the calendar.
- `visibility` (string, required, enum: `everyone`, `managers_only`) — Who can see the type: `everyone` or `managers_only`.
- `is_system` (boolean, required) — Whether it is a default system type seeded for every company.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (usable) or `archived` (retired from use).
- `created_at` (string | null, required, format: date-time) — Creation timestamp (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — Last-update timestamp (ISO 8601).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/account/api-keys — Create an API key
- **Operation ID**: `public-api.v1.account.api_keys.create`
- **Tag**: Account
- **Required scope**: `account:write` — Create and update account.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/account/public-api.v1.account.api_keys.create
Create a new API key and return its plaintext `secret` exactly once — store it now, it cannot be retrieved later. Requesting a scope above the holder's plan or outside the catalog returns 422. Pass `environment: test` to mint a sandbox key (`fact_test_`) with no real-world side effects; omit it for a live key (`fact_live_`).
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 5 properties; 2 required: `name`, `scopes`.
Create an API key for your own tenant. The `tier` is never accepted from the body — it is derived from the holder plan and add-ons. Requested `scopes` must belong to the closed v1 catalog and stay within the holder plan; a scope above the plan returns 422.
- `name` (string, required, maxLength 120, minLength 1) — Human-readable name for the API key (1-120 characters).
- `expires_at` (string | null, optional, format: date-time) — Future ISO 8601 date after which the key stops authenticating.
- `environment` (string | null, optional, enum: `live`, `test`) — Key environment: `live` (production) or `test` (sandbox). Defaults to `live`.
- `scopes` (array, required) — List of scopes from the closed v1 catalog (at least one).
- `ip_allowlist` (array | null, optional) — Optional list of allowed IPs / CIDR ranges (IPv4, IPv6, /N).
## Responses
- **201**
- Body (`application/json`):
- `data` (object (ApiKeyWithSecret), required) — An API key returned once at creation or after secret rotation. Includes the plaintext `secret` — store it now, it cannot be retrieved later.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the API key.
- `object` (string, required, enum: `api_key`) — Always `api_key`.
- `name` (string, required) — Human-friendly label assigned at creation time.
- `prefix` (string, required) — First chars of the key (e.g. `fact_live_1N0Fnyhh`) — safe to log. Does NOT authenticate.
- `scopes` (array, required) — Authorized scopes. `*` means super scope (full access).
- `tier` (string, required) — Rate-limit tier (`free`, `starter`, `pro`, `scale`). Derived from the company plan (or from an active capacity boost when higher), never set from the request body.
- `environment` (string, required, enum: `live`, `test`) — Key environment: `live` (`fact_live_`, real side effects) or `test` (`fact_test_`, sandbox company, no real-world effects).
- `created_at` (string, required, format: date-time)
- `last_used_at` (string | null, required, format: date-time) — Timestamp of the last authenticated request with this key, or `null` if never used.
- `expires_at` (string | null, required, format: date-time) — Expiry instant (ISO 8601), or `null` if the key does not expire.
- `revoked_at` (string | null, required, format: date-time) — Revocation instant (ISO 8601), or `null` if the key is not revoked.
- `is_active` (boolean, required) — `true` when the key is usable (not revoked and not expired).
- `is_revoked` (boolean, required) — `true` once the key has been revoked.
- `secret` (string, required) — Plaintext secret. Returned only at creation or after rotation — never again.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed — e.g. the API key plan limit was reached, or an invoice language outside the allowed catalog (`es`, `en`, `ca`).
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/account/api-keys — List your API keys
- **Operation ID**: `public-api.v1.account.api_keys.list`
- **Tag**: Account
- **Required scope**: `account:read` — Read account.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/account/public-api.v1.account.api_keys.list
List the API keys of the authenticated company with cursor-based pagination. Each key exposes its `prefix`, `scopes`, `tier`, `environment` (`live`/`test`) and lifecycle timestamps. The plaintext secret is never returned — it is shown once, at creation or rotation.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200** — `ApiKeyListV1Resource`
- Body (`application/json`):
- `data` (array, required) — Page of API keys.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the API key.
- `object` (string, required, enum: `api_key`) — Always `api_key`.
- `name` (string, required) — Human-friendly label assigned at creation time.
- `prefix` (string, required) — First chars of the key (e.g. `fact_live_1N0Fnyhh`) — safe to log. Does NOT authenticate.
- `scopes` (array, required) — Authorized scopes. `*` means super scope (full access).
- `tier` (string, required) — Rate-limit tier (`free`, `starter`, `pro`, `scale`). Derived from the company plan (or from an active capacity boost when higher), never set from the request body.
- `environment` (string, required, enum: `live`, `test`) — Key environment: `live` (`fact_live_`, real side effects) or `test` (`fact_test_`, sandbox company, no real-world effects).
- `created_at` (string, required, format: date-time)
- `last_used_at` (string | null, required, format: date-time) — Timestamp of the last authenticated request with this key, or `null` if never used.
- `expires_at` (string | null, required, format: date-time) — Expiry instant (ISO 8601), or `null` if the key does not expire.
- `revoked_at` (string | null, required, format: date-time) — Revocation instant (ISO 8601), or `null` if the key is not revoked.
- `is_active` (boolean, required) — `true` when the key is usable (not revoked and not expired).
- `is_revoked` (boolean, required) — `true` once the key has been revoked.
- `has_more` (boolean, required) — `true` when more keys exist beyond this page.
- `next_cursor` (string | null, required) — Opaque cursor for the next page, or `null` when `has_more` is `false`.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/account/api-keys/{api_key}/revoke — Revoke an API key
- **Operation ID**: `public-api.v1.account.api_keys.revoke`
- **Tag**: Account
- **Required scope**: `account:write` — Create and update account.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/account/public-api.v1.account.api_keys.revoke
Revoke an API key immediately and irreversibly. Subsequent requests authenticated with that key fail with 401. You may revoke the key currently in use — doing so cuts off your own access. Revoking a key of another company returns 404 `api_key_not_found`.
## Path parameters
- `api_key` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, optional. 1 property; none of them required.
Optionally record why the API key is being revoked. `reason` (string, ≤500 chars) is optional; when omitted a default reason is stored for auditing.
- `reason` (string | null, optional, maxLength 500) — Optional reason for the revocation (recorded in the audit log).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (ApiKey), required) — An API key of your company. The plaintext secret is never exposed in this representation — it is shown only once, at creation or after rotation.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the API key.
- `object` (string, required, enum: `api_key`) — Always `api_key`.
- `name` (string, required) — Human-friendly label assigned at creation time.
- `prefix` (string, required) — First chars of the key (e.g. `fact_live_1N0Fnyhh`) — safe to log. Does NOT authenticate.
- `scopes` (array, required) — Authorized scopes. `*` means super scope (full access).
- `tier` (string, required) — Rate-limit tier (`free`, `starter`, `pro`, `scale`). Derived from the company plan (or from an active capacity boost when higher), never set from the request body.
- `environment` (string, required, enum: `live`, `test`) — Key environment: `live` (`fact_live_`, real side effects) or `test` (`fact_test_`, sandbox company, no real-world effects).
- `created_at` (string, required, format: date-time)
- `last_used_at` (string | null, required, format: date-time) — Timestamp of the last authenticated request with this key, or `null` if never used.
- `expires_at` (string | null, required, format: date-time) — Expiry instant (ISO 8601), or `null` if the key does not expire.
- `revoked_at` (string | null, required, format: date-time) — Revocation instant (ISO 8601), or `null` if the key is not revoked.
- `is_active` (boolean, required) — `true` when the key is usable (not revoked and not expired).
- `is_revoked` (boolean, required) — `true` once the key has been revoked.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed — e.g. the API key plan limit was reached, or an invoice language outside the allowed catalog (`es`, `en`, `ca`).
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/account/api-keys/{api_key}/rotate_secret — Rotate an API key secret
- **Operation ID**: `public-api.v1.account.api_keys.rotate_secret`
- **Tag**: Account
- **Required scope**: `account:write` — Create and update account.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/account/public-api.v1.account.api_keys.rotate_secret
Invalidate the current secret of an API key immediately, generate a fresh `prefix` + `secret`, and return the new secret in plaintext exactly once. Any request made with the previous secret stops authenticating right away. Irreversible.
## Path parameters
- `api_key` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (ApiKeyWithSecret), required) — An API key returned once at creation or after secret rotation. Includes the plaintext `secret` — store it now, it cannot be retrieved later.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the API key.
- `object` (string, required, enum: `api_key`) — Always `api_key`.
- `name` (string, required) — Human-friendly label assigned at creation time.
- `prefix` (string, required) — First chars of the key (e.g. `fact_live_1N0Fnyhh`) — safe to log. Does NOT authenticate.
- `scopes` (array, required) — Authorized scopes. `*` means super scope (full access).
- `tier` (string, required) — Rate-limit tier (`free`, `starter`, `pro`, `scale`). Derived from the company plan (or from an active capacity boost when higher), never set from the request body.
- `environment` (string, required, enum: `live`, `test`) — Key environment: `live` (`fact_live_`, real side effects) or `test` (`fact_test_`, sandbox company, no real-world effects).
- `created_at` (string, required, format: date-time)
- `last_used_at` (string | null, required, format: date-time) — Timestamp of the last authenticated request with this key, or `null` if never used.
- `expires_at` (string | null, required, format: date-time) — Expiry instant (ISO 8601), or `null` if the key does not expire.
- `revoked_at` (string | null, required, format: date-time) — Revocation instant (ISO 8601), or `null` if the key is not revoked.
- `is_active` (boolean, required) — `true` when the key is usable (not revoked and not expired).
- `is_revoked` (boolean, required) — `true` once the key has been revoked.
- `secret` (string, required) — Plaintext secret. Returned only at creation or after rotation — never again.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed — e.g. the API key plan limit was reached, or an invoice language outside the allowed catalog (`es`, `en`, `ca`).
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/account/api-keys/{api_key} — Retrieve an API key
- **Operation ID**: `public-api.v1.account.api_keys.show`
- **Tag**: Account
- **Required scope**: `account:read` — Read account.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/account/public-api.v1.account.api_keys.show
Retrieve a single API key of the authenticated company by its `id` (UUID v7). The plaintext secret is never included. A key belonging to another company returns 404 `api_key_not_found` (anti-enumeration).
## Path parameters
- `api_key` (string, required)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (ApiKey), required) — An API key of your company. The plaintext secret is never exposed in this representation — it is shown only once, at creation or after rotation.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the API key.
- `object` (string, required, enum: `api_key`) — Always `api_key`.
- `name` (string, required) — Human-friendly label assigned at creation time.
- `prefix` (string, required) — First chars of the key (e.g. `fact_live_1N0Fnyhh`) — safe to log. Does NOT authenticate.
- `scopes` (array, required) — Authorized scopes. `*` means super scope (full access).
- `tier` (string, required) — Rate-limit tier (`free`, `starter`, `pro`, `scale`). Derived from the company plan (or from an active capacity boost when higher), never set from the request body.
- `environment` (string, required, enum: `live`, `test`) — Key environment: `live` (`fact_live_`, real side effects) or `test` (`fact_test_`, sandbox company, no real-world effects).
- `created_at` (string, required, format: date-time)
- `last_used_at` (string | null, required, format: date-time) — Timestamp of the last authenticated request with this key, or `null` if never used.
- `expires_at` (string | null, required, format: date-time) — Expiry instant (ISO 8601), or `null` if the key does not expire.
- `revoked_at` (string | null, required, format: date-time) — Revocation instant (ISO 8601), or `null` if the key is not revoked.
- `is_active` (boolean, required) — `true` when the key is usable (not revoked and not expired).
- `is_revoked` (boolean, required) — `true` once the key has been revoked.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/account/billing — Retrieve account billing details
- **Operation ID**: `public-api.v1.account.billing`
- **Tag**: Account
- **Required scope**: `account:read` — Read account.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/account/public-api.v1.account.billing
Returns the subscription billing snapshot of the authenticated company: base plan subscription (status, trial, current period end, pending plan change), gestoría seats subscription (quantity, active managed companies, per-seat cost with VAT, recurring total, next invoice) and default payment method. Managed companies (plan `gestionada`) receive `managed: true` without the master's billing data. Amounts are integer cents; unresolved amounts are `null`, never a misleading 0.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AccountBilling), required) — Billing snapshot of the effective tenant: current plan (trial, grace period, pending change), gestoría seat subscription (when the account manages companies) and payment method. Managed child companies return `managed: true` with the synthetic `gestionada` plan — the master's billing never leaks to a child key.
- `object` (string, required, enum: `account_billing`) — Stripe-like discriminator. Always `account_billing` for this resource.
- `managed` (boolean, required) — true when the effective tenant is a managed child company (gestoría): its billing is the master's seat, so `gestoria_seats` and `payment_method` are null.
- `plan` (object, required) — Current plan subscription of the account.
- `gestoria_seats` (object | null, required) — Gestoría seat subscription of the account, or null when the tenant does not manage companies. Amounts are in minor currency units (cents) and are null when the recurring cost is not resolvable (enterprise outside Stripe, sandbox, no active plan) — never a misleading 0.
- `payment_method` (object | null, required) — Default payment method on file, or null when none is configured.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/account/personalization/templates — List available personalization templates
- **Operation ID**: `public-api.v1.account.personalization.templates`
- **Tag**: Account
- **Required scope**: `account:read` — Read account.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/account/public-api.v1.account.personalization.templates
List the PDF templates available for the account's plan (plan-aware) plus the accepted format for the `accent_color`. Use it to discover which `pdf_template` slugs and colors can be set via `PATCH /v1/account/personalization`.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (AccountPersonalizationTemplates), required) — Plan-aware catalog of PDF templates available for the account, plus the accepted hex format for the accent color. Returned by `GET /v1/account/personalization/templates`. Use it to discover which `pdf_template` slugs and colors can be set via `PATCH /v1/account/personalization`.
- `object` (string, required, enum: `personalization_templates`) — Stripe-like discriminator. Always `personalization_templates` for this resource.
- `templates` (array, required) — Available PDF templates for the account's plan.
- `accent_color` (object, required) — Accepted format for the `accent_color` of the PDF.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# PATCH /v1/account/personalization — Update account personalization
- **Operation ID**: `public-api.v1.account.personalization.update`
- **Tag**: Account
- **Required scope**: `account:write` — Create and update account.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/account/public-api.v1.account.personalization.update
Set the invoice-emission language, PDF template and accent color of the company in one partial update; omitted fields keep their value. `language` is one of `es`, `en`, `ca`; `pdf_template` is a slug from the `PdfTemplate` catalog; `accent_color` is a `#RRGGBB` hex color. Returns the updated `Account` resource.
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, optional. 3 properties; none of them required.
Partial update of the account personalization; only the fields present in the body are applied. `language` is one of `es`, `en`, `ca`; `pdf_template` is a template slug; `accent_color` is a `#RRGGBB` hex color. Values outside the catalog return 422 with the accepted values.
- `language` (string | null, optional, enum: `es`, `en`, `ca`) — Account issuing language (es, en or ca).
- `pdf_template` (string | null, optional, enum: `classic`, `modern`, `minimal`, `corporative`, `premium`) — Invoice PDF template (slug from the template catalog).
- `accent_color` (string | null, optional, pattern: `^#[0-9A-Fa-f]{6}$`) — PDF accent color in hexadecimal (#RRGGBB).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (Account), required) — Snapshot of the company, plan, developer addon status and metadata of the API key used to make the request. Use this endpoint to introspect credentials and discover limits with a single call.
- `object` (string, required, enum: `account`) — Stripe-like discriminator. Always `account` for this resource.
- `company` (object, required)
- `plan` (object, required)
- `addon` (object, required) — State of the `developer_api` addon for this company.
- `api_key` (object, required) — Metadata of the API key used to authenticate the request. The secret is never returned (it is only shown once at creation time).
- `personalization` (object, required) — Account personalization: invoice-emission language and PDF template/accent color. Mutable via `PATCH /v1/account/personalization`.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed — e.g. the API key plan limit was reached, or an invoice language outside the allowed catalog (`es`, `en`, `ca`).
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/account — Retrieve account details
- **Operation ID**: `public-api.v1.account.show`
- **Tag**: Account
- **Required scope**: `account:read` — Read account.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/account/public-api.v1.account.show
Stripe-like account endpoint: returns the authenticated company together with its plan, add-ons, and the metadata of the API key in use (environment, scopes). Use it to introspect what the current key can do.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (Account), required) — Snapshot of the company, plan, developer addon status and metadata of the API key used to make the request. Use this endpoint to introspect credentials and discover limits with a single call.
- `object` (string, required, enum: `account`) — Stripe-like discriminator. Always `account` for this resource.
- `company` (object, required)
- `plan` (object, required)
- `addon` (object, required) — State of the `developer_api` addon for this company.
- `api_key` (object, required) — Metadata of the API key used to authenticate the request. The secret is never returned (it is only shown once at creation time).
- `personalization` (object, required) — Account personalization: invoice-emission language and PDF template/accent color. Mutable via `PATCH /v1/account/personalization`.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/account/census-verification — Verify account against the AEAT census
- **Operation ID**: `public-api.v1.account.verify_census`
- **Tag**: Account
- **Required scope**: `account:read` — Read account.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/account/public-api.v1.account.verify_census
Check the persisted company name + tax ID pair against the AEAT census (VNifV2) to anticipate VeriFactu 4104 rejections. No request body: the endpoint always verifies the account's persisted fiscal data. Fail-open — if AEAT is unreachable the call returns 200 with `status: unavailable`. Test keys (`fact_test_`) return deterministic statuses per magic NIF without contacting AEAT.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (CensusVerification), required) — Result of verifying the account's persisted company name + tax ID pair against the AEAT census (VNifV2). Use it to anticipate VeriFactu 4104 rejections before invoicing. Fail-open: returns `unavailable` when AEAT cannot be reached.
- `object` (string, required, enum: `census_verification`) — Stripe-like discriminator. Always `census_verification` for this resource.
- `status` (string, required, enum: `identified`, `not_identified`, `not_identified_similar`, `identified_inactive`, `identified_revoked`, `unavailable`) — Census result. `identified`: name + tax ID match an active taxpayer. `not_identified`: the pair is not in the census. `not_identified_similar`: a similar individual exists (natural persons only). `identified_inactive` / `identified_revoked`: the taxpayer is deregistered or revoked. `unavailable`: AEAT could not answer (timeout, fault, no platform certificate) — verification is informational and never blocks.
- `verified_name` (string | null, required) — The company name that was checked against the census (the persisted account name). `null` when the account has never been verified.
- `checked_at` (string | null, required, format: date-time) — ISO 8601 timestamp of the last verification. `null` when never verified.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/clients/{client}/activities — List client activity timeline
- **Operation ID**: `public-api.v1.clients.activities`
- **Tag**: Clients
- **Required scope**: `clients:read` — Read clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.activities
Return the audit timeline for a client combining its own domain events plus invoice, quote, delivery note, proforma and purchase invoice events that reference it. Paginated with page and per_page query params (default 50).
## Path parameters
- `client` (string, required)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (array, required)
- `object` (string, required, enum: `activity`)
- `event_type` (string, required) — Tipo de evento de dominio (p. ej. `client.updated`, `invoice.created`).
- `description` (string, required) — Human-readable description of the event in Spanish.
- `metadata` (object, required) — Event metadata. Internal identifiers (PKs) are stripped; `*_uuid` values are preserved.
- `performed_by` (object | null, required) — Actor that originated the event. `{type:"user",...}` for an internal user, `{type:"api_key",...}` when performed via the public v1 API, or `null` when the event is system-generated (scheduler, periodic sweep) with no attributable actor.
- `created_at` (string, required, format: date-time) — When the event occurred (ISO 8601).
- `has_more` (boolean, required) — Whether more pages are available after this one.
- `next_cursor` (string | null, required) — Opaque cursor for the next page, or `null` when `has_more` is `false`. The example below shows the shape used by most listings; others return a different one, so pass the value back verbatim instead of validating it.
- **400** — The request is syntactically malformed — e.g. an unknown query parameter, an integer parameter with non-numeric value, or a value outside the documented range.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/clients/bulk-create — Bulk create clients
- **Operation ID**: `public-api.v1.clients.bulk_create`
- **Tag**: Clients
- **Required scope**: `clients:write` — Create and update clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.bulk_create
Create up to 500 clients in one call, each entry a full client payload. With `dry_run=true` it validates every row without persisting and returns a per-row classification (`results[]`, including duplicate `external_id`/`tax_id` and a non-blocking AEAT census warning); with `dry_run=false` it creates only the valid rows and reports the rest in `failures[]`. Returns the `BulkCreateResult` shape.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 2 properties; 1 required: `clients`.
Create clients in bulk. `clients[]` holds up to 500 client payloads and `dry_run` (default `false`) validates each row without persisting. Per-row rules (format, duplicate `external_id`/`tax_id`, AEAT census) are reported per row instead of failing the whole batch.
- `dry_run` (boolean | null, optional)
- `clients` (array>, required, maxItems 500)
## Responses
- **200**
- Body (`application/json`):
- `data` (object (BulkCreateResult), required) — Result of a bulk invoice creation (`POST /v1/invoices/bulk-create`). `dry_run` reports the mode. In validate-only mode (`dry_run=true`) `results[]` carries the per-row classification `{index, status, errors[], warnings[]}` and nothing is persisted; in create mode (`dry_run=false`) only valid rows are created and `failures[]` carries the rows that were not created (identified by `index`). `total = successful + failed`.
- `dry_run` (boolean, required) — Whether the operation ran in validate-only mode (no invoice was created).
- `total` (integer, required) — Number of rows processed (`successful + failed`).
- `successful` (integer, required) — Number of rows that validated successfully (`dry_run=true`) or were created (`dry_run=false`).
- `failed` (integer, required) — Number of rows that were invalid or could not be created. Equals `failures` length in create mode.
- `results` (array, required) — Per-row classification of validate-only mode (`dry_run=true`). Empty in create mode.
- `failures` (array, required) — Rows that could not be created (create mode, `dry_run=false`). Each item carries the 0-based `index`, an `error_code`, a Spanish `error_message` and the per-field `errors[]`. Empty in validate-only mode.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/clients/bulk-delete — Delete multiple clients in bulk
- **Operation ID**: `public-api.v1.clients.bulk_delete`
- **Tag**: Clients
- **Required scope**: `clients:delete` — Delete clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.bulk_delete
Delete up to 200 clients in one request. Returns a `BulkPartialSuccessResult` with `total`, `successful` and `failed` counts plus a `failures` list (`id` + `error_code` + Spanish `error_message`); clients with associated documents are reported in `failures`.
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 1 property; 1 required: `ids`.
Delete several clients in one request. `ids` is an array of 1 to 200 UUIDs; identifiers that do not belong to your company are reported under `failed` rather than failing the whole request.
- `ids` (array, required, maxItems 200)
## Responses
- **200**
- Body (`application/json`):
- `data` (object (BulkPartialSuccessResult), required) — Result of a bulk or import operation that reports per-resource status. `total` is how many rows/resources were processed (`successful + failed`), `successful` how many were applied (deleted, created or validated) and `failed` how many could not be processed. `failures[]` carries one item per failed row. Shape shared by every bulk endpoint of the public API (the `/v1/{resource}/bulk-delete` endpoints emit it today). Anchored integrators before `2026-09-01` keep receiving the previous `{object, deleted, failed[{id, reason}]}` shape via `Factuarea-Version`.
- `total` (integer, required) — Number of rows/resources processed (`successful + failed`).
- `successful` (integer, required) — Number of rows/resources processed successfully (deleted, created or validated).
- `failed` (integer, required) — Number of rows/resources that could not be processed. Equals `failures` length.
- `failures` (array, required) — One item per failed row/resource. Always a list (empty, never `null`, when there are no failures).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/clients — Create a client
- **Operation ID**: `public-api.v1.clients.create`
- **Tag**: Clients
- **Required scope**: `clients:write` — Create and update clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.create
Create a new client (customer) for your company. The returned object includes the generated `uuid` you should store for subsequent operations.
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 30 properties; 1 required: `name`.
- `name` (string, required, maxLength 200)
- `commercial_name` (string | null, optional, maxLength 255)
- `tax_id` (string | null, optional, maxLength 20)
- `vat_id` (string | null, optional, maxLength 20)
- `email` (string | null, optional, format: email, maxLength 191)
- `phone` (string | null, optional, maxLength 20, pattern: `^\+?[0-9\s\-()]{6,20}$`)
- `fax` (string | null, optional, maxLength 20)
- `mobile` (string | null, optional, maxLength 20)
- `website` (string | null, optional, format: uri, maxLength 255)
- `contact_person` (string | null, optional, maxLength 200)
- `latitude` (number | null, optional, min -90, max 90)
- `longitude` (number | null, optional, min -180, max 180)
- `default_discount` (number | null, optional, min 0, max 100)
- `default_vat_rate` (number | null, optional, min 0, max 100)
- `default_retention_rate` (number | null, optional, min -100, max 0)
- `is_surcharge_subject` (boolean | null, optional)
- `accumulate_347` (boolean, optional)
- `preferred_operation_regime` (string | null, optional, enum: `general`, `intracomunitaria`, `importacion_exportacion`, `isp`)
- `payment_method` (string | null, optional, enum: `bank_transfer`, `direct_debit`, `cash`, `credit_card`, `check`, `paypal`, `other`)
- `payment_terms_days` (integer | null, optional, min 0, max 365)
- `notes` (string | null, optional, maxLength 1000)
- `metadata` (object (Metadata) | null, optional) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `dir3_accounting_office` (string | null, optional)
- `dir3_managing_body` (string | null, optional)
- `dir3_processing_unit` (string | null, optional)
- `external_id` (string | null, optional, maxLength 100)
- `billing_emails` (array | null, optional, maxItems 5)
- `alternative_id` (object, optional)
- `type` (string, optional, enum: `nif_iva`, `passport`, `country_id`, `residence_certificate`, `other_document`, `not_registered`, `tax_id_foreign`, `national_id`) — Alternative identifier type from the AEAT L7 catalog. Legacy aliases (`tax_id_foreign`/`national_id`) are accepted on input for backward compatibility.
- `value` (string, optional, maxLength 50, minLength 1)
- `country_code` (string, optional, maxLength 2, minLength 2, pattern: `^[A-Z]{2}$`)
- `address` (object, optional)
- `line1` (string | null, optional, maxLength 500)
- `line2` (string | null, optional, maxLength 100)
- `number` (string | null, optional, maxLength 100)
- `floor` (string | null, optional, maxLength 100)
- `door` (string | null, optional, maxLength 100)
- `staircase` (string | null, optional, maxLength 100)
- `postal_code` (string | null, optional, maxLength 10)
- `city` (string | null, optional, maxLength 100)
- `province` (string | null, optional, maxLength 100)
- `country` (string | null, optional, maxLength 2, minLength 2)
- `bank_accounts` (array | null, optional)
- `iban` (string, required, maxLength 50, pattern: `^[A-Za-z]{2}[0-9]{2}[A-Za-z0-9 ]{11,42}$`)
- `bic` (string | null, optional, maxLength 20, pattern: `^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$`)
- `is_default` (boolean | null, optional)
- `notes` (string | null, optional, maxLength 255)
## Responses
- **201** — Client created successfully. The `Location` header contains the canonical URL of the newly created resource.
- Body (`application/json`):
- `data` (object (Client), required) — A customer of your company.
- `id` (string, required)
- `object` (string, required, enum: `client`)
- `name` (string, required)
- `commercial_name` (string | null, optional) — Optional trade name (DBA), distinct from the legal name.
- `tax_id` (string | null, required, pattern: `^(\d{8}[A-Z]|[XYZ]\d{7}[A-Z]|[A-Z]\d{7}[A-Z0-9])$`) — Spanish fiscal identifier (NIF, CIF, NIE). Structural format: NIF `^\d{8}[A-Z]$`, NIE `^[XYZ]\d{7}[A-Z]$`, CIF `^[A-Z]\d{7}[A-Z0-9]$`. AEAT control-digit (checksum) validation is enforced when the request opts in via the `Factuarea-Version` header on or after the activation version; without that opt-in the legacy permissive behaviour is preserved.
- `vat_id` (string | null, required) — EU VAT identifier.
- `email` (string | null, required, format: email)
- `phone` (string | null, required)
- `fax` (string | null, optional) — Fax number (rarely used, legacy).
- `mobile` (string | null, optional) — Mobile number.
- `website` (string | null, optional, format: uri) — Client website.
- `contact_person` (string | null, optional, maxLength 200) — B2B contact person.
- `billing_emails` (array, optional, maxItems 5) — Additional emails for invoice delivery (administration, accounting). Maximum 5.
- `address` (object (Address), required)
- `coordinates` (object | null, optional) — Geographic coordinates of the client. `null` when not recorded.
- `default_discount` (number | null, optional, format: float) — Default discount applied to the client (percentage).
- `default_vat_rate` (number | null, optional, format: float) — Default VAT rate applied to the client (percentage).
- `default_retention_rate` (number | null, optional, format: float) — Default IRPF withholding rate (percentage).
- `is_surcharge_subject` (boolean, optional) — Indica si al cliente se le aplica recargo de equivalencia.
- `bank_accounts` (array, optional) — Bank accounts associated with the client. Empty `[]` when there are none.
- `preferred_operation_regime` (string | null, optional, enum: `general`, `intracomunitaria`, `importacion_exportacion`, `isp`, `null`) — Preferred operation regime of the client for VAT / VeriFactu purposes.
- `accumulate_347` (boolean, required) — Whether this client accumulates towards the annual Modelo 347 report (operations with third parties above the legal threshold).
- `alternative_id` (object (AlternativeId) | null, optional)
- `payment_preferences` (object (PaymentPreferences) | null, optional)
- `dir3_accounting_office` (string | null, required) — DIR3 code of the public-administration accounting office (Oficina Contable). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `dir3_managing_body` (string | null, required) — DIR3 code of the public-administration managing body (Órgano Gestor). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `dir3_processing_unit` (string | null, required) — DIR3 code of the public-administration processing unit (Unidad Tramitadora). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `external_id` (string | null, optional, maxLength 100) — External integration key (ERP/CRM/e-commerce) mapping this client to a record in a third-party system. Free-format, unique per company, distinct from the fiscal `tax_id`.
- `notes` (string | null, required)
- `metadata` (object (Metadata) | null, required) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `is_active` (boolean, required)
- `created_at` (string | null, required, format: date-time)
- `updated_at` (string | null, required, format: date-time)
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# DELETE /v1/clients/{client} — Delete a client
- **Operation ID**: `public-api.v1.clients.delete`
- **Tag**: Clients
- **Required scope**: `clients:delete` — Delete clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.delete
Delete a client. Returns 422 if the client is referenced by any document (invoice, quote, etc.).
## Path parameters
- `client` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **204** — No content
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/clients/find-by-external-id — Find a client by external ID
- **Operation ID**: `public-api.v1.clients.find_by_external_id`
- **Tag**: Clients
- **Required scope**: `clients:read` — Read clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.find_by_external_id
Look up a client by their `external_id` (sent in the JSON body), the integration key that maps them to a record in a third-party system (ERP/CRM/e-commerce). Distinct from the fiscal `tax_id`. Returns the matching client or 404 if no client uses that external_id within your company.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 1 property; 1 required: `external_id`.
Look up a client by its `external_id` (the integration key that maps it to a record in a third-party ERP/CRM/e-commerce system) within your company.
- `external_id` (string, required, maxLength 100)
## Responses
- **200**
- Body (`application/json`):
- `data` (object (Client), required) — A customer of your company.
- `id` (string, required)
- `object` (string, required, enum: `client`)
- `name` (string, required)
- `commercial_name` (string | null, optional) — Optional trade name (DBA), distinct from the legal name.
- `tax_id` (string | null, required, pattern: `^(\d{8}[A-Z]|[XYZ]\d{7}[A-Z]|[A-Z]\d{7}[A-Z0-9])$`) — Spanish fiscal identifier (NIF, CIF, NIE). Structural format: NIF `^\d{8}[A-Z]$`, NIE `^[XYZ]\d{7}[A-Z]$`, CIF `^[A-Z]\d{7}[A-Z0-9]$`. AEAT control-digit (checksum) validation is enforced when the request opts in via the `Factuarea-Version` header on or after the activation version; without that opt-in the legacy permissive behaviour is preserved.
- `vat_id` (string | null, required) — EU VAT identifier.
- `email` (string | null, required, format: email)
- `phone` (string | null, required)
- `fax` (string | null, optional) — Fax number (rarely used, legacy).
- `mobile` (string | null, optional) — Mobile number.
- `website` (string | null, optional, format: uri) — Client website.
- `contact_person` (string | null, optional, maxLength 200) — B2B contact person.
- `billing_emails` (array, optional, maxItems 5) — Additional emails for invoice delivery (administration, accounting). Maximum 5.
- `address` (object (Address), required)
- `coordinates` (object | null, optional) — Geographic coordinates of the client. `null` when not recorded.
- `default_discount` (number | null, optional, format: float) — Default discount applied to the client (percentage).
- `default_vat_rate` (number | null, optional, format: float) — Default VAT rate applied to the client (percentage).
- `default_retention_rate` (number | null, optional, format: float) — Default IRPF withholding rate (percentage).
- `is_surcharge_subject` (boolean, optional) — Indica si al cliente se le aplica recargo de equivalencia.
- `bank_accounts` (array, optional) — Bank accounts associated with the client. Empty `[]` when there are none.
- `preferred_operation_regime` (string | null, optional, enum: `general`, `intracomunitaria`, `importacion_exportacion`, `isp`, `null`) — Preferred operation regime of the client for VAT / VeriFactu purposes.
- `accumulate_347` (boolean, required) — Whether this client accumulates towards the annual Modelo 347 report (operations with third parties above the legal threshold).
- `alternative_id` (object (AlternativeId) | null, optional)
- `payment_preferences` (object (PaymentPreferences) | null, optional)
- `dir3_accounting_office` (string | null, required) — DIR3 code of the public-administration accounting office (Oficina Contable). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `dir3_managing_body` (string | null, required) — DIR3 code of the public-administration managing body (Órgano Gestor). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `dir3_processing_unit` (string | null, required) — DIR3 code of the public-administration processing unit (Unidad Tramitadora). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `external_id` (string | null, optional, maxLength 100) — External integration key (ERP/CRM/e-commerce) mapping this client to a record in a third-party system. Free-format, unique per company, distinct from the fiscal `tax_id`.
- `notes` (string | null, required)
- `metadata` (object (Metadata) | null, required) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `is_active` (boolean, required)
- `created_at` (string | null, required, format: date-time)
- `updated_at` (string | null, required, format: date-time)
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/clients/find-by-tax-id — Find a client by tax ID
- **Operation ID**: `public-api.v1.clients.find_by_tax_id`
- **Tag**: Clients
- **Required scope**: `clients:read` — Read clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.find_by_tax_id
Look up a client by their Spanish tax identifier (NIF/CIF/NIE). Returns the matching client or 404 if no client uses that tax_id within your company.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 1 property; 1 required: `tax_id`.
Look up a client by its Spanish tax ID (NIF/CIF/NIE) within your company.
- `tax_id` (string, required, maxLength 50)
## Responses
- **200**
- Body (`application/json`):
- `data` (object (Client), required) — A customer of your company.
- `id` (string, required)
- `object` (string, required, enum: `client`)
- `name` (string, required)
- `commercial_name` (string | null, optional) — Optional trade name (DBA), distinct from the legal name.
- `tax_id` (string | null, required, pattern: `^(\d{8}[A-Z]|[XYZ]\d{7}[A-Z]|[A-Z]\d{7}[A-Z0-9])$`) — Spanish fiscal identifier (NIF, CIF, NIE). Structural format: NIF `^\d{8}[A-Z]$`, NIE `^[XYZ]\d{7}[A-Z]$`, CIF `^[A-Z]\d{7}[A-Z0-9]$`. AEAT control-digit (checksum) validation is enforced when the request opts in via the `Factuarea-Version` header on or after the activation version; without that opt-in the legacy permissive behaviour is preserved.
- `vat_id` (string | null, required) — EU VAT identifier.
- `email` (string | null, required, format: email)
- `phone` (string | null, required)
- `fax` (string | null, optional) — Fax number (rarely used, legacy).
- `mobile` (string | null, optional) — Mobile number.
- `website` (string | null, optional, format: uri) — Client website.
- `contact_person` (string | null, optional, maxLength 200) — B2B contact person.
- `billing_emails` (array, optional, maxItems 5) — Additional emails for invoice delivery (administration, accounting). Maximum 5.
- `address` (object (Address), required)
- `coordinates` (object | null, optional) — Geographic coordinates of the client. `null` when not recorded.
- `default_discount` (number | null, optional, format: float) — Default discount applied to the client (percentage).
- `default_vat_rate` (number | null, optional, format: float) — Default VAT rate applied to the client (percentage).
- `default_retention_rate` (number | null, optional, format: float) — Default IRPF withholding rate (percentage).
- `is_surcharge_subject` (boolean, optional) — Indica si al cliente se le aplica recargo de equivalencia.
- `bank_accounts` (array, optional) — Bank accounts associated with the client. Empty `[]` when there are none.
- `preferred_operation_regime` (string | null, optional, enum: `general`, `intracomunitaria`, `importacion_exportacion`, `isp`, `null`) — Preferred operation regime of the client for VAT / VeriFactu purposes.
- `accumulate_347` (boolean, required) — Whether this client accumulates towards the annual Modelo 347 report (operations with third parties above the legal threshold).
- `alternative_id` (object (AlternativeId) | null, optional)
- `payment_preferences` (object (PaymentPreferences) | null, optional)
- `dir3_accounting_office` (string | null, required) — DIR3 code of the public-administration accounting office (Oficina Contable). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `dir3_managing_body` (string | null, required) — DIR3 code of the public-administration managing body (Órgano Gestor). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `dir3_processing_unit` (string | null, required) — DIR3 code of the public-administration processing unit (Unidad Tramitadora). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `external_id` (string | null, optional, maxLength 100) — External integration key (ERP/CRM/e-commerce) mapping this client to a record in a third-party system. Free-format, unique per company, distinct from the fiscal `tax_id`.
- `notes` (string | null, required)
- `metadata` (object (Metadata) | null, required) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `is_active` (boolean, required)
- `created_at` (string | null, required, format: date-time)
- `updated_at` (string | null, required, format: date-time)
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/clients/import — Import clients from a file
- **Operation ID**: `public-api.v1.clients.import`
- **Tag**: Clients
- **Required scope**: `clients:write` — Create and update clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.import
Import clients in bulk from a CSV/XLSX file as `multipart/form-data`; processing is synchronous and the response carries the per-row outcome. Upload with `dry_run=true` first to validate without persisting, fix the reported `failures[]`, then re-upload with `dry_run=false` to create only the valid rows. `mapping` maps your column headers to the target fields (`name` and `tax_id` are mandatory). Download the header template from `GET /v1/clients/import-template`. ```json { "dry_run": true, "mapping": { "Nombre": "name", "CIF": "tax_id", "Email": "email" } } ``` Limits: file ≤10 MB and under 200 rows; a larger file returns 422 `client_import_too_large`.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`multipart/form-data`, required. 3 properties; 2 required: `file`, `mapping`.
Import clients from a file as `multipart/form-data`. `file` is a CSV/XLSX/XLS/ODS/TXT document (≤10 MB); `mapping` maps your column headers to target fields and must include at least `name` and `tax_id`; `dry_run` (default `false`) validates the file and returns a per-row preview without persisting.
- `file` (string, required, format: binary, maxLength 10240) — CSV/XLSX/XLS/ODS/TXT file with the clients to import (max 10 MB).
- `dry_run` (boolean | null, optional) — If `true`, validates the file and returns a per-row preview WITHOUT persisting any client. Defaults to `false`.
- `mapping` (object, required) — Mapeo de columnas `{cabecera_csv: campo_destino}`. Debe declarar al menos `name` y `tax_id`.
## Responses
- **200**
- Body (`application/json`):
- `data` (object (ClientImportPreview), required) — Validate-only preview of a client import (`POST /v1/clients/import` with `dry_run=true`). The CSV/XLSX file is checked row by row without persisting anything: `rows[]` carries the per-row classification `{row, status, errors[], warnings[]}` identified by the 1-based file `row` number (the header is row 1).
- `object` (string, required, enum: `client_import_preview`) — Stripe-like discriminator. Always `client_import_preview` for this resource.
- `total_rows` (integer, required) — Number of data rows in the uploaded file (excluding the header).
- `rows` (array, required) — Per-row validation of the file. One item per data row, in file order.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/clients/import/template — Download the client import template
- **Operation ID**: `public-api.v1.clients.import_template`
- **Tag**: Clients
- **Required scope**: `clients:read` — Read clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.import_template
Download the CSV template (Spanish headers + two example rows) to fill in before uploading it to `POST /v1/clients/import`. The content is static and accesses no company data. Returns a `text/csv` stream as an attachment.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- object
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/clients — List all clients
- **Operation ID**: `public-api.v1.clients.list`
- **Tag**: Clients
- **Required scope**: `clients:read` — Read clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.list
List your clients with cursor-based pagination. Supports filtering by `is_active`, `created_at[gte|lte]`, and `name[in]`.
## Query parameters
- `limit` (integer, optional, min 1, max 100, default: `25`) — Number of objects to return.
- `starting_after` (string, optional, format: uuid) — Cursor for forward pagination.
- `ending_before` (string, optional, format: uuid) — Cursor for backward pagination.
- `tax_id` (string, optional) — Fiscal tax number (NIF/CIF/NIE) of the client.
- `tax_id[in]` (string, optional) — Fiscal tax number (NIF/CIF/NIE) of the client.
- `email` (string, optional, format: email) — Client email.
- `name` (string, optional) — Trade name of the client.
- `city` (string, optional) — City of the client postal address.
- `city[contains]` (string, optional) — City of the client postal address.
- `province` (string, optional) — Province / region of the client postal address.
- `province[contains]` (string, optional) — Province / region of the client postal address.
- `phone` (string, optional) — Phone number of the client.
- `phone[contains]` (string, optional) — Phone number of the client.
- `is_active` (boolean, optional) — Filter by active / inactive clients.
- `created[gte]` (string, optional, format: date-time) — Creation date (ISO 8601).
- `created[lte]` (string, optional, format: date-time) — Creation date (ISO 8601).
- `created[gt]` (string, optional, format: date-time) — Creation date (ISO 8601).
- `created[lt]` (string, optional, format: date-time) — Creation date (ISO 8601).
- `search` (string, optional, maxLength 80) — Free-text search.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (array, required)
- `id` (string, required)
- `object` (string, required, enum: `client`)
- `name` (string, required)
- `commercial_name` (string | null, optional) — Optional trade name (DBA), distinct from the legal name.
- `tax_id` (string | null, required, pattern: `^(\d{8}[A-Z]|[XYZ]\d{7}[A-Z]|[A-Z]\d{7}[A-Z0-9])$`) — Spanish fiscal identifier (NIF, CIF, NIE). Structural format: NIF `^\d{8}[A-Z]$`, NIE `^[XYZ]\d{7}[A-Z]$`, CIF `^[A-Z]\d{7}[A-Z0-9]$`. AEAT control-digit (checksum) validation is enforced when the request opts in via the `Factuarea-Version` header on or after the activation version; without that opt-in the legacy permissive behaviour is preserved.
- `vat_id` (string | null, required) — EU VAT identifier.
- `email` (string | null, required, format: email)
- `phone` (string | null, required)
- `fax` (string | null, optional) — Fax number (rarely used, legacy).
- `mobile` (string | null, optional) — Mobile number.
- `website` (string | null, optional, format: uri) — Client website.
- `contact_person` (string | null, optional, maxLength 200) — B2B contact person.
- `billing_emails` (array, optional, maxItems 5) — Additional emails for invoice delivery (administration, accounting). Maximum 5.
- `address` (object (Address), required)
- `coordinates` (object | null, optional) — Geographic coordinates of the client. `null` when not recorded.
- `default_discount` (number | null, optional, format: float) — Default discount applied to the client (percentage).
- `default_vat_rate` (number | null, optional, format: float) — Default VAT rate applied to the client (percentage).
- `default_retention_rate` (number | null, optional, format: float) — Default IRPF withholding rate (percentage).
- `is_surcharge_subject` (boolean, optional) — Indica si al cliente se le aplica recargo de equivalencia.
- `bank_accounts` (array, optional) — Bank accounts associated with the client. Empty `[]` when there are none.
- `preferred_operation_regime` (string | null, optional, enum: `general`, `intracomunitaria`, `importacion_exportacion`, `isp`, `null`) — Preferred operation regime of the client for VAT / VeriFactu purposes.
- `accumulate_347` (boolean, required) — Whether this client accumulates towards the annual Modelo 347 report (operations with third parties above the legal threshold).
- `alternative_id` (object (AlternativeId) | null, optional)
- `payment_preferences` (object (PaymentPreferences) | null, optional)
- `dir3_accounting_office` (string | null, required) — DIR3 code of the public-administration accounting office (Oficina Contable). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `dir3_managing_body` (string | null, required) — DIR3 code of the public-administration managing body (Órgano Gestor). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `dir3_processing_unit` (string | null, required) — DIR3 code of the public-administration processing unit (Unidad Tramitadora). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `external_id` (string | null, optional, maxLength 100) — External integration key (ERP/CRM/e-commerce) mapping this client to a record in a third-party system. Free-format, unique per company, distinct from the fiscal `tax_id`.
- `notes` (string | null, required)
- `metadata` (object (Metadata) | null, required) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `is_active` (boolean, required)
- `created_at` (string | null, required, format: date-time)
- `updated_at` (string | null, required, format: date-time)
- `has_more` (boolean, required) — Whether more pages are available after this one.
- `next_cursor` (string | null, required) — Opaque cursor for the next page, or `null` when `has_more` is `false`. The example below shows the shape used by most listings; others return a different one, so pass the value back verbatim instead of validating it.
- **400** — The request is syntactically malformed — e.g. an unknown query parameter, an integer parameter with non-numeric value, or a value outside the documented range.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/clients/search — Search clients
- **Operation ID**: `public-api.v1.clients.search`
- **Tag**: Clients
- **Required scope**: `clients:read` — Read clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.search
Search clients by free-text query against `name`, `tax_id`, `vat_id`, `email`, and `phone`. Returns a flat array (no pagination) capped at 50 results.
## Query parameters
- `q` (string, required, maxLength 120, minLength 1)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (array, required)
- `id` (string, required)
- `object` (string, required, enum: `client`)
- `name` (string, required)
- `commercial_name` (string | null, optional) — Optional trade name (DBA), distinct from the legal name.
- `tax_id` (string | null, required, pattern: `^(\d{8}[A-Z]|[XYZ]\d{7}[A-Z]|[A-Z]\d{7}[A-Z0-9])$`) — Spanish fiscal identifier (NIF, CIF, NIE). Structural format: NIF `^\d{8}[A-Z]$`, NIE `^[XYZ]\d{7}[A-Z]$`, CIF `^[A-Z]\d{7}[A-Z0-9]$`. AEAT control-digit (checksum) validation is enforced when the request opts in via the `Factuarea-Version` header on or after the activation version; without that opt-in the legacy permissive behaviour is preserved.
- `vat_id` (string | null, required) — EU VAT identifier.
- `email` (string | null, required, format: email)
- `phone` (string | null, required)
- `fax` (string | null, optional) — Fax number (rarely used, legacy).
- `mobile` (string | null, optional) — Mobile number.
- `website` (string | null, optional, format: uri) — Client website.
- `contact_person` (string | null, optional, maxLength 200) — B2B contact person.
- `billing_emails` (array, optional, maxItems 5) — Additional emails for invoice delivery (administration, accounting). Maximum 5.
- `address` (object (Address), required)
- `coordinates` (object | null, optional) — Geographic coordinates of the client. `null` when not recorded.
- `default_discount` (number | null, optional, format: float) — Default discount applied to the client (percentage).
- `default_vat_rate` (number | null, optional, format: float) — Default VAT rate applied to the client (percentage).
- `default_retention_rate` (number | null, optional, format: float) — Default IRPF withholding rate (percentage).
- `is_surcharge_subject` (boolean, optional) — Indica si al cliente se le aplica recargo de equivalencia.
- `bank_accounts` (array, optional) — Bank accounts associated with the client. Empty `[]` when there are none.
- `preferred_operation_regime` (string | null, optional, enum: `general`, `intracomunitaria`, `importacion_exportacion`, `isp`, `null`) — Preferred operation regime of the client for VAT / VeriFactu purposes.
- `accumulate_347` (boolean, required) — Whether this client accumulates towards the annual Modelo 347 report (operations with third parties above the legal threshold).
- `alternative_id` (object (AlternativeId) | null, optional)
- `payment_preferences` (object (PaymentPreferences) | null, optional)
- `dir3_accounting_office` (string | null, required) — DIR3 code of the public-administration accounting office (Oficina Contable). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `dir3_managing_body` (string | null, required) — DIR3 code of the public-administration managing body (Órgano Gestor). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `dir3_processing_unit` (string | null, required) — DIR3 code of the public-administration processing unit (Unidad Tramitadora). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `external_id` (string | null, optional, maxLength 100) — External integration key (ERP/CRM/e-commerce) mapping this client to a record in a third-party system. Free-format, unique per company, distinct from the fiscal `tax_id`.
- `notes` (string | null, required)
- `metadata` (object (Metadata) | null, required) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `is_active` (boolean, required)
- `created_at` (string | null, required, format: date-time)
- `updated_at` (string | null, required, format: date-time)
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/clients/{client} — Retrieve a client
- **Operation ID**: `public-api.v1.clients.show`
- **Tag**: Clients
- **Required scope**: `clients:read` — Read clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.show
Retrieve a client by its `uuid`. Returns 404 if the client does not exist or belongs to another company.
## Path parameters
- `client` (string, required)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (Client), required) — A customer of your company.
- `id` (string, required)
- `object` (string, required, enum: `client`)
- `name` (string, required)
- `commercial_name` (string | null, optional) — Optional trade name (DBA), distinct from the legal name.
- `tax_id` (string | null, required, pattern: `^(\d{8}[A-Z]|[XYZ]\d{7}[A-Z]|[A-Z]\d{7}[A-Z0-9])$`) — Spanish fiscal identifier (NIF, CIF, NIE). Structural format: NIF `^\d{8}[A-Z]$`, NIE `^[XYZ]\d{7}[A-Z]$`, CIF `^[A-Z]\d{7}[A-Z0-9]$`. AEAT control-digit (checksum) validation is enforced when the request opts in via the `Factuarea-Version` header on or after the activation version; without that opt-in the legacy permissive behaviour is preserved.
- `vat_id` (string | null, required) — EU VAT identifier.
- `email` (string | null, required, format: email)
- `phone` (string | null, required)
- `fax` (string | null, optional) — Fax number (rarely used, legacy).
- `mobile` (string | null, optional) — Mobile number.
- `website` (string | null, optional, format: uri) — Client website.
- `contact_person` (string | null, optional, maxLength 200) — B2B contact person.
- `billing_emails` (array, optional, maxItems 5) — Additional emails for invoice delivery (administration, accounting). Maximum 5.
- `address` (object (Address), required)
- `coordinates` (object | null, optional) — Geographic coordinates of the client. `null` when not recorded.
- `default_discount` (number | null, optional, format: float) — Default discount applied to the client (percentage).
- `default_vat_rate` (number | null, optional, format: float) — Default VAT rate applied to the client (percentage).
- `default_retention_rate` (number | null, optional, format: float) — Default IRPF withholding rate (percentage).
- `is_surcharge_subject` (boolean, optional) — Indica si al cliente se le aplica recargo de equivalencia.
- `bank_accounts` (array, optional) — Bank accounts associated with the client. Empty `[]` when there are none.
- `preferred_operation_regime` (string | null, optional, enum: `general`, `intracomunitaria`, `importacion_exportacion`, `isp`, `null`) — Preferred operation regime of the client for VAT / VeriFactu purposes.
- `accumulate_347` (boolean, required) — Whether this client accumulates towards the annual Modelo 347 report (operations with third parties above the legal threshold).
- `alternative_id` (object (AlternativeId) | null, optional)
- `payment_preferences` (object (PaymentPreferences) | null, optional)
- `dir3_accounting_office` (string | null, required) — DIR3 code of the public-administration accounting office (Oficina Contable). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `dir3_managing_body` (string | null, required) — DIR3 code of the public-administration managing body (Órgano Gestor). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `dir3_processing_unit` (string | null, required) — DIR3 code of the public-administration processing unit (Unidad Tramitadora). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `external_id` (string | null, optional, maxLength 100) — External integration key (ERP/CRM/e-commerce) mapping this client to a record in a third-party system. Free-format, unique per company, distinct from the fiscal `tax_id`.
- `notes` (string | null, required)
- `metadata` (object (Metadata) | null, required) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `is_active` (boolean, required)
- `created_at` (string | null, required, format: date-time)
- `updated_at` (string | null, required, format: date-time)
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/clients/stats — Get client stats
- **Operation ID**: `public-api.v1.clients.stats`
- **Tag**: Clients
- **Required scope**: `clients:read` — Read clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.stats
Aggregated KPIs for the authenticated company: total client count, active count, count with sales invoices, count with quotes, and totals by document type. Returned as `{ "data": ClientStats }`.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (ClientStats), required) — Aggregated summary of the client portfolio of the authenticated company: counters, invoiced and pending amounts, and average metrics. Returned by `GET /v1/clients/stats`.
- `object` (string, required, enum: `client_stats`)
- `total` (integer, required) — Total number of clients registered in the company.
- `active` (integer, required) — Clientes marcados como activos.
- `with_email` (integer, required) — Clients with a registered email address.
- `with_phone` (integer, required) — Clients with a registered phone number.
- `with_invoices` (integer, required) — Clients with at least one issued invoice.
- `total_invoiced` (number, required) — Total amount invoiced to clients (EUR).
- `total_pending` (number, required) — Total amount pending collection (EUR).
- `with_pending_invoices` (integer, required) — Clients with at least one invoice pending collection.
- `pending_invoices_amount` (number, required) — Aggregate amount of invoices pending collection (EUR).
- `new_this_month` (integer, required) — Clientes nuevos creados durante el mes en curso.
- `inactive_clients` (integer, required) — Clientes marcados como inactivos.
- `average_per_client` (number, required) — Average amount invoiced per client (EUR).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# PUT /v1/clients/{client} — Update a client
- **Operation ID**: `public-api.v1.clients.update`
- **Tag**: Clients
- **Required scope**: `clients:write` — Create and update clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.update
Update a client. Only fields included in the payload are modified; omitted fields retain their previous values.
## Path parameters
- `client` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, optional. 30 properties; none of them required.
Public REST API v1 — PUT /v1/clients/{uuid}. Partial PUT update: all fields are `sometimes`. If not sent, the handler keeps the current value. If sent as `null`, the field is cleared (when the domain allows it). Accepts the same fields as `CreateClientRequest` V1 (see docblock there). The validation of domain invariants (XOR `tax_id`/`alternative_id`, direct_debit ⇒ default bank account, billing_emails without duplicates) is performed by the `Client` aggregate. The typed exceptions propagate to the `ExceptionRenderer` with the canonical v1 envelope.
- `name` (string, optional, maxLength 200)
- `commercial_name` (string | null, optional, maxLength 255)
- `tax_id` (string | null, optional, maxLength 20)
- `vat_id` (string | null, optional, maxLength 20)
- `email` (string | null, optional, format: email, maxLength 191)
- `phone` (string | null, optional, maxLength 20, pattern: `^\+?[0-9\s\-()]{6,20}$`)
- `fax` (string | null, optional, maxLength 20)
- `mobile` (string | null, optional, maxLength 20)
- `website` (string | null, optional, format: uri, maxLength 255)
- `contact_person` (string | null, optional, maxLength 200)
- `latitude` (number | null, optional, min -90, max 90)
- `longitude` (number | null, optional, min -180, max 180)
- `default_discount` (number | null, optional, min 0, max 100)
- `default_vat_rate` (number | null, optional, min 0, max 100)
- `default_retention_rate` (number | null, optional, min -100, max 0)
- `is_surcharge_subject` (boolean | null, optional)
- `accumulate_347` (boolean, optional)
- `preferred_operation_regime` (string | null, optional, enum: `general`, `intracomunitaria`, `importacion_exportacion`, `isp`)
- `payment_method` (string | null, optional, enum: `bank_transfer`, `direct_debit`, `cash`, `credit_card`, `check`, `paypal`, `other`)
- `payment_terms_days` (integer | null, optional, min 0, max 365)
- `notes` (string | null, optional, maxLength 1000)
- `metadata` (object (Metadata) | null, optional) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `dir3_accounting_office` (string | null, optional)
- `dir3_managing_body` (string | null, optional)
- `dir3_processing_unit` (string | null, optional)
- `external_id` (string | null, optional, maxLength 100)
- `billing_emails` (array | null, optional, maxItems 5)
- `alternative_id` (object, optional)
- `type` (string, optional, enum: `nif_iva`, `passport`, `country_id`, `residence_certificate`, `other_document`, `not_registered`, `tax_id_foreign`, `national_id`) — Alternative identifier type from the AEAT L7 catalog. Legacy aliases (`tax_id_foreign`/`national_id`) are accepted on input for backward compatibility.
- `value` (string, optional, maxLength 50, minLength 1)
- `country_code` (string, optional, maxLength 2, minLength 2, pattern: `^[A-Z]{2}$`)
- `address` (object, optional)
- `line1` (string | null, optional, maxLength 500)
- `line2` (string | null, optional, maxLength 100)
- `number` (string | null, optional, maxLength 100)
- `floor` (string | null, optional, maxLength 100)
- `door` (string | null, optional, maxLength 100)
- `staircase` (string | null, optional, maxLength 100)
- `postal_code` (string | null, optional, maxLength 10)
- `city` (string | null, optional, maxLength 100)
- `province` (string | null, optional, maxLength 100)
- `country` (string | null, optional, maxLength 2, minLength 2)
- `bank_accounts` (array | null, optional)
- `iban` (string, required, maxLength 50, pattern: `^[A-Za-z]{2}[0-9]{2}[A-Za-z0-9 ]{11,42}$`)
- `bic` (string | null, optional, maxLength 20, pattern: `^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$`)
- `is_default` (boolean | null, optional)
- `notes` (string | null, optional, maxLength 255)
## Responses
- **200**
- Body (`application/json`):
- `data` (object (Client), required) — A customer of your company.
- `id` (string, required)
- `object` (string, required, enum: `client`)
- `name` (string, required)
- `commercial_name` (string | null, optional) — Optional trade name (DBA), distinct from the legal name.
- `tax_id` (string | null, required, pattern: `^(\d{8}[A-Z]|[XYZ]\d{7}[A-Z]|[A-Z]\d{7}[A-Z0-9])$`) — Spanish fiscal identifier (NIF, CIF, NIE). Structural format: NIF `^\d{8}[A-Z]$`, NIE `^[XYZ]\d{7}[A-Z]$`, CIF `^[A-Z]\d{7}[A-Z0-9]$`. AEAT control-digit (checksum) validation is enforced when the request opts in via the `Factuarea-Version` header on or after the activation version; without that opt-in the legacy permissive behaviour is preserved.
- `vat_id` (string | null, required) — EU VAT identifier.
- `email` (string | null, required, format: email)
- `phone` (string | null, required)
- `fax` (string | null, optional) — Fax number (rarely used, legacy).
- `mobile` (string | null, optional) — Mobile number.
- `website` (string | null, optional, format: uri) — Client website.
- `contact_person` (string | null, optional, maxLength 200) — B2B contact person.
- `billing_emails` (array, optional, maxItems 5) — Additional emails for invoice delivery (administration, accounting). Maximum 5.
- `address` (object (Address), required)
- `coordinates` (object | null, optional) — Geographic coordinates of the client. `null` when not recorded.
- `default_discount` (number | null, optional, format: float) — Default discount applied to the client (percentage).
- `default_vat_rate` (number | null, optional, format: float) — Default VAT rate applied to the client (percentage).
- `default_retention_rate` (number | null, optional, format: float) — Default IRPF withholding rate (percentage).
- `is_surcharge_subject` (boolean, optional) — Indica si al cliente se le aplica recargo de equivalencia.
- `bank_accounts` (array, optional) — Bank accounts associated with the client. Empty `[]` when there are none.
- `preferred_operation_regime` (string | null, optional, enum: `general`, `intracomunitaria`, `importacion_exportacion`, `isp`, `null`) — Preferred operation regime of the client for VAT / VeriFactu purposes.
- `accumulate_347` (boolean, required) — Whether this client accumulates towards the annual Modelo 347 report (operations with third parties above the legal threshold).
- `alternative_id` (object (AlternativeId) | null, optional)
- `payment_preferences` (object (PaymentPreferences) | null, optional)
- `dir3_accounting_office` (string | null, required) — DIR3 code of the public-administration accounting office (Oficina Contable). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `dir3_managing_body` (string | null, required) — DIR3 code of the public-administration managing body (Órgano Gestor). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `dir3_processing_unit` (string | null, required) — DIR3 code of the public-administration processing unit (Unidad Tramitadora). FACe/Facturae directory code, not a foreign key. `null` for non-AAPP clients.
- `external_id` (string | null, optional, maxLength 100) — External integration key (ERP/CRM/e-commerce) mapping this client to a record in a third-party system. Free-format, unique per company, distinct from the fiscal `tax_id`.
- `notes` (string | null, required)
- `metadata` (object (Metadata) | null, required) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `is_active` (boolean, required)
- `created_at` (string | null, required, format: date-time)
- `updated_at` (string | null, required, format: date-time)
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/clients/census-verification — Verify a client against the AEAT census
- **Operation ID**: `public-api.v1.clients.verify_census`
- **Tag**: Clients
- **Required scope**: `clients:read` — Read clients.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/clients/public-api.v1.clients.verify_census
Check a third-party name + tax ID pair (the recipient of an invoice) against the AEAT census (VNifV2) to anticipate VeriFactu 1239 rejections before invoicing. Stateless and informational: nothing is persisted on the client. Fail-open — if AEAT is unreachable the call returns 200 with `status: unavailable`. Test keys (`fact_test_`) return deterministic statuses per magic NIF without contacting AEAT.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 2 properties; 2 required: `tax_id`, `name`.
Public REST API v1 — POST /v1/clients/census-verification. Verifies a THIRD PARTY's name + tax_id pair (invoice recipient) against the AEAT census (VNifV2). Max lengths mirror the Company BC value objects consumed by the bridge (`TaxIdentifier` ≤ 20, `CompanyName` ≤ 100).
- `tax_id` (string, required, maxLength 20) — Spanish tax identifier (NIF/CIF/NIE) of the third party to check against the AEAT census
- `name` (string, required, maxLength 100) — Name or business name of the third party (the name + tax ID pair is verified together)
## Responses
- **200**
- Body (`application/json`):
- `data` (object (CensusVerification), required) — Result of verifying the account's persisted company name + tax ID pair against the AEAT census (VNifV2). Use it to anticipate VeriFactu 4104 rejections before invoicing. Fail-open: returns `unavailable` when AEAT cannot be reached.
- `object` (string, required, enum: `census_verification`) — Stripe-like discriminator. Always `census_verification` for this resource.
- `status` (string, required, enum: `identified`, `not_identified`, `not_identified_similar`, `identified_inactive`, `identified_revoked`, `unavailable`) — Census result. `identified`: name + tax ID match an active taxpayer. `not_identified`: the pair is not in the census. `not_identified_similar`: a similar individual exists (natural persons only). `identified_inactive` / `identified_revoked`: the taxpayer is deregistered or revoked. `unavailable`: AEAT could not answer (timeout, fault, no platform certificate) — verification is informational and never blocks.
- `verified_name` (string | null, required) — The company name that was checked against the census (the persisted account name). `null` when the account has never been verified.
- `checked_at` (string | null, required, format: date-time) — ISO 8601 timestamp of the last verification. `null` when never verified.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/companies/{company}/activate — Activate a managed company
- **Operation ID**: `public-api.v1.companies.activate`
- **Tag**: Companies
- **Required scope**: `companies:write` — Create and update companies.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.activate
Reactivate a previously deactivated (`inactive`) managed company. Activation is gated by an atomic per-seat charge — in live mode the prorated seat is charged synchronously and the company only becomes `active` if the charge succeeds. No payment method on file returns 402, and a plan without the gestoría module returns 403. Trial, enterprise and test keys skip the charge.
## Path parameters
- `company` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (Company), required) — A managed company (child sub-account) under your master tenant. Created and operated through the gestoría endpoints; its child API keys can only hold a subset of the scopes of the key that created them.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the managed company.
- `object` (string, required, enum: `company`) — Always `company`.
- `name` (string, required) — Commercial name of the managed company.
- `business_name` (string | null, required) — Legal/registered business name (razón social), or `null` if not set.
- `tax_id` (string, required) — Spanish fiscal identifier (NIF/CIF/NIE). Unique across the companies managed by your master tenant.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (operational) or `archived` (no longer accepts operations).
- `address` (string | null, required) — Street address of the fiscal domicile, or `null`.
- `city` (string | null, required) — City of the fiscal domicile, or `null`.
- `postal_code` (string | null, required) — Postal code of the fiscal domicile, or `null`.
- `province` (string | null, required) — Province of the fiscal domicile, or `null`.
- `country_aeat_zone` (string | null, required, enum: `peninsula`, `canarias`, `ceuta`, `melilla`, `null`) — AEAT territorial zone of the company (`peninsula`, `canarias`, `ceuta`, `melilla`), or `null`.
- `email` (string | null, required, format: email) — Contact email, or `null`.
- `phone` (string | null, required) — Contact phone, or `null`.
- `logo_url` (string | null, required, format: uri) — Absolute URL of the company logo, or `null` if not set.
- `seat_paid_until` (string | null, required, format: date-time) — Paid seat coverage of this managed company (ISO 8601): reactivating it before this date is free. `null` when the seat was never charged or the coverage expired.
- `created_at` (string, required, format: date-time) — When the managed company was registered (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — When the managed company was last updated (ISO 8601), or `null`.
- **401** — Missing or invalid API key.
- **402** — The operation requires a payment that could not be completed: either no payment method is on file (`error.details.payment_setup_url` links to the Billing Portal where it can be set up), the immediate charge was declined by the payment provider, or the account lacks the plan or add-on this operation bills against.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/companies/activate — Activate several managed companies
- **Operation ID**: `public-api.v1.companies.activate_batch`
- **Tag**: Companies
- **Required scope**: `companies:write` — Create and update companies.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.activate_batch
Reactivate several deactivated (`inactive`) managed companies in one operation, charging the combined prorated seats in a single invoice. Pass `company_ids`. The gate is atomic: every company is validated (ownership and `inactive` status) before any charge, so if one is invalid the whole batch is rejected without charging or activating any.
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 1 property; 1 required: `company_ids`.
Reactivate several managed companies in a single request. `company_ids` is a list of company IDs (UUID v7) to reactivate, between 1 and 1000. Ownership of each company and its required `inactive` status are enforced server-side; companies that are not yours or not inactive are reported per item without affecting the rest.
- `company_ids` (array, required, maxItems 1000) — List of managed child company IDs (UUID v7) to reactivate in bulk (between 1 and 1000).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (Company), required) — A managed company (child sub-account) under your master tenant. Created and operated through the gestoría endpoints; its child API keys can only hold a subset of the scopes of the key that created them.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the managed company.
- `object` (string, required, enum: `company`) — Always `company`.
- `name` (string, required) — Commercial name of the managed company.
- `business_name` (string | null, required) — Legal/registered business name (razón social), or `null` if not set.
- `tax_id` (string, required) — Spanish fiscal identifier (NIF/CIF/NIE). Unique across the companies managed by your master tenant.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (operational) or `archived` (no longer accepts operations).
- `address` (string | null, required) — Street address of the fiscal domicile, or `null`.
- `city` (string | null, required) — City of the fiscal domicile, or `null`.
- `postal_code` (string | null, required) — Postal code of the fiscal domicile, or `null`.
- `province` (string | null, required) — Province of the fiscal domicile, or `null`.
- `country_aeat_zone` (string | null, required, enum: `peninsula`, `canarias`, `ceuta`, `melilla`, `null`) — AEAT territorial zone of the company (`peninsula`, `canarias`, `ceuta`, `melilla`), or `null`.
- `email` (string | null, required, format: email) — Contact email, or `null`.
- `phone` (string | null, required) — Contact phone, or `null`.
- `logo_url` (string | null, required, format: uri) — Absolute URL of the company logo, or `null` if not set.
- `seat_paid_until` (string | null, required, format: date-time) — Paid seat coverage of this managed company (ISO 8601): reactivating it before this date is free. `null` when the seat was never charged or the coverage expired.
- `created_at` (string, required, format: date-time) — When the managed company was registered (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — When the managed company was last updated (ISO 8601), or `null`.
- **401** — Missing or invalid API key.
- **402** — The operation requires a payment that could not be completed: either no payment method is on file (`error.details.payment_setup_url` links to the Billing Portal where it can be set up), the immediate charge was declined by the payment provider, or the account lacks the plan or add-on this operation bills against.
- **403** — The API key lacks the required scope for this operation.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/companies/{company}/api-keys — Create a child API key
- **Operation ID**: `public-api.v1.companies.api_keys.create`
- **Tag**: Companies
- **Required scope**: `api_keys:write` — Create and update api keys.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.api_keys.create
Create an API key scoped to one of your managed companies and return its plaintext `secret` exactly once — store it now, it cannot be retrieved later. The requested scopes must be a subset of the calling key's scopes; requesting a scope the parent key does not hold returns 422 (no silent narrowing).
## Path parameters
- `company` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 4 properties; 2 required: `name`, `scopes`.
Issue an API key scoped to one of your managed child companies. The plaintext secret is returned once in the creation response. Requested `scopes` must belong to the closed v1 catalog and be a subset of the calling key scopes; the environment and tier are never accepted from the body.
- `name` (string, required, maxLength 120, minLength 1) — Human-readable name for the API key (1-120 characters).
- `expires_at` (string | null, optional, format: date-time) — Future ISO 8601 date after which the key stops authenticating.
- `scopes` (array, required) — List of scopes from the closed v1 catalog (at least one; a subset of the parent key scopes).
- `ip_allowlist` (array | null, optional) — Optional list of allowed IPs / CIDR ranges (IPv4, IPv6, /N).
## Responses
- **201**
- Body (`application/json`):
- `data` (object (ApiKeyWithSecret), required) — An API key returned once at creation or after secret rotation. Includes the plaintext `secret` — store it now, it cannot be retrieved later.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the API key.
- `object` (string, required, enum: `api_key`) — Always `api_key`.
- `name` (string, required) — Human-friendly label assigned at creation time.
- `prefix` (string, required) — First chars of the key (e.g. `fact_live_1N0Fnyhh`) — safe to log. Does NOT authenticate.
- `scopes` (array, required) — Authorized scopes. `*` means super scope (full access).
- `tier` (string, required) — Rate-limit tier (`free`, `starter`, `pro`, `scale`). Derived from the company plan (or from an active capacity boost when higher), never set from the request body.
- `environment` (string, required, enum: `live`, `test`) — Key environment: `live` (`fact_live_`, real side effects) or `test` (`fact_test_`, sandbox company, no real-world effects).
- `created_at` (string, required, format: date-time)
- `last_used_at` (string | null, required, format: date-time) — Timestamp of the last authenticated request with this key, or `null` if never used.
- `expires_at` (string | null, required, format: date-time) — Expiry instant (ISO 8601), or `null` if the key does not expire.
- `revoked_at` (string | null, required, format: date-time) — Revocation instant (ISO 8601), or `null` if the key is not revoked.
- `is_active` (boolean, required) — `true` when the key is usable (not revoked and not expired).
- `is_revoked` (boolean, required) — `true` once the key has been revoked.
- `secret` (string, required) — Plaintext secret. Returned only at creation or after rotation — never again.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed — e.g. the API key plan limit was reached, or an invoice language outside the allowed catalog (`es`, `en`, `ca`).
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/companies/{company}/api-keys — List child API keys
- **Operation ID**: `public-api.v1.companies.api_keys.list`
- **Tag**: Companies
- **Required scope**: `api_keys:read` — Read api keys.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.api_keys.list
List the API keys of one of your managed companies with cursor-based pagination, including revoked keys for audit. The plaintext secret is never returned. A company not managed by your master tenant returns 404.
## Path parameters
- `company` (string, required)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200** — `ApiKeyListV1Resource`
- Body (`application/json`):
- `data` (array, required)
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the API key.
- `object` (string, required, enum: `api_key`) — Always `api_key`.
- `name` (string, required) — Human-friendly label assigned at creation time.
- `prefix` (string, required) — First chars of the key (e.g. `fact_live_1N0Fnyhh`) — safe to log. Does NOT authenticate.
- `scopes` (array, required) — Authorized scopes. `*` means super scope (full access).
- `tier` (string, required) — Rate-limit tier (`free`, `starter`, `pro`, `scale`). Derived from the company plan (or from an active capacity boost when higher), never set from the request body.
- `environment` (string, required, enum: `live`, `test`) — Key environment: `live` (`fact_live_`, real side effects) or `test` (`fact_test_`, sandbox company, no real-world effects).
- `created_at` (string, required, format: date-time)
- `last_used_at` (string | null, required, format: date-time) — Timestamp of the last authenticated request with this key, or `null` if never used.
- `expires_at` (string | null, required, format: date-time) — Expiry instant (ISO 8601), or `null` if the key does not expire.
- `revoked_at` (string | null, required, format: date-time) — Revocation instant (ISO 8601), or `null` if the key is not revoked.
- `is_active` (boolean, required) — `true` when the key is usable (not revoked and not expired).
- `is_revoked` (boolean, required) — `true` once the key has been revoked.
- `has_more` (boolean, required) — Whether more pages are available after this one.
- `next_cursor` (string | null, required) — Opaque cursor for the next page, or `null` when `has_more` is `false`. The example below shows the shape used by most listings; others return a different one, so pass the value back verbatim instead of validating it.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# DELETE /v1/companies/{company}/api-keys/{api_key} — Revoke a child API key
- **Operation ID**: `public-api.v1.companies.api_keys.revoke`
- **Tag**: Companies
- **Required scope**: `api_keys:write` — Create and update api keys.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.api_keys.revoke
Revoke a child API key immediately and irreversibly, leaving it unusable. Subsequent requests authenticated with that key fail with 401. A company not managed by your master tenant returns 404.
## Path parameters
- `company` (string, required)
- `api_key` (string, required)
## Query parameters
- `reason` (string | null, optional, maxLength 500) — Motivo opcional de la revocación (queda en audit log).
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (ApiKey), required) — An API key of your company. The plaintext secret is never exposed in this representation — it is shown only once, at creation or after rotation.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the API key.
- `object` (string, required, enum: `api_key`) — Always `api_key`.
- `name` (string, required) — Human-friendly label assigned at creation time.
- `prefix` (string, required) — First chars of the key (e.g. `fact_live_1N0Fnyhh`) — safe to log. Does NOT authenticate.
- `scopes` (array, required) — Authorized scopes. `*` means super scope (full access).
- `tier` (string, required) — Rate-limit tier (`free`, `starter`, `pro`, `scale`). Derived from the company plan (or from an active capacity boost when higher), never set from the request body.
- `environment` (string, required, enum: `live`, `test`) — Key environment: `live` (`fact_live_`, real side effects) or `test` (`fact_test_`, sandbox company, no real-world effects).
- `created_at` (string, required, format: date-time)
- `last_used_at` (string | null, required, format: date-time) — Timestamp of the last authenticated request with this key, or `null` if never used.
- `expires_at` (string | null, required, format: date-time) — Expiry instant (ISO 8601), or `null` if the key does not expire.
- `revoked_at` (string | null, required, format: date-time) — Revocation instant (ISO 8601), or `null` if the key is not revoked.
- `is_active` (boolean, required) — `true` when the key is usable (not revoked and not expired).
- `is_revoked` (boolean, required) — `true` once the key has been revoked.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed — e.g. the API key plan limit was reached, or an invoice language outside the allowed catalog (`es`, `en`, `ca`).
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/companies/{company}/api-keys/{api_key}/rotate-secret — Rotate a child API key secret
- **Operation ID**: `public-api.v1.companies.api_keys.rotate_secret`
- **Tag**: Companies
- **Required scope**: `api_keys:write` — Create and update api keys.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.api_keys.rotate_secret
Invalidate the current secret of a child API key immediately, generate a fresh `prefix` + `secret`, and return the new secret in plaintext exactly once. Any request made with the previous secret stops authenticating right away. Irreversible. A company not managed by your master tenant returns 404.
## Path parameters
- `company` (string, required)
- `api_key` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (ApiKeyWithSecret), required) — An API key returned once at creation or after secret rotation. Includes the plaintext `secret` — store it now, it cannot be retrieved later.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the API key.
- `object` (string, required, enum: `api_key`) — Always `api_key`.
- `name` (string, required) — Human-friendly label assigned at creation time.
- `prefix` (string, required) — First chars of the key (e.g. `fact_live_1N0Fnyhh`) — safe to log. Does NOT authenticate.
- `scopes` (array, required) — Authorized scopes. `*` means super scope (full access).
- `tier` (string, required) — Rate-limit tier (`free`, `starter`, `pro`, `scale`). Derived from the company plan (or from an active capacity boost when higher), never set from the request body.
- `environment` (string, required, enum: `live`, `test`) — Key environment: `live` (`fact_live_`, real side effects) or `test` (`fact_test_`, sandbox company, no real-world effects).
- `created_at` (string, required, format: date-time)
- `last_used_at` (string | null, required, format: date-time) — Timestamp of the last authenticated request with this key, or `null` if never used.
- `expires_at` (string | null, required, format: date-time) — Expiry instant (ISO 8601), or `null` if the key does not expire.
- `revoked_at` (string | null, required, format: date-time) — Revocation instant (ISO 8601), or `null` if the key is not revoked.
- `is_active` (boolean, required) — `true` when the key is usable (not revoked and not expired).
- `is_revoked` (boolean, required) — `true` once the key has been revoked.
- `secret` (string, required) — Plaintext secret. Returned only at creation or after rotation — never again.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/companies/{company}/api-keys/{api_key} — Retrieve a child API key
- **Operation ID**: `public-api.v1.companies.api_keys.show`
- **Tag**: Companies
- **Required scope**: `api_keys:read` — Read api keys.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.api_keys.show
Retrieve a single API key of one of your managed companies by its `id` (UUID v7). The plaintext secret is never included. A key not belonging to a company you manage returns 404 `api_key_not_found` (anti-enumeration).
## Path parameters
- `company` (string, required)
- `api_key` (string, required)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (ApiKey), required) — An API key of your company. The plaintext secret is never exposed in this representation — it is shown only once, at creation or after rotation.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the API key.
- `object` (string, required, enum: `api_key`) — Always `api_key`.
- `name` (string, required) — Human-friendly label assigned at creation time.
- `prefix` (string, required) — First chars of the key (e.g. `fact_live_1N0Fnyhh`) — safe to log. Does NOT authenticate.
- `scopes` (array, required) — Authorized scopes. `*` means super scope (full access).
- `tier` (string, required) — Rate-limit tier (`free`, `starter`, `pro`, `scale`). Derived from the company plan (or from an active capacity boost when higher), never set from the request body.
- `environment` (string, required, enum: `live`, `test`) — Key environment: `live` (`fact_live_`, real side effects) or `test` (`fact_test_`, sandbox company, no real-world effects).
- `created_at` (string, required, format: date-time)
- `last_used_at` (string | null, required, format: date-time) — Timestamp of the last authenticated request with this key, or `null` if never used.
- `expires_at` (string | null, required, format: date-time) — Expiry instant (ISO 8601), or `null` if the key does not expire.
- `revoked_at` (string | null, required, format: date-time) — Revocation instant (ISO 8601), or `null` if the key is not revoked.
- `is_active` (boolean, required) — `true` when the key is usable (not revoked and not expired).
- `is_revoked` (boolean, required) — `true` once the key has been revoked.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/companies — Create a managed company
- **Operation ID**: `public-api.v1.companies.create`
- **Tag**: Companies
- **Required scope**: `companies:write` — Create and update companies.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.create
Register a new managed company (a child sub-account) under your master tenant — the gestoría model. `name` and `tax_id` are required, and `tax_id` must be unique among the companies you manage (a duplicate returns 409). In live mode the prorated per-seat charge gates creation: with no payment method on file or a failed charge the call returns 402 and nothing is created. Use `GET /v1/companies/seat-charge-preview` to anticipate the cost; test keys skip the charge.
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 10 properties; 2 required: `name`, `tax_id`.
Register a managed child company under your master tenant. `name` and `tax_id` are required; the rest of the profile (business name, fiscal address, contact details) is optional. `tax_id` is validated as a Spanish tax ID (NIF/CIF/NIE) and must be unique among the companies you manage; `country_aeat_zone` is derived from the postal code.
- `name` (string, required, maxLength 255, minLength 1) — Trade name of the child company (1-255 characters).
- `tax_id` (string, required, maxLength 20) — Spanish tax identifier (NIF, CIF or NIE). Immutable after creation.
- `business_name` (string | null, optional, maxLength 100) — Legal/registered business name of the child company.
- `address` (string | null, optional, maxLength 255) — Fiscal address.
- `city` (string | null, optional, maxLength 100) — City of the fiscal address.
- `postal_code` (string | null, optional, maxLength 10) — Postal code (derives the AEAT zone).
- `province` (string | null, optional, maxLength 100) — Province.
- `country` (string | null, optional, maxLength 100) — Country.
- `email` (string | null, optional, format: email, maxLength 255) — Contact email of the child company.
- `phone` (string | null, optional, maxLength 20) — Contact phone number.
## Responses
- **201**
- Body (`application/json`):
- `data` (object (Company), required) — A managed company (child sub-account) under your master tenant. Created and operated through the gestoría endpoints; its child API keys can only hold a subset of the scopes of the key that created them.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the managed company.
- `object` (string, required, enum: `company`) — Always `company`.
- `name` (string, required) — Commercial name of the managed company.
- `business_name` (string | null, required) — Legal/registered business name (razón social), or `null` if not set.
- `tax_id` (string, required) — Spanish fiscal identifier (NIF/CIF/NIE). Unique across the companies managed by your master tenant.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (operational) or `archived` (no longer accepts operations).
- `address` (string | null, required) — Street address of the fiscal domicile, or `null`.
- `city` (string | null, required) — City of the fiscal domicile, or `null`.
- `postal_code` (string | null, required) — Postal code of the fiscal domicile, or `null`.
- `province` (string | null, required) — Province of the fiscal domicile, or `null`.
- `country_aeat_zone` (string | null, required, enum: `peninsula`, `canarias`, `ceuta`, `melilla`, `null`) — AEAT territorial zone of the company (`peninsula`, `canarias`, `ceuta`, `melilla`), or `null`.
- `email` (string | null, required, format: email) — Contact email, or `null`.
- `phone` (string | null, required) — Contact phone, or `null`.
- `logo_url` (string | null, required, format: uri) — Absolute URL of the company logo, or `null` if not set.
- `seat_paid_until` (string | null, required, format: date-time) — Paid seat coverage of this managed company (ISO 8601): reactivating it before this date is free. `null` when the seat was never charged or the coverage expired.
- `created_at` (string, required, format: date-time) — When the managed company was registered (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — When the managed company was last updated (ISO 8601), or `null`.
- **401** — Missing or invalid API key.
- **402** — The operation requires a payment that could not be completed: either no payment method is on file (`error.details.payment_setup_url` links to the Billing Portal where it can be set up), the immediate charge was declined by the payment provider, or the account lacks the plan or add-on this operation bills against.
- **403** — The API key lacks the required scope for this operation.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/companies/{company}/creation-status — Retrieve the creation status of a managed company
- **Operation ID**: `public-api.v1.companies.creation_status`
- **Tag**: Companies
- **Required scope**: `companies:read` — Read companies.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.creation_status
Poll the provisioning lifecycle of a managed company. Returns `provisioning_status` (`pending`, `awaiting_payment`, `provisioning`, `active`, `failed`). `payment_setup_url` is present only while `awaiting_payment` and points to the master tenant's payment-method onboarding; `failed_reason` is present only when provisioning has `failed`. Test keys move the child to `active` directly.
## Path parameters
- `company` (string, required)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (CompanyCreationStatus), required) — Provisioning lifecycle status of a managed company (child sub-account) created through the gestoría endpoints. Poll it after creating a company to know when it becomes operational.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the managed company.
- `object` (string, required, enum: `company_creation_status`) — Always `company_creation_status`.
- `provisioning_status` (string, required, enum: `pending`, `awaiting_payment`, `provisioning`, `active`, `failed`) — Provisioning lifecycle status: `pending`, `awaiting_payment`, `provisioning`, `active` or `failed`.
- `payment_setup_url` (string | null, required, format: uri) — URL to onboard the master tenant's payment method. Only present while `awaiting_payment`; `null` otherwise.
- `failed_reason` (string | null, required) — Human-readable reason the provisioning failed. Only present when `provisioning_status` is `failed`; `null` otherwise.
- `started_at` (string | null, required, format: date-time) — When provisioning started (ISO 8601), or `null` if not started yet.
- `completed_at` (string | null, required, format: date-time) — When provisioning completed (ISO 8601), or `null` if not completed yet.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/companies/{company}/deactivate — Deactivate a managed company
- **Operation ID**: `public-api.v1.companies.deactivate`
- **Tag**: Companies
- **Required scope**: `companies:write` — Create and update companies.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.deactivate
Deactivate a managed company, moving it from `active` to `inactive`: it becomes non-operational but its data is preserved and the change is reversible (reactivate it later by paying its seat). No charge is applied; instead a prorated seat credit is emitted best-effort for the unused time.
## Path parameters
- `company` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (Company), required) — A managed company (child sub-account) under your master tenant. Created and operated through the gestoría endpoints; its child API keys can only hold a subset of the scopes of the key that created them.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the managed company.
- `object` (string, required, enum: `company`) — Always `company`.
- `name` (string, required) — Commercial name of the managed company.
- `business_name` (string | null, required) — Legal/registered business name (razón social), or `null` if not set.
- `tax_id` (string, required) — Spanish fiscal identifier (NIF/CIF/NIE). Unique across the companies managed by your master tenant.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (operational) or `archived` (no longer accepts operations).
- `address` (string | null, required) — Street address of the fiscal domicile, or `null`.
- `city` (string | null, required) — City of the fiscal domicile, or `null`.
- `postal_code` (string | null, required) — Postal code of the fiscal domicile, or `null`.
- `province` (string | null, required) — Province of the fiscal domicile, or `null`.
- `country_aeat_zone` (string | null, required, enum: `peninsula`, `canarias`, `ceuta`, `melilla`, `null`) — AEAT territorial zone of the company (`peninsula`, `canarias`, `ceuta`, `melilla`), or `null`.
- `email` (string | null, required, format: email) — Contact email, or `null`.
- `phone` (string | null, required) — Contact phone, or `null`.
- `logo_url` (string | null, required, format: uri) — Absolute URL of the company logo, or `null` if not set.
- `seat_paid_until` (string | null, required, format: date-time) — Paid seat coverage of this managed company (ISO 8601): reactivating it before this date is free. `null` when the seat was never charged or the coverage expired.
- `created_at` (string, required, format: date-time) — When the managed company was registered (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — When the managed company was last updated (ISO 8601), or `null`.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# DELETE /v1/companies/{company} — Archive a managed company
- **Operation ID**: `public-api.v1.companies.delete`
- **Tag**: Companies
- **Required scope**: `companies:delete` — Delete companies.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.delete
Archive a managed company, moving it to the `archived` status so it no longer accepts operations. The underlying company row and its history are preserved. Archiving may be blocked by business rules (returns 422 `business_rule_violation`). A company not managed by your master tenant returns 404.
## Path parameters
- `company` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **204** — No content
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/companies — List your managed companies
- **Operation ID**: `public-api.v1.companies.list`
- **Tag**: Companies
- **Required scope**: `companies:read` — Read companies.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.list
List the companies managed by your master tenant with cursor-based pagination. By default only `active` and `inactive` companies are returned; pass `status` (`active`, `inactive`, `archived`) to filter — `status=archived` is the opt-in way to surface archived companies. Only your own children are ever returned.
## Query parameters
- `status` (string | null, optional, enum: `active`, `inactive`, `archived`) — Filtrar por estado del vínculo de gestoría.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200** — `CompanyListV1Resource`
- Body (`application/json`):
- `data` (array, required) — Page of managed companies.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the managed company.
- `object` (string, required, enum: `company`) — Always `company`.
- `name` (string, required) — Commercial name of the managed company.
- `business_name` (string | null, required) — Legal/registered business name (razón social), or `null` if not set.
- `tax_id` (string, required) — Spanish fiscal identifier (NIF/CIF/NIE). Unique across the companies managed by your master tenant.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (operational) or `archived` (no longer accepts operations).
- `address` (string | null, required) — Street address of the fiscal domicile, or `null`.
- `city` (string | null, required) — City of the fiscal domicile, or `null`.
- `postal_code` (string | null, required) — Postal code of the fiscal domicile, or `null`.
- `province` (string | null, required) — Province of the fiscal domicile, or `null`.
- `country_aeat_zone` (string | null, required, enum: `peninsula`, `canarias`, `ceuta`, `melilla`, `null`) — AEAT territorial zone of the company (`peninsula`, `canarias`, `ceuta`, `melilla`), or `null`.
- `email` (string | null, required, format: email) — Contact email, or `null`.
- `phone` (string | null, required) — Contact phone, or `null`.
- `logo_url` (string | null, required, format: uri) — Absolute URL of the company logo, or `null` if not set.
- `seat_paid_until` (string | null, required, format: date-time) — Paid seat coverage of this managed company (ISO 8601): reactivating it before this date is free. `null` when the seat was never charged or the coverage expired.
- `created_at` (string, required, format: date-time) — When the managed company was registered (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — When the managed company was last updated (ISO 8601), or `null`.
- `has_more` (boolean, required) — `true` when more companies exist beyond this page.
- `next_cursor` (string | null, required) — Opaque cursor for the next page, or `null` when `has_more` is `false`.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/companies/seat-charge-preview — Preview the seat charge of adding a company
- **Operation ID**: `public-api.v1.companies.seat_charge_preview`
- **Tag**: Companies
- **Required scope**: `companies:read` — Read companies.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.seat_charge_preview
Preview the prorated per-seat amount for adding or activating managed companies, computed from the master tenant's Stripe upcoming invoice, without charging. Use `count` (≥1) to preview a batch, or `company_ids` for a coverage-aware preview: companies still covered for the current period cost `0` (`already_covered: true`). `amount` is in the currency's minor units; `requires_payment_method` is `true` when no payment method is on file.
## Query parameters
- `count` (integer | null, optional, min 1, max 1000) — Número de empresas hijas que se activarían en bloque (≥1, default 1).
- `company_ids[]` (array, required)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (Company), required) — A managed company (child sub-account) under your master tenant. Created and operated through the gestoría endpoints; its child API keys can only hold a subset of the scopes of the key that created them.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the managed company.
- `object` (string, required, enum: `company`) — Always `company`.
- `name` (string, required) — Commercial name of the managed company.
- `business_name` (string | null, required) — Legal/registered business name (razón social), or `null` if not set.
- `tax_id` (string, required) — Spanish fiscal identifier (NIF/CIF/NIE). Unique across the companies managed by your master tenant.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (operational) or `archived` (no longer accepts operations).
- `address` (string | null, required) — Street address of the fiscal domicile, or `null`.
- `city` (string | null, required) — City of the fiscal domicile, or `null`.
- `postal_code` (string | null, required) — Postal code of the fiscal domicile, or `null`.
- `province` (string | null, required) — Province of the fiscal domicile, or `null`.
- `country_aeat_zone` (string | null, required, enum: `peninsula`, `canarias`, `ceuta`, `melilla`, `null`) — AEAT territorial zone of the company (`peninsula`, `canarias`, `ceuta`, `melilla`), or `null`.
- `email` (string | null, required, format: email) — Contact email, or `null`.
- `phone` (string | null, required) — Contact phone, or `null`.
- `logo_url` (string | null, required, format: uri) — Absolute URL of the company logo, or `null` if not set.
- `seat_paid_until` (string | null, required, format: date-time) — Paid seat coverage of this managed company (ISO 8601): reactivating it before this date is free. `null` when the seat was never charged or the coverage expired.
- `created_at` (string, required, format: date-time) — When the managed company was registered (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — When the managed company was last updated (ISO 8601), or `null`.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/companies/{company} — Retrieve a managed company
- **Operation ID**: `public-api.v1.companies.show`
- **Tag**: Companies
- **Required scope**: `companies:read` — Read companies.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.show
Retrieve a single managed company by its `id` (UUID v7). A company not managed by your master tenant returns 404 `company_not_found` (anti-enumeration).
## Path parameters
- `company` (string, required)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (Company), required) — A managed company (child sub-account) under your master tenant. Created and operated through the gestoría endpoints; its child API keys can only hold a subset of the scopes of the key that created them.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the managed company.
- `object` (string, required, enum: `company`) — Always `company`.
- `name` (string, required) — Commercial name of the managed company.
- `business_name` (string | null, required) — Legal/registered business name (razón social), or `null` if not set.
- `tax_id` (string, required) — Spanish fiscal identifier (NIF/CIF/NIE). Unique across the companies managed by your master tenant.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (operational) or `archived` (no longer accepts operations).
- `address` (string | null, required) — Street address of the fiscal domicile, or `null`.
- `city` (string | null, required) — City of the fiscal domicile, or `null`.
- `postal_code` (string | null, required) — Postal code of the fiscal domicile, or `null`.
- `province` (string | null, required) — Province of the fiscal domicile, or `null`.
- `country_aeat_zone` (string | null, required, enum: `peninsula`, `canarias`, `ceuta`, `melilla`, `null`) — AEAT territorial zone of the company (`peninsula`, `canarias`, `ceuta`, `melilla`), or `null`.
- `email` (string | null, required, format: email) — Contact email, or `null`.
- `phone` (string | null, required) — Contact phone, or `null`.
- `logo_url` (string | null, required, format: uri) — Absolute URL of the company logo, or `null` if not set.
- `seat_paid_until` (string | null, required, format: date-time) — Paid seat coverage of this managed company (ISO 8601): reactivating it before this date is free. `null` when the seat was never charged or the coverage expired.
- `created_at` (string, required, format: date-time) — When the managed company was registered (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — When the managed company was last updated (ISO 8601), or `null`.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# PATCH /v1/companies/{company} — Update a managed company
- **Operation ID**: `public-api.v1.companies.update`
- **Tag**: Companies
- **Required scope**: `companies:write` — Create and update companies.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.update
Update the profile of a managed company (`name`, `business_name`, address fields, `email`, `phone`). The `tax_id` is immutable after creation (sending it returns 422) and `country_aeat_zone` is derived from the address. Partial update: omitted fields keep their value; send `""` to clear a field.
## Path parameters
- `company` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, optional. 9 properties; none of them required.
Partial update of a managed child company profile (business name, fiscal address, contact details). Omitted fields keep their current value; send `""` to clear a field. `country_aeat_zone` is derived from the postal code, and `tax_id` is immutable after creation (sending it returns 422).
- `name` (string, optional, maxLength 255, minLength 1) — Trade name of the child company (1-255 characters).
- `business_name` (string | null, optional, maxLength 100) — Legal/registered business name of the child company.
- `address` (string | null, optional, maxLength 255) — Fiscal address.
- `city` (string | null, optional, maxLength 100) — City of the fiscal address.
- `postal_code` (string | null, optional, maxLength 10) — Postal code (derives the AEAT zone).
- `province` (string | null, optional, maxLength 100) — Province.
- `country` (string | null, optional, maxLength 100) — Country.
- `email` (string | null, optional, format: email, maxLength 255) — Contact email of the child company.
- `phone` (string | null, optional, maxLength 20) — Contact phone number.
## Responses
- **200**
- Body (`application/json`):
- `data` (object (Company), required) — A managed company (child sub-account) under your master tenant. Created and operated through the gestoría endpoints; its child API keys can only hold a subset of the scopes of the key that created them.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the managed company.
- `object` (string, required, enum: `company`) — Always `company`.
- `name` (string, required) — Commercial name of the managed company.
- `business_name` (string | null, required) — Legal/registered business name (razón social), or `null` if not set.
- `tax_id` (string, required) — Spanish fiscal identifier (NIF/CIF/NIE). Unique across the companies managed by your master tenant.
- `status` (string, required, enum: `active`, `archived`) — Lifecycle status: `active` (operational) or `archived` (no longer accepts operations).
- `address` (string | null, required) — Street address of the fiscal domicile, or `null`.
- `city` (string | null, required) — City of the fiscal domicile, or `null`.
- `postal_code` (string | null, required) — Postal code of the fiscal domicile, or `null`.
- `province` (string | null, required) — Province of the fiscal domicile, or `null`.
- `country_aeat_zone` (string | null, required, enum: `peninsula`, `canarias`, `ceuta`, `melilla`, `null`) — AEAT territorial zone of the company (`peninsula`, `canarias`, `ceuta`, `melilla`), or `null`.
- `email` (string | null, required, format: email) — Contact email, or `null`.
- `phone` (string | null, required) — Contact phone, or `null`.
- `logo_url` (string | null, required, format: uri) — Absolute URL of the company logo, or `null` if not set.
- `seat_paid_until` (string | null, required, format: date-time) — Paid seat coverage of this managed company (ISO 8601): reactivating it before this date is free. `null` when the seat was never charged or the coverage expired.
- `created_at` (string, required, format: date-time) — When the managed company was registered (ISO 8601).
- `updated_at` (string | null, required, format: date-time) — When the managed company was last updated (ISO 8601), or `null`.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **422** — Validation failed.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/companies/{company}/verify-creation — Verify the creation of a managed company
- **Operation ID**: `public-api.v1.companies.verify_creation`
- **Tag**: Companies
- **Required scope**: `companies:write` — Create and update companies.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.companies.verify_creation
Reconcile and advance the provisioning of a managed company against the master tenant's subscription. No request body; idempotent. While `awaiting_payment`, once the master has a payment method on file the child is charged the prorated seat and moves to `active`; otherwise it stays `awaiting_payment` with no error. Returns the creation-status resource.
## Path parameters
- `company` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (CompanyCreationStatus), required) — Provisioning lifecycle status of a managed company (child sub-account) created through the gestoría endpoints. Poll it after creating a company to know when it becomes operational.
- `id` (string, required, format: uuid) — Opaque identifier (UUID v7) of the managed company.
- `object` (string, required, enum: `company_creation_status`) — Always `company_creation_status`.
- `provisioning_status` (string, required, enum: `pending`, `awaiting_payment`, `provisioning`, `active`, `failed`) — Provisioning lifecycle status: `pending`, `awaiting_payment`, `provisioning`, `active` or `failed`.
- `payment_setup_url` (string | null, required, format: uri) — URL to onboard the master tenant's payment method. Only present while `awaiting_payment`; `null` otherwise.
- `failed_reason` (string | null, required) — Human-readable reason the provisioning failed. Only present when `provisioning_status` is `failed`; `null` otherwise.
- `started_at` (string | null, required, format: date-time) — When provisioning started (ISO 8601), or `null` if not started yet.
- `completed_at` (string | null, required, format: date-time) — When provisioning completed (ISO 8601), or `null` if not completed yet.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The request conflicts with the current resource state — e.g. an idempotency key was reused with a different body, or the resource is in a state that does not allow this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# GET /v1/gestoria/workforce-summary — Retrieve the consolidated workforce compliance overview
- **Operation ID**: `public-api.v1.gestoria.workforce_summary`
- **Tag**: Companies
- **Required scope**: `companies:read` — Read companies.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/companies/public-api.v1.gestoria.workforce_summary
Return the consolidated time-tracking compliance panel for your whole managed portfolio: one row per `active` managed company, each projected from that company's latest monthly close without recomputation — whether the current (last closable) period is closed, its status (`closed`/`reopened`), the last closed period (`last_closed_year`/`last_closed_month`), and the aggregated `total_balance_minutes`, `total_overtime_minutes` and `employee_count`. Master-scoped: the portfolio is resolved from your API key, never from the payload, and only your own children appear. Unlike the per-company `X-Active-Profile` endpoints, this aggregates across children in a single call. Returned as `{ "data": [ConsolidatedWorkforce, ...] }`.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (array, required)
- `id` (string, required, format: uuid) — UUID v7 of the managed company this row belongs to (the resource has no id of its own).
- `name` (string, required) — Trade name / legal name of the managed company.
- `tax_id` (string, required) — Spanish fiscal identifier (NIF/CIF) of the managed company.
- `current_period_closed` (boolean, required) — true when the current (last closable) period is closed for this company.
- `current_period_status` (string | null, required, enum: `closed`, `reopened`, `null`) — Status of the current period close: `closed`, `reopened`, or `null` when the company has no close for it.
- `last_closed_year` (integer | null, required) — Year of the company’s latest existing monthly close, or `null` when it has none.
- `last_closed_month` (integer | null, required) — Month (1-12) of the company’s latest existing monthly close, or `null` when it has none.
- `total_balance_minutes` (integer, required) — Total balance in minutes from the snapshot of the latest close (may be negative).
- `total_overtime_minutes` (integer, required) — Total overtime in minutes from the snapshot of the latest close.
- `employee_count` (integer, required) — Number of employees in the snapshot of the latest close.
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/delivery_notes/bulk-delete — Bulk delete delivery notes
- **Operation ID**: `public-api.v1.delivery_notes.bulk_delete`
- **Tag**: Delivery Notes
- **Required scope**: `delivery_notes:delete` — Delete delivery notes.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/delivery-notes/public-api.v1.delivery_notes.bulk_delete
Delete several delivery notes in a single request. The body takes an `ids` array of `uuid`s. Returns a `BulkPartialSuccessResult` with `total`, `successful`, `failed` counts and a `failures` list (`id` + `error_code` + Spanish `error_message`) for those that could not be deleted (e.g. signed or invoiced). Supports `Idempotency-Key` for safe retries.
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 1 property; 1 required: `ids`.
Delete several delivery notes in one request. `ids` is an array of 1 to 100 UUIDs; unknown or cross-tenant identifiers are reported as failed rather than failing the whole request.
- `ids` (array, required, maxItems 100)
## Responses
- **200**
- Body (`application/json`):
- `data` (object (BulkPartialSuccessResult), required) — Result of a bulk or import operation that reports per-resource status. `total` is how many rows/resources were processed (`successful + failed`), `successful` how many were applied (deleted, created or validated) and `failed` how many could not be processed. `failures[]` carries one item per failed row. Shape shared by every bulk endpoint of the public API (the `/v1/{resource}/bulk-delete` endpoints emit it today). Anchored integrators before `2026-09-01` keep receiving the previous `{object, deleted, failed[{id, reason}]}` shape via `Factuarea-Version`.
- `total` (integer, required) — Number of rows/resources processed (`successful + failed`).
- `successful` (integer, required) — Number of rows/resources processed successfully (deleted, created or validated).
- `failed` (integer, required) — Number of rows/resources that could not be processed. Equals `failures` length.
- `failures` (array, required) — One item per failed row/resource. Always a list (empty, never `null`, when there are no failures).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The delivery note request conflicts with its current state — e.g. an invalid status transition (signing an already-signed delivery note), an attempt to sign a non-delivered note, or a reused idempotency key.
- **422** — Validation failed, or the delivery note cannot undergo the requested state transition (e.g. signing a non-delivered note).
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/delivery_notes/bulk-pdf — Bulk download delivery note PDFs
- **Operation ID**: `public-api.v1.delivery_notes.bulk_pdf`
- **Tag**: Delivery Notes
- **Required scope**: `pdfs:read` — Read pdfs.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/delivery-notes/public-api.v1.delivery_notes.bulk_pdf
Packages the PDFs of up to 50 delivery notes (by id) into a single ZIP. Ids that are not found or have no generable PDF do not abort the request: the ZIP carries only the valid ones and the per-resource counts travel in the `X-Bulk-*` response headers.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 1 property; 1 required: `ids`.
Package the PDFs of several delivery notes into a single ZIP. `ids` is an array of delivery-note UUIDs, up to 50 per request.
- `ids` (array, required, maxItems 50)
## Responses
- **200**
- Body (`application/zip`):
- string
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The delivery note request conflicts with its current state — e.g. an invalid status transition (signing an already-signed delivery note), an attempt to sign a non-delivered note, or a reused idempotency key.
- **422** — Validation failed, or the delivery note cannot undergo the requested state transition (e.g. signing a non-delivered note).
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/delivery_notes/bulk-send — Bulk send delivery notes
- **Operation ID**: `public-api.v1.delivery_notes.bulk_send`
- **Tag**: Delivery Notes
- **Required scope**: `delivery_notes:write` — Create and update delivery notes.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/delivery-notes/public-api.v1.delivery_notes.bulk_send
Sends up to 200 delivery notes by email (queued) in one call, reusing the single-send path per id. Returns a `BulkPartialSuccessResult` with `total`, `successful` and `failed` counts plus a `failures` list (`id` + `error_code` + Spanish `error_message`) for each delivery note that could not be sent (not found, non-sendable status or no resolvable recipient).
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 6 properties; 1 required: `ids`.
Email several delivery notes in one request (queued), up to 200 per batch. `ids` is an array of delivery-note UUIDs; the optional `to`/`cc` arrays and `subject`/`message`/`language` overrides apply to the whole batch (when `to` is omitted, each delivery note uses its client email).
- `subject` (string | null, optional, maxLength 200)
- `message` (string | null, optional, maxLength 5000)
- `language` (string | null, optional, maxLength 5)
- `ids` (array, required, maxItems 200)
- `to` (array | null, optional)
- `cc` (array | null, optional)
## Responses
- **200**
- Body (`application/json`):
- `data` (object (BulkPartialSuccessResult), required) — Result of a bulk or import operation that reports per-resource status. `total` is how many rows/resources were processed (`successful + failed`), `successful` how many were applied (deleted, created or validated) and `failed` how many could not be processed. `failures[]` carries one item per failed row. Shape shared by every bulk endpoint of the public API (the `/v1/{resource}/bulk-delete` endpoints emit it today). Anchored integrators before `2026-09-01` keep receiving the previous `{object, deleted, failed[{id, reason}]}` shape via `Factuarea-Version`.
- `total` (integer, required) — Number of rows/resources processed (`successful + failed`).
- `successful` (integer, required) — Number of rows/resources processed successfully (deleted, created or validated).
- `failed` (integer, required) — Number of rows/resources that could not be processed. Equals `failures` length.
- `failures` (array, required) — One item per failed row/resource. Always a list (empty, never `null`, when there are no failures).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The delivery note request conflicts with its current state — e.g. an invalid status transition (signing an already-signed delivery note), an attempt to sign a non-delivered note, or a reused idempotency key.
- **422** — Validation failed, or the delivery note cannot undergo the requested state transition (e.g. signing a non-delivered note).
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/delivery_notes/bulk-status — Bulk change delivery note status
- **Operation ID**: `public-api.v1.delivery_notes.bulk_status`
- **Tag**: Delivery Notes
- **Required scope**: `delivery_notes:transition` — Change the lifecycle status of delivery notes.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/delivery-notes/public-api.v1.delivery_notes.bulk_status
Transition up to 50 delivery notes (by id) to a status from the closed set `[delivered, cancelled]`, each through the document state guard. Returns a `BulkPartialSuccessResult`; delivery notes whose transition is rejected (not found or not transitionable) come back in `failures[]`.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 2 properties; 2 required: `new_status`, `ids`.
Transition several delivery notes to `new_status` (`delivered` or `cancelled`) in one request, up to 50 per batch. `ids` is an array of delivery-note UUIDs; every transition passes the document state guard, and notes that cannot transition are returned under `failures[]`.
- `new_status` (string, required, enum: `delivered`, `cancelled`)
- `ids` (array, required, maxItems 50)
## Responses
- **200**
- Body (`application/json`):
- `data` (object (BulkPartialSuccessResult), required) — Result of a bulk or import operation that reports per-resource status. `total` is how many rows/resources were processed (`successful + failed`), `successful` how many were applied (deleted, created or validated) and `failed` how many could not be processed. `failures[]` carries one item per failed row. Shape shared by every bulk endpoint of the public API (the `/v1/{resource}/bulk-delete` endpoints emit it today). Anchored integrators before `2026-09-01` keep receiving the previous `{object, deleted, failed[{id, reason}]}` shape via `Factuarea-Version`.
- `total` (integer, required) — Number of rows/resources processed (`successful + failed`).
- `successful` (integer, required) — Number of rows/resources processed successfully (deleted, created or validated).
- `failed` (integer, required) — Number of rows/resources that could not be processed. Equals `failures` length.
- `failures` (array, required) — One item per failed row/resource. Always a list (empty, never `null`, when there are no failures).
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The delivery note request conflicts with its current state — e.g. an invalid status transition (signing an already-signed delivery note), an attempt to sign a non-delivered note, or a reused idempotency key.
- **422** — Validation failed, or the delivery note cannot undergo the requested state transition (e.g. signing a non-delivered note).
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/delivery_notes/{delivery_note}/cancel — Cancel a delivery note
- **Operation ID**: `public-api.v1.delivery_notes.cancel`
- **Tag**: Delivery Notes
- **Required scope**: `delivery_notes:transition` — Change the lifecycle status of delivery notes.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/delivery-notes/public-api.v1.delivery_notes.cancel
Transition a delivery note to the `cancelled` state. Canonical REST replacement for the deprecated `POST /change_status`. Returns 409 `invalid_status_transition` if the note cannot be cancelled (e.g. already invoiced). Supports `Idempotency-Key` for safe retries.
## Path parameters
- `delivery_note` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **200**
- Body (`application/json`):
- `data` (object (DeliveryNote), required) — A delivery note tracking goods delivered to a customer.
- `id` (string, required)
- `object` (string, required, enum: `delivery_note`)
- `number` (string, required)
- `series` (object (SeriesRef), required)
- `client` (object (ClientRef), required)
- `status` (string, required, enum: `draft`, `sent`, `signed`, `invoiced`, `cancelled`) — Delivery note lifecycle status exposed by the public API: `draft` (created, editable), `sent` (goods marked as delivered, no signature recorded yet), `signed` (delivered AND a recipient signature has been recorded), `invoiced` (converted into an invoice, terminal), `cancelled` (terminal). Note: the internal `delivered` state is surfaced as `sent` (without signature) or `signed` (with signature) — there is no `delivered` value in the public API. Recording a signature on a `sent` note moves it to `signed` (it does not introduce a brand-new lifecycle stage; the signature presence is the only difference).
- `issued_on` (string | null, required, format: date)
- `delivery_date` (string | null, required, format: date)
- `signed_at` (string | null, required, format: date-time)
- `signed_by` (string | null, required)
- `signature_image_url` (string | null, required, format: uri)
- `vehicle_plate` (string | null, required) — License plate of the delivery vehicle.
- `driver` (object | null, required) — Delivery driver data. `null` when no sub-field is provided.
- `tracking` (object | null, required) — Shipment tracking data. `null` when no sub-field is provided.
- `received_by` (object | null, required) — Goods recipient data. `null` when no sub-field is provided.
- `billing_emails` (array, required, maxItems 5) — Additional emails for delivery note dispatch (administration, accounting). Maximum 5. Empty `[]` when there are none.
- `subtotal` (number, required)
- `taxes_total` (number, required)
- `total` (number, required)
- `currency` (string, required)
- `notes` (string | null, required)
- `external_id` (string | null, required, maxLength 100) — External integration key (ERP/CRM/e-commerce) mapping this document to a record in a third-party system. Free-format, unique per company, filterable via `?external_id=`. `null` when not set. Persistent synchronization key, independent of the request-level `Idempotency-Key`.
- `lines` (array, required)
- `metadata` (object (Metadata) | null, required) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `tags` (array, required, maxItems 30) — Free classification tags (lowercase slugs `[a-z0-9-]`, ≤ 40 chars each, ≤ 30 tags). Filterable via `?tags[in]=tag1,tag2` (JSON_CONTAINS, OR semantics). Empty `[]` when there are none.
- `custom_fields` (array, required, maxItems 50) — Ordered list of typed custom fields `[{field, value}]` (≤ 50). Distinct from `metadata` (a free key→value map): use `custom_fields` for structured, display-oriented integration metadata. Empty `[]` when there are none.
- `converted_to_id` (string | null, required) — UUID (v7) of the invoice this delivery note was converted into.
- `created_at` (string | null, required, format: date-time)
- `updated_at` (string | null, required, format: date-time)
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The delivery note request conflicts with its current state — e.g. an invalid status transition (signing an already-signed delivery note), an attempt to sign a non-delivered note, or a reused idempotency key.
- **422** — Validation failed, or the delivery note cannot undergo the requested state transition (e.g. signing a non-delivered note).
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/delivery_notes/{delivery_note}/convert — Convert delivery note to invoice
- **Operation ID**: `public-api.v1.delivery_notes.convert`
- **Tag**: Delivery Notes
- **Required scope**: `delivery_notes:transition` — Change the lifecycle status of delivery notes.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/delivery-notes/public-api.v1.delivery_notes.convert
Convert a delivery note into a sales invoice. The delivery note moves to `invoiced` with `converted_to_id` populated and the new invoice is returned under `data`. Only `target=invoice` is supported.
## Path parameters
- `delivery_note` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 2 properties; 1 required: `target`.
- `target` (string, required, enum: `invoice`)
- `target_series_id` (string | null, optional, format: uuid)
## Responses
- **201**
- Body (`application/json`):
- `data` (object (Invoice), required) — A sales invoice (compliant with Spanish AEAT VeriFactu).
- `id` (string, required)
- `object` (string, required, enum: `invoice`)
- `number` (string | null, required) — Definitive invoice number, or `null` for drafts (where `is_number_assigned` is `false`).
- `is_number_assigned` (boolean, required) — Whether the invoice has a definitive number assigned. `false` for drafts (where `number` is `null`); becomes `true` after `POST /v1/invoices/{uuid}/assign-real-number`, or automatically on send/payment.
- `type` (string, required) — AEAT invoice type code: `F1` (ordinaria), `F2` (simplificada), `F3` (sustitutiva de simplificadas), `R1`–`R5` (rectificativa).
- `series` (object (SeriesRef), required)
- `client` (object (ClientRef), required)
- `status` (string, required) — Invoice lifecycle status.
- `issued_on` (string, required, format: date)
- `due_on` (string | null, required, format: date)
- `subtotal` (number, required)
- `taxes_total` (number, required)
- `total` (number, required)
- `total_disbursements` (number, required) — Sum of the `SUPLIDO` (disbursement) lines of this invoice: amounts the issuer paid in the name and on behalf of the client and re-invoices at cost. Deliberately OUTSIDE `subtotal`, `taxes_total` and `total`, because a disbursement is not part of the issuer's taxable base (art. 78.Tres.3 LIVA) and is not declared in the AEAT VeriFactu record. `0` on an invoice without disbursements.
- `total_to_pay` (number, required) — Amount the client actually has to pay: `total + total_disbursements`. DERIVED, never stored — one single formula computes it — and equal to `total` on an invoice without disbursements. Worked example: a 1,000.00 service line at 21% plus a 150.00 `SUPLIDO` line yields `subtotal` 1000.00, `taxes_total` 210.00, `total` 1210.00, `total_disbursements` 150.00 and `total_to_pay` 1360.00. Note that `paid_amount`/`pending_amount` are measured against `total`, not against `total_to_pay`.
- `currency` (string, required) — ISO 4217 currency code (always "EUR" in v1).
- `notes` (string | null, required)
- `external_id` (string | null, required, maxLength 100) — External integration key (ERP/CRM/e-commerce) mapping this document to a record in a third-party system. Free-format, unique per company, filterable via `?external_id=`. `null` when not set. Persistent synchronization key, independent of the request-level `Idempotency-Key`.
- `lines` (array, required)
- `metadata` (object (Metadata) | null, required) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `tags` (array, required, maxItems 30) — Free classification tags (lowercase slugs `[a-z0-9-]`, ≤ 40 chars each, ≤ 30 tags). Filterable via `?tags[in]=tag1,tag2` (JSON_CONTAINS, OR semantics). Empty `[]` when there are none.
- `custom_fields` (array, required, maxItems 50) — Ordered list of typed custom fields `[{field, value}]` (≤ 50). Distinct from `metadata` (a free key→value map): use `custom_fields` for structured, display-oriented integration metadata. Empty `[]` when there are none.
- `operation_regime` (string, required) — AEAT VAT operation regime (`general`, `recargo_equivalencia`, `exenta`, etc.).
- `exemption_reason` (string | null, required) — VAT exemption cause per document (AEAT catalog E1..E6), or `null` when not exempt. Read-only: the public API derives the AEAT qualification from the regime/cause at issuance.
- `legal_mentions` (array, required) — Header legal mentions (includes the mention derived from the exemption cause). Read-only.
- `exclude_347` (boolean, required) — Read-only flag: whether this invoice is excluded from the annual Modelo 347 report. The public API cannot mutate it (the create/update FormRequest does not accept it); managing the flag is exclusive to the internal app.
- `verifactu_status` (string, required) — Status of the AEAT VeriFactu submission.
- `paid_amount` (number, required) — Amount already collected for this invoice (derived from the payment ledger). Satisfies the invariant `paid_amount + pending_amount === total`.
- `pending_amount` (number, required) — Outstanding balance pending collection for this invoice (derived from the payment ledger).
- `payments` (object, required) — Payment ledger summary, ALWAYS present (never `null`). `total` mirrors `paid_amount`, `pending` mirrors `pending_amount`. `detail` lists the individual payments and is materialized ONLY on the show endpoint (`GET /v1/invoices/{id}`); in list responses `detail` is `[]` (by cost) while `total`/`pending` stay populated. The detail is also available via `GET /v1/invoices/{id}/payments`.
- `is_corrective` (boolean, required) — Whether this invoice is a corrective (rectificativa) of another invoice.
- `corrective` (object (InvoiceCorrective) | null, required)
- `payment` (object (InvoicePayment) | null, required)
- `public_link` (object (PublicLink) | null, required)
- `substituted_by` (object (InvoiceSubstitutedBy) | null, required)
- `recurring` (object (InvoiceRecurring) | null, required)
- `paid_at` (string | null, required, format: date-time)
- `paid_on` (string | null, required, format: date)
- `sent_at` (string | null, required, format: date-time)
- `voided_at` (string | null, required, format: date-time)
- `void_reason` (string | null, required)
- `scheduled_for` (string | null, required, format: date-time) — Scheduled emission timestamp (ISO 8601), or `null` when the invoice is not scheduled. Populated only while `status` is `scheduled`.
- `scheduled_action` (string | null, required, enum: `draft`, `issue_and_send`, `null`) — Action the scheduler runs when `scheduled_for` is reached: `issue_and_send` (issue and email) or `draft` (issue only). `null` when the invoice is not scheduled.
- `created_at` (string, required, format: date-time)
- `updated_at` (string, required, format: date-time)
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The delivery note request conflicts with its current state — e.g. an invalid status transition (signing an already-signed delivery note), an attempt to sign a non-delivered note, or a reused idempotency key.
- **422** — Validation failed, or the delivery note cannot undergo the requested state transition (e.g. signing a non-delivered note).
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/delivery_notes — Create a delivery note
- **Operation ID**: `public-api.v1.delivery_notes.create`
- **Tag**: Delivery Notes
- **Required scope**: `delivery_notes:write` — Create and update delivery notes.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/delivery-notes/public-api.v1.delivery_notes.create
Create a new delivery note (albarán) in `draft` status. Delivery notes track goods shipped to a customer and can later be converted to invoices.
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 26 properties; 2 required: `client_id`, `lines`.
- `client_id` (string, required, format: uuid)
- `series_id` (string | null, optional, format: uuid)
- `delivery_date` (string | null, optional, format: date)
- `notes` (string | null, optional, maxLength 2000)
- `internal_notes` (string | null, optional, maxLength 2000)
- `reference_number` (string | null, optional, maxLength 255)
- `transport_details` (string | null, optional, maxLength 2000)
- `delivery_address` (string | null, optional, maxLength 500)
- `delivery_city` (string | null, optional, maxLength 255)
- `delivery_postal_code` (string | null, optional, maxLength 20)
- `delivery_province` (string | null, optional, maxLength 255)
- `delivery_country` (string | null, optional, maxLength 255)
- `vehicle_plate` (string | null, optional, maxLength 20) — Licence plate of the vehicle used for the delivery (up to 20 characters).
- `driver_name` (string | null, optional, maxLength 120)
- `driver_tax_id` (string | null, optional, maxLength 20)
- `tracking_number` (string | null, optional, maxLength 100)
- `carrier_company` (string | null, optional, maxLength 120)
- `received_by_name` (string | null, optional, maxLength 120)
- `received_by_tax_id` (string | null, optional, maxLength 20)
- `external_id` (string | null, optional, maxLength 100)
- `currency` (string | null, optional, enum: `EUR`)
- `metadata` (object (Metadata) | null, optional) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `billing_emails` (array | null, optional, maxItems 5)
- `tags` (array | null, optional, maxItems 30)
- `custom_fields` (array | null, optional, maxItems 50)
- `field` (string, required, maxLength 60, minLength 1)
- `value` (string, required, maxLength 500)
- `lines` (array, required)
- `description` (string, required, maxLength 255)
- `quantity` (number, required, min 0.01)
- `unit_price` (number, required, min 0)
- `tax_rate_id` (string | null, optional, format: uuid)
- `tax_rate` (number | null, optional, min 0, max 100)
- `retention_rate` (number | null, optional, min 0, max 100)
- `surcharge_rate` (number | null, optional, min 0, max 100)
- `retention_rate_id` (string | null, optional, format: uuid)
- `surcharge_rate_id` (string | null, optional, format: uuid)
- `product_id` (string | null, optional, format: uuid)
- `discount_percent` (number | null, optional, min 0, max 100)
- `indirect_tax_regime` (string | null, optional, enum: `iva`, `igic`, `ipsi`)
## Responses
- **201** — Delivery note created successfully. The `Location` header contains the canonical URL of the newly created resource.
- Body (`application/json`):
- `data` (object (DeliveryNote), required) — A delivery note tracking goods delivered to a customer.
- `id` (string, required)
- `object` (string, required, enum: `delivery_note`)
- `number` (string, required)
- `series` (object (SeriesRef), required)
- `client` (object (ClientRef), required)
- `status` (string, required, enum: `draft`, `sent`, `signed`, `invoiced`, `cancelled`) — Delivery note lifecycle status exposed by the public API: `draft` (created, editable), `sent` (goods marked as delivered, no signature recorded yet), `signed` (delivered AND a recipient signature has been recorded), `invoiced` (converted into an invoice, terminal), `cancelled` (terminal). Note: the internal `delivered` state is surfaced as `sent` (without signature) or `signed` (with signature) — there is no `delivered` value in the public API. Recording a signature on a `sent` note moves it to `signed` (it does not introduce a brand-new lifecycle stage; the signature presence is the only difference).
- `issued_on` (string | null, required, format: date)
- `delivery_date` (string | null, required, format: date)
- `signed_at` (string | null, required, format: date-time)
- `signed_by` (string | null, required)
- `signature_image_url` (string | null, required, format: uri)
- `vehicle_plate` (string | null, required) — License plate of the delivery vehicle.
- `driver` (object | null, required) — Delivery driver data. `null` when no sub-field is provided.
- `tracking` (object | null, required) — Shipment tracking data. `null` when no sub-field is provided.
- `received_by` (object | null, required) — Goods recipient data. `null` when no sub-field is provided.
- `billing_emails` (array, required, maxItems 5) — Additional emails for delivery note dispatch (administration, accounting). Maximum 5. Empty `[]` when there are none.
- `subtotal` (number, required)
- `taxes_total` (number, required)
- `total` (number, required)
- `currency` (string, required)
- `notes` (string | null, required)
- `external_id` (string | null, required, maxLength 100) — External integration key (ERP/CRM/e-commerce) mapping this document to a record in a third-party system. Free-format, unique per company, filterable via `?external_id=`. `null` when not set. Persistent synchronization key, independent of the request-level `Idempotency-Key`.
- `lines` (array, required)
- `metadata` (object (Metadata) | null, required) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `tags` (array, required, maxItems 30) — Free classification tags (lowercase slugs `[a-z0-9-]`, ≤ 40 chars each, ≤ 30 tags). Filterable via `?tags[in]=tag1,tag2` (JSON_CONTAINS, OR semantics). Empty `[]` when there are none.
- `custom_fields` (array, required, maxItems 50) — Ordered list of typed custom fields `[{field, value}]` (≤ 50). Distinct from `metadata` (a free key→value map): use `custom_fields` for structured, display-oriented integration metadata. Empty `[]` when there are none.
- `converted_to_id` (string | null, required) — UUID (v7) of the invoice this delivery note was converted into.
- `created_at` (string | null, required, format: date-time)
- `updated_at` (string | null, required, format: date-time)
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **409** — The delivery note request conflicts with its current state — e.g. an invalid status transition (signing an already-signed delivery note), an attempt to sign a non-delivered note, or a reused idempotency key.
- **422** — Validation failed, or the delivery note cannot undergo the requested state transition (e.g. signing a non-delivered note).
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# DELETE /v1/delivery_notes/{delivery_note} — Delete a delivery note
- **Operation ID**: `public-api.v1.delivery_notes.delete`
- **Tag**: Delivery Notes
- **Required scope**: `delivery_notes:delete` — Delete delivery notes.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/delivery-notes/public-api.v1.delivery_notes.delete
Delete a delivery note. Only `draft` notes without an assigned number can be deleted; any other state returns 409 `invalid_status_transition`.
## Path parameters
- `delivery_note` (string, required)
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **204** — No content
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The delivery note request conflicts with its current state — e.g. an invalid status transition (signing an already-signed delivery note), an attempt to sign a non-delivered note, or a reused idempotency key.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/delivery_notes/{delivery_note}/duplicate — Duplicate a delivery note
- **Operation ID**: `public-api.v1.delivery_notes.duplicate`
- **Tag**: Delivery Notes
- **Required scope**: `delivery_notes:write` — Create and update delivery notes.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/delivery-notes/public-api.v1.delivery_notes.duplicate
Create a new draft delivery note by copying lines, client, and metadata.
## Path parameters
- `delivery_note` (string, required)
## Request headers
- `Idempotency-Key` (string, optional, maxLength 255, minLength 1) — Client-generated opaque key (up to 255 characters; UUID v7 recommended) that makes retries safe: the first response is cached and replayed for repeats without re-executing the mutation.
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Responses
- **201**
- Body (`application/json`):
- `data` (object (DeliveryNote), required) — A delivery note tracking goods delivered to a customer.
- `id` (string, required)
- `object` (string, required, enum: `delivery_note`)
- `number` (string, required)
- `series` (object (SeriesRef), required)
- `client` (object (ClientRef), required)
- `status` (string, required, enum: `draft`, `sent`, `signed`, `invoiced`, `cancelled`) — Delivery note lifecycle status exposed by the public API: `draft` (created, editable), `sent` (goods marked as delivered, no signature recorded yet), `signed` (delivered AND a recipient signature has been recorded), `invoiced` (converted into an invoice, terminal), `cancelled` (terminal). Note: the internal `delivered` state is surfaced as `sent` (without signature) or `signed` (with signature) — there is no `delivered` value in the public API. Recording a signature on a `sent` note moves it to `signed` (it does not introduce a brand-new lifecycle stage; the signature presence is the only difference).
- `issued_on` (string | null, required, format: date)
- `delivery_date` (string | null, required, format: date)
- `signed_at` (string | null, required, format: date-time)
- `signed_by` (string | null, required)
- `signature_image_url` (string | null, required, format: uri)
- `vehicle_plate` (string | null, required) — License plate of the delivery vehicle.
- `driver` (object | null, required) — Delivery driver data. `null` when no sub-field is provided.
- `tracking` (object | null, required) — Shipment tracking data. `null` when no sub-field is provided.
- `received_by` (object | null, required) — Goods recipient data. `null` when no sub-field is provided.
- `billing_emails` (array, required, maxItems 5) — Additional emails for delivery note dispatch (administration, accounting). Maximum 5. Empty `[]` when there are none.
- `subtotal` (number, required)
- `taxes_total` (number, required)
- `total` (number, required)
- `currency` (string, required)
- `notes` (string | null, required)
- `external_id` (string | null, required, maxLength 100) — External integration key (ERP/CRM/e-commerce) mapping this document to a record in a third-party system. Free-format, unique per company, filterable via `?external_id=`. `null` when not set. Persistent synchronization key, independent of the request-level `Idempotency-Key`.
- `lines` (array, required)
- `metadata` (object (Metadata) | null, required) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `tags` (array, required, maxItems 30) — Free classification tags (lowercase slugs `[a-z0-9-]`, ≤ 40 chars each, ≤ 30 tags). Filterable via `?tags[in]=tag1,tag2` (JSON_CONTAINS, OR semantics). Empty `[]` when there are none.
- `custom_fields` (array, required, maxItems 50) — Ordered list of typed custom fields `[{field, value}]` (≤ 50). Distinct from `metadata` (a free key→value map): use `custom_fields` for structured, display-oriented integration metadata. Empty `[]` when there are none.
- `converted_to_id` (string | null, required) — UUID (v7) of the invoice this delivery note was converted into.
- `created_at` (string | null, required, format: date-time)
- `updated_at` (string | null, required, format: date-time)
- **401** — Missing or invalid API key.
- **403** — The API key lacks the required scope for this operation.
- **404** — The requested resource does not exist or belongs to another company.
- **409** — The delivery note request conflicts with its current state — e.g. an invalid status transition (signing an already-signed delivery note), an attempt to sign a non-delivered note, or a reused idempotency key.
- **429** — Rate limit exceeded.
- **500** — Unexpected server error.
---
# POST /v1/delivery_notes/find-by-external-id — Find a delivery note by external ID
- **Operation ID**: `public-api.v1.delivery_notes.find_by_external_id`
- **Tag**: Delivery Notes
- **Required scope**: `delivery_notes:read` — Read delivery notes.
- **Authentication**: `Authorization: Bearer `, `X-API-Key: ` or an OAuth 2.1 access token.
- **Docs**: https://docs.factuarea.com/api-reference/delivery-notes/public-api.v1.delivery_notes.find_by_external_id
Look up a single delivery note by its `external_id` (sent in the JSON body), the integration key that maps it to a record in a third-party system (ERP/CRM/e-commerce). Returns the matching delivery note or 404 `delivery_note_not_found` if no delivery note uses that external_id within your company.
## Request headers
- `Factuarea-Version` (string, optional, format: date) — Pin the API version (`YYYY-MM-DD`, Stripe-style date versioning) for this request; omit to use the key's pinned version, or the latest if none.
- `X-Active-Profile` (string, optional, format: uuid) — Operate on behalf of a child company (gestoría master key): pass its public `id` (UUID v7) and the request runs against that child's data without changing the key's scope, tier or environment (omit to use the key's own company).
## Request body
`application/json`, required. 1 property; 1 required: `external_id`.
Look up a delivery note by its `external_id` (the integration key that maps it to a record in a third-party ERP/CRM/e-commerce system) within your company.
- `external_id` (string, required, maxLength 100)
## Responses
- **200**
- Body (`application/json`):
- `data` (object (DeliveryNote), required) — A delivery note tracking goods delivered to a customer.
- `id` (string, required)
- `object` (string, required, enum: `delivery_note`)
- `number` (string, required)
- `series` (object (SeriesRef), required)
- `client` (object (ClientRef), required)
- `status` (string, required, enum: `draft`, `sent`, `signed`, `invoiced`, `cancelled`) — Delivery note lifecycle status exposed by the public API: `draft` (created, editable), `sent` (goods marked as delivered, no signature recorded yet), `signed` (delivered AND a recipient signature has been recorded), `invoiced` (converted into an invoice, terminal), `cancelled` (terminal). Note: the internal `delivered` state is surfaced as `sent` (without signature) or `signed` (with signature) — there is no `delivered` value in the public API. Recording a signature on a `sent` note moves it to `signed` (it does not introduce a brand-new lifecycle stage; the signature presence is the only difference).
- `issued_on` (string | null, required, format: date)
- `delivery_date` (string | null, required, format: date)
- `signed_at` (string | null, required, format: date-time)
- `signed_by` (string | null, required)
- `signature_image_url` (string | null, required, format: uri)
- `vehicle_plate` (string | null, required) — License plate of the delivery vehicle.
- `driver` (object | null, required) — Delivery driver data. `null` when no sub-field is provided.
- `tracking` (object | null, required) — Shipment tracking data. `null` when no sub-field is provided.
- `received_by` (object | null, required) — Goods recipient data. `null` when no sub-field is provided.
- `billing_emails` (array, required, maxItems 5) — Additional emails for delivery note dispatch (administration, accounting). Maximum 5. Empty `[]` when there are none.
- `subtotal` (number, required)
- `taxes_total` (number, required)
- `total` (number, required)
- `currency` (string, required)
- `notes` (string | null, required)
- `external_id` (string | null, required, maxLength 100) — External integration key (ERP/CRM/e-commerce) mapping this document to a record in a third-party system. Free-format, unique per company, filterable via `?external_id=`. `null` when not set. Persistent synchronization key, independent of the request-level `Idempotency-Key`.
- `lines` (array, required)
- `metadata` (object (Metadata) | null, required) — A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
- `tags` (array, required, maxItems 30) — Free classification tags (lowercase slugs `[a-z0-9-]`, ≤ 40 chars each, ≤ 30 tags). Filterable via `?tags[in]=tag1,tag2` (JSON_CONTAINS, OR semantics). Empty `[]` when there are none.
- `custom_fields` (array