Skip to main content
Every non-2xx response uses the same application/json envelope:
  • code is a stable, machine-readable identifier. Branch your integration on this value. It never changes meaning, so it is safe to compare against.
  • detail is a human-readable sentence meant for logs and debugging. Treat it as informational only: the wording can change at any time, so never match on it.
  • errors is an array of field-level problems, present mainly on invalid_request. Each entry has a field (the parameter that failed), a code (a short reason), and a message (a human explanation). It is an empty array when no single field is at fault.
  • doc_url links to this page, anchored on the section for the returned code, so a logged error carries a clickable reference to its documentation.

HTTP status codes

The HTTP status reflects the broad category of the failure, while code identifies the exact case:
  • 4xx: the request was rejected as sent. Fix the request before retrying (bad input, missing or invalid key, resource not found).
  • 5xx: the request failed on our side. It is safe to retry, ideally with backoff.

Handle unknown codes gracefully

This catalog grows over time. We may add new code values, and new endpoints may return existing codes in new places. Treat the set as open: branch on the codes you know, and fall back to handling anything unrecognized by its HTTP status (for example, retry on 5xx, surface a generic message on 4xx) instead of failing.

Error codes

invalid_request

HTTP 400 The request was rejected during validation. The errors array lists each failing field with a reason, so you can map the failure back to a specific parameter. Raised by: GET /v1/invoices · POST /v1/invoices · POST /v1/invoices/validate · POST /v1/number_series · GET /v1/number_series · POST /v1/customer_portal/sessions · POST /v1/tax_ids/validate.

unauthorized

HTTP 401 Authentication failed: the API key is missing, malformed, or revoked. Send a valid key as a bearer token in the Authorization header. Each key is bound to a single environment (test or live).

no_active_subscription

HTTP 402 Live invoice issuance requires an active subscription. When the account has no active plan, live keys receive this error until a subscription is in place. Test-environment keys are unaffected. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

aeat_mandate_not_approved

HTTP 403 Live invoice issuance requires the company’s AEAT representation mandate to be approved, which happens during onboarding. Until then, live keys receive this error; test-environment keys can issue freely (test invoices are never sent to AEAT). Raised by: POST /v1/invoices · POST /v1/invoices/validate.

customer_not_found

HTTP 404 No customer matches the supplied external_customer_id in the environment the API key belongs to. A customer recorded with a live key is not visible to a test key, and vice versa.

invoice_not_found

HTTP 404 No invoice matches the supplied identifier in the environment the API key belongs to. An invoice created with a live key is not visible to a test key, and vice versa. Raised by: GET /v1/invoices/{id} · GET /v1/invoices/{id}/download.

number_series_not_found

HTTP 404 No number series matches the supplied identifier in the environment the API key belongs to. A number series created with a live key is not visible to a test key, and vice versa. Raised by: GET /v1/number_series/{id} · DELETE /v1/number_series/{id}.

customer_ambiguous

HTTP 409 The environment has more than one integration, so external_customer_id on its own does not identify a single customer (the same id can exist in two integrations for different customers). Supply integration_id alongside external_customer_id to scope the session to one integration. You do not need it when the environment has a single integration. Find the id in the Finseed app under Configuración > Integraciones: open the integration and copy the “ID de integración” shown below its name. See Create a customer portal session for the full parameter reference. Raised by: POST /v1/customer_portal/sessions.

idempotency_conflict

HTTP 409 The supplied Idempotency-Key cannot be honored. This happens in three situations: the key was already used with a different request body; the original request with this key is still being processed, in which case retrying with the same key and body once it completes replays its response; or the original request ended in an unknown state, in which case the key stays blocked for up to 24 hours. If you never received a stored response, send the request again with a fresh key. Raised by: POST /v1/invoices · POST /v1/number_series.

number_series_in_use

HTTP 409 The number series cannot be deleted because invoices have been issued on it or an integration is configured to use it. A series is immutable once created (there is no update endpoint), so the way to change numbering is to create a new series and issue future invoices on it. A series referenced by an integration can be deleted after the integration is pointed elsewhere. Raised by: DELETE /v1/number_series/{id}.

number_series_prefix_taken

HTTP 409 The prefix of the series being created is already taken in this environment. Prefixes are normalized before the uniqueness check (whitespace is removed and letters are uppercased), so two prefixes that only differ in casing or spacing are the same prefix. Pick a different prefix, or reuse the existing series. Raised by: POST /v1/number_series. HTTP 410 The download_url returned by the download endpoint has expired or was altered. These links are time-limited bearer URLs; request a new one from the download endpoint rather than storing them indefinitely.

external_reference_invalid

