---
title: "Ensure a default series per document type"
method: PUT
path: "/v1/companies/{company_id}/series/defaults"
tags: ["CompanySeries"]
---

# Ensure a default series per document type

`PUT /v1/companies/{company_id}/series/defaults`

## Request

- Base URL: `https://app.beel.es/api`
- URL: `https://app.beel.es/api/v1/companies/{company_id}/series/defaults`
- Auth: HTTP bearer

Ensures the company has a default invoice series for `STANDARD`, `SIMPLIFIED` and
`CORRECTIVE` in the current environment, and returns the resulting set. The request takes
no body: the desired end state is one default per document type, so repeating it changes
nothing.

- **Already there:** a document type that already has a default keeps it, and it is
  returned unchanged.
- **Missing:** it is created with code `F`, `S` or `R` and format
  `{CODIGO}-{YYYY}-{NUM:4}`, active and marked as default.
- **Code taken:** if that code already belongs to another series, the document type is
  omitted from the response and is left with no default.

**Closed catalogue.** This collection is fixed and bounded — one entry per `DocumentType`:
it carries no `pagination`, it takes no `page`/`limit`, and every response holds the whole
set.

## Headers

- `Idempotency-Key` string

## Response `200`

Default series ensured

- object: The envelope every successful JSON response of the BeeL. API is wrapped in. There are no bare resources in v1 and none are planned: the payload always hangs off `data`. - A **single resource** is an object in `data`. - A **collection** hangs off a named key inside `data`, together with its `pagination`, also inside `data` — `data: {invoices: [...], pagination: {...}}`. - A collection carries `pagination` unless its operation declares itself a **closed catalogue**: a fixed, bounded list with nothing to page through. The declaration is explicit in the operation; a missing `pagination` is never something to infer. - `GET /v1/accounts` pages by cursor (`data: {accounts: [...], next_cursor}`). It is a documented variant of pagination, not another envelope. Putting the array straight into `data` with `pagination` as its sibling is the shape a v2 would adopt; v1 is not being flipped to it.
  - `success` boolean, required
  - `data` object, required: The payload. An object for a single resource; an object holding the named collection (and its `pagination`) for a listing. Never a bare array at this level in v1.
    - `series` InvoiceSeries[], required
      - `id` string, uuid, required: Unique series ID
      - `document_type` 'UNASSIGNED' | 'STANDARD' | 'SIMPLIFIED' | 'CORRECTIVE' | 'SIMPLIFIED_CORRECTIVE' | 'PROFORMA', required: Document type associated with a series. Values mirror `InvoiceType`, so the series a document needs is named exactly like the document: - UNASSIGNED: Legacy value of series created before types existed. A series numbers only documents of its own type, so an `UNASSIGNED` series numbers none (`422 SERIES_INCOMPATIBLE_DOC_TYPE`); give it a type to keep using it. No series can be created with it or moved to it (`422 SERIES_UNASSIGNED_TYPE_NOT_ALLOWED`). The live ones were given the type they numbered most. - STANDARD: Standard invoice - SIMPLIFIED: Simplified invoice - CORRECTIVE: Corrects or cancels a previous standard invoice (R1–R4) - SIMPLIFIED_CORRECTIVE: Corrects a simplified invoice (R5). Spanish law requires R5 to be numbered apart from R1–R4, so they have a series type of their own - PROFORMA: Proforma (commercial document, non-fiscal numbering)
      - `name` string, required: Descriptive name of the series
      - `code` string, required: Alphanumeric series code (used in {CODIGO} variable). Allows uppercase letters, numbers, hyphens and underscores.
      - `description` string: Optional series description
      - `format` string, required: Format template with available variables (UPPERCASE ONLY): - {CODIGO}: Series code (e.g., "FAC") - {YYYY}: Year with 4 digits (e.g., "2025") - {YY}: Year with 2 digits (e.g., "25") - {MM}: Month with 2 digits (e.g., "01") - {NUM}: Sequential number without padding (e.g., "1") - {NUM:X}: Sequential number with padding (e.g., {NUM:4} → "0001") **REQUIRED**: Must contain at least {NUM} or {NUM:X} **IMPORTANT**: Only uppercase (rejects {yy}, {mm}, {codigo}, etc.) Valid examples: - "{CODIGO}-{YYYY}-{NUM:4}" → "FAC-2025-0001" - "{CODIGO}/{NUM:6}" → "FAC/000001" - "{YYYY}{MM}-{NUM:3}" → "202501-001" The generated number is the invoice number sent to the AEAT, which accepts at most 60 printable ASCII characters and none of `"`, `'`, `<`, `>`, `=`. A format whose longest possible number breaks that rule is rejected with `422 SERIES_FORMAT_NUMBER_TOO_LONG` or `SERIES_FORMAT_INVALID_CHARACTERS`. The counter counts as at least 9 digits, with or without padding: `{NUM:X}` is a minimum width, not a maximum.
      - `counter_reset` 'NEVER' | 'ANNUAL' | 'MONTHLY', required: Counter reset policy: - NEVER: Counter never resets (continuous numbering) - ANNUAL: Counter resets yearly - MONTHLY: Counter resets monthly
      - `initial_number` integer: Initial number configured for this series counter: the number the series starts from. It applies only to the first period in which the series issues an invoice: with `counter_reset: ANNUAL` or `MONTHLY`, every later year or month starts at 1. With `NEVER` there is a single period, so numbering simply continues from it.
      - `active` boolean, required: Indicates whether the series is active
      - `default_series` boolean, required: Indicates whether this is the user's default series (only one can be)
      - `numbering_locked` boolean: `true` once this series has issued at least one invoice. From that point the fields that drive numbering (`code`, `format`, `counter_reset`, `initial_number`) are permanently locked and any attempt to change them is rejected — numbering can only be adjusted **before** the first invoice of a series. Cosmetic fields (`name`, `description`) remain editable. To use a different numbering, create a new series.
      - `created_at` string, date-time: Creation date
      - `next_number` integer: Next invoice number that will be assigned for this series
      - `updated_at` string, date-time: Last update date
  - `meta` ResponseMeta
    - `timestamp` string, date-time
    - `request_id` string

