---
title: "Update an invoice series"
method: PATCH
path: "/v1/companies/{company_id}/series/{series_id}"
tags: ["CompanySeries"]
---

# Update an invoice series

`PATCH /v1/companies/{company_id}/series/{series_id}`

## Request

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

Updates only the fields present in the body, leaving every other field of the series as
it is.

- **Clearing a field:** a field sent as `null` is cleared, which only `description`
  supports.
- **Numbering fields:** `code`, `format`, `counter_reset` and `initial_number` are rejected
  once the series has issued invoices (`numbering_locked` is `true`).
- **Unique numbers per issuer:** changing `code`, `format` or `document_type` so that the
  series could print a number another series of the company can also print is rejected
  with `409 SERIES_FORMAT_OVERLAPS`.
- **`default_series`:** it cannot be used to clear the default. Sending `false` for the
  series that currently is the default answers `DEFAULT_CANNOT_BE_UNMARKED`, because it
  would leave the document type with active series and no default, and issuing without an
  explicit `series_id` would then fail with `SERIES_DEFAULT_NOT_FOUND`. Hand the default
  over with `PUT /v1/companies/{company_id}/series/{series_id}/default` on the new series,
  which unmarks the previous one. Sending `false` for a series that is not the default is a
  no-op.

## Request body

- PatchSeriesRequest: Partial update of an invoice series (RFC 5789). Only the fields present in the body are touched: - **field omitted** → the current value is kept; - **field sent with a value** → replaced; - **field sent as `null`** → cleared (only `description`, the one field a series can live without). The same rules as `PUT` apply: the fields that drive numbering (`code`, `format`, `counter_reset`, `initial_number`) and `document_type` are rejected once the series has issued invoices (`document_type` can still be set on an `UNASSIGNED` series), so renaming a series in production keeps working. Moving a series to `document_type: UNASSIGNED` is rejected with `422 SERIES_UNASSIGNED_TYPE_NOT_ALLOWED`; a series that already is `UNASSIGNED` can be edited without changing its type.
  - `document_type` 'UNASSIGNED' | 'STANDARD' | 'SIMPLIFIED' | 'CORRECTIVE' | 'SIMPLIFIED_CORRECTIVE' | 'PROFORMA': 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: Descriptive name of the series. Cannot be cleared.
  - `code` string: Alphanumeric series code (used in {CODIGO} variable). Allows uppercase letters, numbers, hyphens and underscores.
  - `description` string, nullable: Series description. Send `null` to clear it.
  - `format` string: 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': Counter reset policy: - NEVER: Counter never resets (continuous numbering) - ANNUAL: Counter resets yearly - MONTHLY: Counter resets monthly
  - `initial_number` integer: Initial number for this series counter. Only while the series has no issued invoices. 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: Whether the series is active. **Restriction:** A default series cannot be deactivated (another must be set as default first).
  - `default_series` boolean: Whether this is the default series. **Restriction:** An inactive series cannot be marked as default.

## Response `200`

Series updated successfully

- 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.
    - `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

- `400`: The update is rejected. Error codes: - `SERIES_CODE_LOCKED_HAS_INVOICES`, `SERIES_FORMAT_LOCKED_HAS_INVOICES`, `SERIES_RESET_LOCKED_HAS_INVOICES`, `SERIES_INITIAL_NUMBER_LOCKED_HAS_INVOICES`, `SERIES_DOCUMENT_TYPE_LOCKED_HAS_INVOICES` — the series already consumed a number (`numbering_locked` is `true`) - `DEFAULT_CANNOT_BE_UNMARKED` — `default_series: false` on the series that currently is the default; promote another one with `PUT .../series/{series_id}/default` instead - `DEFAULT_CANNOT_DEACTIVATE` — `active: false` on the default series - `INACTIVE_CANNOT_BE_DEFAULT` — `default_series: true` on an inactive series - `REFERENCED_BY_PAYMENT_CONNECTION` — deactivating a series a payment connection is configured to use - `REFERENCED_BY_RECURRING_INVOICE` — deactivating a series with a live (ACTIVE or PAUSED) recurring invoice template using it. Finish it or switch it to another series first - `SERIES_NUMBERING_FROZEN` — the series issued its first number while this change was being applied, so its numbering can no longer change. Read it again: the `*_LOCKED_HAS_INVOICES` codes above name the field
- `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`.
- `404`: No series with that id under this company. A series belonging to another NIF answers the same way: under the scope of the NIF in the path it simply does not exist.
- `409`: `SERIES_FORMAT_OVERLAPS` — with the change, the series could print numbers another series of the company prints. `SERIES_DEFAULT_CONFLICT` — another request made a different series the default for the same document type at the same time; read the series again and retry.
- `422`: The series is rejected. `error.code` is `VALIDATION_ERROR` when the body parses but a value is not acceptable: `details` is a flat map from the offending property's name (in the contract's `snake_case`) to a message describing what is wrong with it, or follows `FieldDeserializationError` when the value is outside an enum's vocabulary. Otherwise it is one of the series rules, among them: - `SERIES_MONTHLY_REQUIRES_MONTH_AND_YEAR` / `SERIES_ANNUAL_REQUIRES_YEAR` — the format cannot tell the counter reset periods apart. - `SERIES_INITIAL_NUMBER_OUT_OF_RANGE` — the initial number is out of range. - `SERIES_FORMAT_NUMBER_TOO_LONG` — the longest number the series can generate (the format with the code, the year, the month and the counter substituted) exceeds the 60 characters the AEAT accepts for the invoice number. The counter counts as at least 9 digits, with or without padding: `{NUM:X}` is a minimum width and the number keeps growing past it. - `SERIES_FORMAT_INVALID_CHARACTERS` — that number would contain a character the AEAT does not accept: only printable ASCII (32-126) is allowed, except `"`, `'`, `<`, `>` and `=`. - `SERIES_TYPE_CHANGE_LEAVES_TYPE_UNCOVERED` — changing `document_type` of the default series would leave its current document type, which still has other active series, without a default. Make one of those the default first. - `SERIES_COMPANY_DELETED` — the company was deleted while the request was running: its series keep their numbering but can no longer be activated or made the default.
- `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)