HTTP 422 The external original reference is malformed: the number exceeds 60 characters or contains forbidden characters (", ', <, >, =), or issued_on is not a valid calendar date. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

external_reference_matches_existing_invoice

HTTP 422 The external number matches an invoice that was actually issued through Finseed. Rectify it as a local invoice with rectifies.invoice_id; rectifies.external is only for originals that never existed in Finseed. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

family_balance_below_zero

HTTP 422 A rectificativa por diferencias adjusts the original by a delta. This delta would make the accumulated total of the correction chain negative, which is not a valid invoice. Reduce the delta. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

insufficient_recipient_data

HTTP 422 The recipient block does not meet the legal minimums for a complete invoice (for example a too-short name or tax id, or an unrecognized country code). Fix the flagged recipient fields or issue a simplified invoice when no identified recipient exists. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

invalid_number_series

HTTP 422 The number_series_id in the request body cannot be used to issue this invoice: it is malformed, belongs to another environment, or names a rectifying series (reserved for corrective invoices). List the usable series with the Number Series endpoints. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

invalid_recargo_equivalencia

HTTP 422 The recargo_equivalencia flag requires every line to carry exactly one vat tax whose rate has a legal surcharge pairing (21%→5.2%, 10%→1.4%, 4%→0.5%). Lines with other tax types or unpaired VAT rates cannot be invoiced under this regime. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

invalid_rectifies_reference

HTTP 422 The rectifies block did not name exactly one original to correct. Supply either invoice_id for an invoice issued through Finseed, or external for one issued elsewhere — not both, and not neither. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

invalid_tax_data

HTTP 422 The combination of tax types, rates and computed amounts is one the tax agency rejects (for example a positive rate that yields a zero tax amount on a non-zero line). Review the tax on each line. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

invalid_tax_id

HTTP 422 The recipient tax id failed just-in-time validation. Either the format is invalid for its type, or the census lookup (AEAT for Spanish NIFs, VIES for EU VAT numbers) did not recognize the id together with the supplied name. Verify the id and use the exact name the recipient is registered under. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

invalid_tax_rate

HTTP 422 A line item carries a tax rate the tax agency (AEAT) would reject for that tax type on the current date, a rate on a rate-free type, or a missing rate on a rated type. Spanish VAT (vat) currently accepts 0, 4, 10 and 21 percent for invoices issued today; historical temporary rates (2, 5, 7.5) only applied to past operation windows. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

invoice_already_replaced

HTTP 422 The referenced invoice has already been fully replaced by a rectificativa por sustitución. Only the active head of the correction chain can be rectified; rectify the replacement instead. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

invoice_already_voided

HTTP 422 The referenced invoice was voided (anulada). A voided invoice is legally cancelled, so it cannot be corrected with a rectificativa. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

invoice_not_family_tip

HTTP 422 The referenced invoice is not the active head of its correction chain: a later rectificativa already corrects it. Rectify the current head of the chain instead. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

invoice_not_rectifiable_at_aeat

HTTP 422 The referenced invoice is not in a state the tax agency (AEAT) allows a rectificativa against: its registration failed, was skipped, or never happened. Only invoices that registered successfully can be rectified. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

invoice_pending_at_aeat

HTTP 422 The referenced invoice has not finished registering at the tax agency (AEAT). Poll it until verifactu.status leaves pending, then rectify it. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

line_item_without_tax_items

HTTP 422 A rectificativa line item was submitted without a tax. Every line must declare its tax so the corrective document reconciles with the tax agency. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

reason_incompatible_with_recipient

HTTP 422 The chosen reason restricts which recipient tax id types are allowed. insolvency accepts a Spanish NIF, an EU VAT number, or an unregistered recipient; uncollectible accepts a Spanish NIF or an unregistered recipient. The recipient on this invoice does not meet that restriction. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

recipient_overrides_not_allowed_for_differences

HTTP 422 A rectificativa por diferencias inherits its recipient from the corrected invoice, so a recipient block cannot be supplied. Omit it, or issue a rectificativa por sustitución (method: "substitution") when the recipient must change. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

rectified_amounts_required_for_substitution

HTTP 422 When replacing an external original by substitution, the tax agency requires the original amounts (ImporteRectificación), which Finseed cannot compute because the original never existed here. Supply rectifies.external.rectified_amounts. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

rectified_invoice_not_found

HTTP 422 The rectifies.invoice_id does not match any invoice in the environment the API key belongs to. Use the opaque inv_ identifier of an invoice issued through Finseed; to correct an invoice issued elsewhere, use rectifies.external instead. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

rectifying_series_invalid

HTTP 422 A rectificativa must be issued on a rectifying series. The number_series_id is malformed, belongs to another environment, or names a standard series. List your rectifying series with the Number Series endpoints, or omit it to use the environment default. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

replacement_total_negative

HTTP 422 A rectificativa por sustitución restates the full invoice, so its net total cannot be negative. Adjust the line items so the total is at or above zero. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

simplified_amount_exceeded

HTTP 422 Spanish law caps simplified invoices at 3,000 EUR including tax. Above that amount the recipient must be identified: send type: "complete" with a full recipient block. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

simplified_incompatible_tax_type

HTTP 422 The invoice combines type: "simplified" with a tax type that legally requires an identified recipient, such as es_inversion_sujeto_pasivo (reverse charge). Issue a complete invoice for these operations. Raised by: POST /v1/invoices · POST /v1/invoices/validate.

tax_id_type_country_mismatch

HTTP 422 The supplied tax_id_type and tax_id_country contradict each other. Each type constrains the country: es_nif and not_registered require ES; eu_vat requires an EU country other than ES; official_document, residence_certificate and other require a country other than ES; passport accepts any country. Fix the pair, or omit tax_id_country when the type implies it (or omit both to auto-detect them from the tax id). Raised by: POST /v1/invoices · POST /v1/invoices/validate.

rate_limited

HTTP 429 The per-key rate limit for this endpoint was exceeded. The response carries a Retry-After header with the seconds to wait. Spread out bursts or batch your work; sustained limits are documented per endpoint. Raised by: POST /v1/invoices/validate · POST /v1/tax_ids/validate.

internal_error

HTTP 500 An unexpected error occurred on our side. No partial work is persisted, so the request can be retried safely.

tax_id_validation_unavailable

HTTP 503 Complete invoices validate the recipient tax id against the census (AEAT or VIES) at creation time. When that external service is down, creation fails with this code and nothing is persisted. Retry later; sending the same Idempotency-Key makes the retry safe. Raised by: POST /v1/invoices · POST /v1/invoices/validate.