## Other responses

- `401`: Missing or invalid authentication. Like every other error, `message`/`detail` is localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English when the header is missing or asks for none of those.
- `403`: Your account does not own or manage this company, or does not hold the required access over it. A NIF that does not exist answers the same way. That case answers `error.code` `ACTIVE_COMPANY_NOT_ACCESSIBLE`.
- `429`: Rate limit exceeded. `RATE_LIMIT_EXCEEDED` when the request rate of your credential is over its limit; `TOO_MANY_REQUESTS` when an email the operation sends is held back because too many went out to that recipient in a short time. Either way, retry after the `Retry-After` seconds. `QUOTA_EXCEEDED` is the other `429`: the operation would go over a quota of your plan for the current period (issued invoices, for instance). Retrying does not help until the quota resets or the plan changes. `error.details` carries `feature_code`, `current_usage`, `quota_limit` and `reset_date`, and `RateLimit-Reset` the seconds until the reset when it is known.
- `500`: Internal server error
- `default`: Any status code the operation does not list above. Every operation declares it, so a generated client always has a branch to fall into and never loses the cause of a failure it did not anticipate. This is where the transport-level answers land — `405`, `406`, `415` and `429` — together with any status a future version of the API starts returning. All of them carry the same error envelope as the codes listed explicitly, so `error.code` is what tells them apart: switching on the status code alone is not enough. See «Transport-level errors» in the API description for when each one is produced. A `402` carrying `SUBSCRIPTION_REQUIRED` lands here when the account has no active subscription. `error.details` says why and what to do: `current_status` (absent if it never had one), `required_permission` and `action`. A `503` carrying `SERVICE_UNAVAILABLE` lands here too, with a `Retry-After` header: a dependency of BeeL itself (the database, the email provider) was momentarily unavailable and nothing was done. Retry after those seconds. A `502` carrying `EXTERNAL_SERVICE_ERROR` also lands here: an outbound integration the operation depends on failed or did not answer in time. It is a transient condition — retry with the same `Idempotency-Key` where the operation accepts one. One exception to the envelope: a failure of the network edge that never reaches the application (`502`, `503`, `504`, `524`) is generated by Cloudflare and its body is not BeeL's — it may not even be JSON. Treat those as "no answer", and retry.

---

[API](https://skmtc.dev/beel/apis/beel-public-api.md) · [All operations](https://skmtc.dev/beel/apis/beel-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/beel/apis/beel-public-api/revisions/ffbc0ae31f79?raw)
