NewProvince is only required for addresses in Spain
BeeL
Get startedMulti-NIFVeriFactuRulesStripeAPI referenceChangelog

A new series needs its document type, and what else changes in this release

A new series needs document_type, several rejections now carry a specific code or status, and an issued invoice's PDF never changes. Plus behaviour changes.


ChangelogBreaking

Several changes can break a request that works today: a new invoice series must say which documents it numbers, a NIF_IVA or any foreign identifier must be one AEAT accepts, recipient data that used to be ignored is now refused, and many rejections that answered a generic code now name their cause. The rest change what a working integration receives, without any request having to change. Existing series, invoices, numbers and PDFs are not touched.

What breaks

  • document_type is required when you create a series, on POST /v1/companies/{company_id}/series, on POST /v1/configuration/series and in options.series[] of the managed-account import and its preview. Without it the request answers 422 VALIDATION_ERROR. UNASSIGNED is no longer accepted for a new series, nor as the new type of an existing one: 422 SERIES_UNASSIGNED_TYPE_NOT_ALLOWED. Series that were UNASSIGNED now number only one type: see a series numbers only its own type.
  • A NIF_IVA must be one AEAT accepts. An alternative_id of type NIF_IVA is accepted only with the country of another EU Member State (422 ALTERNATIVE_ID_VAT_REQUIRES_EU_COUNTRY) and in that State's EU VAT number format, the country prefix (EL for Greece) followed by the national number (422 ALTERNATIVE_ID_VAT_INVALID_FORMAT). It applies when you create or edit a customer, create an invoice or a recurring invoice with the recipient inline, and when you issue, before a number is used. Until now such an invoice was issued and then refused by VeriFactu, ending up REJECTED with its number spent. A customer saved earlier with one of these identifiers can still be read and edited; to invoice it, change the identifier.
  • A simplified invoice with an identified recipient answers 422, not 400, and on every path. SIMPLIFIED_INVOICE_FORBIDS_IDENTIFIED_RECIPIENT now answers 422 when you create a SIMPLIFIED invoice whose recipient carries nif or alternative_id, when an edit changes its type or its recipient, when you issue it — one by one, in bulk or on schedule — and when you write a recurring invoice template. A draft created earlier with an identifier can still be edited, but it is not issued until it becomes STANDARD or loses the identifier. When you read invoices, an older simplified invoice may still carry a nif.
  • Specific codes instead of generic ones. Rejections that answered BUSINESS_RULE_VIOLATION or VALIDATION_ERROR now carry a code of their own, with the same HTTP status: if you branch on error.code, they reach a different branch. Sending an invoice: 400 INVOICE_DRAFT_NOT_SENDABLE for a draft or a scheduled invoice and 422 INVOICE_EMAIL_NO_RECIPIENTS with no recipients, on a single send and on a batch. Correctives: CORRECTIVE_RECENT_DUPLICATE, CORRECTIVE_TOTAL_ALREADY_EXISTS, CORRECTIVE_ORIGINAL_NOT_FOUND, CORRECTIVE_ORIGINAL_DELETED, CORRECTIVE_ORIGINAL_IS_DRAFT, CORRECTIVE_ORIGINAL_REQUIRED, RECTIFICATION_REASON_REQUIRED and ORIGINAL_REFERENCE_ONLY_ON_CORRECTIVE. An invoice with no recipient: RECIPIENT_NOT_PROVIDED. Fields: FIELD_TOO_LONG, FIELD_OUT_OF_RANGE, and INVALID_IRPF / INVALID_SURCHARGE on products as on invoice lines. Managed accounts: PROVISIONING_TAX_PROFILE_REQUIRED, PROVISIONING_EMAIL_REQUIRED, PROVISIONING_INVALID_CURSOR and CLAIM_TOKEN_EMAIL_REQUIRED. The VeriFactu representation: REPRESENTATION_NOT_FOUND, REPRESENTATION_DOCUMENT_NOT_STORED, REPRESENTATION_ALREADY_ACTIVE, REPRESENTATION_NOT_ACTIVE and NIF_NOT_CONFIGURED.
  • SERIES_DOCUMENT_TYPE_INCOMPATIBLE is retired. A series that does not fit the document's type answers 422 SERIES_INCOMPATIBLE_DOC_TYPE everywhere: when you create or edit an invoice, when you issue it, and when a recurring invoice template changes its type or series.
  • customer_id together with recipient data answers 422 RECIPIENT_CUSTOMER_AND_DATA_EXCLUSIVE, on create and on edit. Until now the other recipient fields were ignored and the invoice took the customer's data. Send one or the other.
  • A corrective with recipient answers 422 CORRECTIVE_RECIPIENT_NOT_ACCEPTED and nothing is created. A corrective always goes to the recipient of the invoice it corrects, with that invoice's data; the field used to be ignored. The one exception is correcting the recipient's data with an R4: see corrective invoices and voids.
  • alternative_id.country_code is required, except for PASSPORT and NOT_REGISTERED, which take ES when you omit it. Any other type without it answers 422 ALTERNATIVE_ID_COUNTRY_REQUIRED, naming the field, on a customer and on an invoice recipient. Until now an invoice recipient without it answered 400 VALIDATION_ERROR, even for a passport, and a customer without it was judged as Spanish and answered ALTERNATIVE_ID_SPAIN_INVALID_TYPE or ALTERNATIVE_ID_VAT_REQUIRES_EU_COUNTRY.
  • An unknown sort_by on the invoice lists answers 400 VALIDATION_ERROR, naming sort_by and the accepted fields, as customers and products already did. Until now it was ignored and the list came back in the default order. due_date is a new accepted field.
  • An empty element in a list filter answers 400 VALIDATION_ERROR naming the parameter, on every list: status=ISSUED, or status=ISSUED&status=. It used to fail with a 500. A list parameter sent entirely empty (status=) is now the same as omitting it.
  • Sending an invoice whose PDF does not exist yet answers 202, not an error. It happens right after issuing, and under VeriFactu while the invoice waits for the QR of its registration. The email goes out shortly after the PDF is stored (minutes, if VeriFactu or the PDF is delayed); sent_at in the 202 is when the request was accepted, and invoice.email.sent tells you when it left. If the invoice ends without a PDF, or its PDF is not stored within a day, the email is recorded as failed with its reason, and a failed email sends no webhook. The preview image answers 202 with Retry-After in the same case.
  • An invoice under VeriFactu that is not registered with the AEAT has no PDF. Downloading it, its preview image, or emailing it with the PDF answers 400 INVOICE_NOT_REGISTERED_NO_PDF at once: its registration was rejected before reaching the AEAT, or it was voided without being registered. verifactu.error_message says why.

Does this affect you?

  • If you create series from code, add document_type to the request.
  • If you parse error messages or depend on their language, send Accept-Language explicitly.
  • If your customers have both email and billing_emails, their invoices now go to billing_emails.
  • If you relied on initial_number restarting every year at the same number, create the series for next year yourself.
  • If you send customers from outside the EU as NIF_IVA, or EU VAT numbers without their country prefix, change them: use OTHER_DOCUMENT or COUNTRY_ID outside the EU, and the full number with its prefix inside it.
  • If you branch on the status of SIMPLIFIED_INVOICE_FORBIDS_IDENTIFIED_RECIPIENT, expect 422; better, compare error.code.
  • If you branch on error.code being BUSINESS_RULE_VIOLATION or VALIDATION_ERROR, or on SERIES_DOCUMENT_TYPE_INCOMPATIBLE, add the new codes to those branches.
  • If you send customer_id together with recipient fields, or a recipient on a corrective, remove them.
  • If you send alternative_id without country_code, add it.
  • If you pass sort_by to the invoice lists, check it is one of the accepted fields; if you build list filters by joining values with commas, make sure no element is empty.
  • If you call /send right after issuing, handle 202: the email is on its way, not sent yet.
  • If you store invoice PDFs, keep them: an issued invoice's PDF does not change after voiding or correcting it.

What else changed

  • Simplified invoices generated from Stripe never identify the customer. Below the connection's simplified threshold, for a customer with a NIF but no address, or when the connection has no default standard series, the simplified invoice keeps the name, email and address of the payment but not the NIF or alternative_id, and it is not linked to the customer. A customer who needs an identified invoice needs a charge at or above the threshold, with full fiscal data.
  • English is the default language. A request without Accept-Language, or with one that asks for none of es, en and ca, gets its messages in English.
  • A missing scope answers 403 first. A key without the scope of the operation gets 403 INSUFFICIENT_SCOPE before the body or the parameters are validated, instead of a 422 or 400 about them.
  • Invoice emails go to billing_emails before email. Without recipients in the request or in the invoice's email_config, an invoice goes to the customer's billing_emails, and to email only when there are none. It applies to /send, to the automatic send on issue and to recurring invoices. A corrective with send_automatically: true uses the customer's addresses too.
  • One number per issuer. Creating or editing a series that could print a number another series of the company prints answers 409 SERIES_FORMAT_OVERLAPS. Issuing a number another invoice of the company already has answers 400 SERIES_NUMBER_COLLISION, without issuing or using a number.
  • initial_number applies to the first period only. With an ANNUAL or MONTHLY reset, every later year or month starts at 1. No number already issued changes.
  • Invoice numbers fit the AEAT. A series whose longest number exceeds 60 characters, or would carry ", ', <, > or =, answers 422 when you create or edit it; an invoice number that breaks the rule at issue answers 422 without using the number.
  • VeriFactu: fewer, clearer webhooks. verifactu.status.updated is sent only when the public status changes. A temporary AEAT error keeps the invoice PENDING, with no error_code or error_message, while BeeL. retries it. A submission refused before it reached AEAT, or one BeeL. stopped waiting for, is REJECTED and now also sends the webhook; its error_message is BeeL.'s own text.
  • Addresses: country_code decides the country. country is accepted only as a real ISO code or the country's official name in Spanish, English or Catalan (UK answers 422 COUNTRY_CODE_REQUIRED; the code is GB), and a country that contradicts country_code answers 422 COUNTRY_CODE_MISMATCH. Responses always carry the code and the Spanish name derived from it.
  • Webhooks. Turning a paused subscription back on also counts towards the 10 active subscriptions: 400 WEBHOOK_ACTIVE_SUBSCRIPTION_LIMIT_REACHED, the code an 11th subscription now gets too. The new retry schedule is in its own entry.
  • Managed accounts. An account claimed by its holder with no NIF yet reads CLAIMED, not ACTIVE, in the account, the list and its status filter. Every account is born with one company, so POST /v1/accounts returns a company_id with or without a tax_profile.
  • Companies. Creating a company keeps the trade name and the whole address, and answers 502 EXTERNAL_SERVICE_ERROR when the AEAT census cannot be reached, storing nothing. A Spanish postal code without 5 digits answers 422 POSTAL_CODE_INVALID_ES. Changing nif, entity_type or legal_form answers 422 IMMUTABLE_…, and a legal_name change with the census down answers 200 and is checked again later.
  • Imports. A Holded file over 5,000 contacts or 10 MB is rejected whole with 400 TOO_MANY_RECORDS or CSV_FILE_TOO_LARGE, instead of being cut.
  • The invoice PDF prints the QR centred at the top of the first page, at the size the AEAT requires, with «QR tributario:» above it and the AEAT legend below, both centred. It applies to invoices issued from now on: the PDF of an invoice already issued is not generated again.
  • Filter invoices by payment method. The invoice lists accept payment_method, one value or several separated by commas (payment_method=DIRECT_DEBIT,CARD); NONE also matches invoices with no payment method stored.
  • Provisioning again keeps a valid claim link. Resending the external_ref of an unclaimed account, or re-importing it, no longer replaces a claim link that is still valid: claim_token and claim_url come back null with claim_link_already_issued: true, and no email is sent. A fresh link is issued only when none is valid; to replace one on purpose, use POST /v1/accounts/{account_id}/claim-tokens.
  • The VeriFactu records of an invoice list a registration refused before it reached AEAT as REJECTED until the invoice is sent again, when the new attempt takes its place. error_message is BeeL.'s own text when AEAT gave no answer, and error_code travels only when AEAT gave one.
  • The PDF of an issued invoice never changes. It is generated once, with its VeriFactu QR when that applies, and served as it was delivered. Voiding the invoice or issuing a corrective for it, including a TOTAL one, does not change the PDF or add a watermark: the status is in status and verifactu.submission_status. In the sandbox every PDF keeps the test-invoice watermark.
  • invoice.pdf.generated fires once for an issued invoice and carries data.generation, which numbers the stored PDF (1 for the first). It only goes past 1 in the exceptional case that BeeL. staff regenerate the PDF to fix a rendering defect. Events recorded before the field existed do not carry it: treat a missing value as unknown, not as 1.
  • The simplified-invoice cap is checked on every path. A SIMPLIFIED invoice over 3,000 € (VAT included) answers 400 SIMPLIFIED_INVOICE_EXCEEDS_LEGAL_LIMIT when it is created, as it always did, and now also when an edit takes it over the cap and when it is issued; an edit used to fail with a 500. The reference said 422 for this code: the status was always 400, and the reference now says so.
  • A recurring invoice template can change its type. PATCH accepts invoice_type; the change is judged on the resulting template with the rules of the new type, so send the new series_id, and customer_id: null when moving to SIMPLIFIED, in the same call.
  • Creating a company returns the whole company. The 201 carries every field GET /v1/companies/{company_id} returns, with the same values, plus series.
  • VeriFactu: a number AEAT already holds for another invoice. If AEAT already has a record with an invoice's number and issue date that is not that invoice, for example one issued with the software you used before, the invoice ends REJECTED, with no error_code and an error_message that says the number is taken. Issue it again with a number AEAT does not hold yet.
  • Customers in bulk. Every row error carries a code, the same one the single create answers or one of its own (CUSTOMER_DUPLICATED_IN_BATCH, CUSTOMER_FIELD_REQUIRED…). If the AEAT census cannot be reached, the whole request answers 502 or 503 and nothing is saved, instead of blaming the rows.
  • POST /v1/nif/validate answers a NIF with bad syntax with 200 and status: INVALID, as its reference says, instead of an error. Its message comes in the language of the request.
  • Webhook deliveries carry only the documented headers, plus User-Agent: BeeL-Webhooks, and whatever HTTP itself adds.
  • The daily cap on distinct recipient addresses applies per environment, and the sandbox now enforces it. The sandbox and production each count their own addresses, and the sandbox applies its published cap, which it did not until now: a sandbox integration that emails many different addresses in a day may start getting 429. Keep sandbox sends to a few addresses of your own.

Endpoints

  • POST/v1/companies/{company_id}/invoices422 SIMPLIFIED_INVOICE_FORBIDS_IDENTIFIED_RECIPIENT (was 400); also on PATCH, issue, bulk and recurring templates; 422 RECIPIENT_CUSTOMER_AND_DATA_EXCLUSIVE
  • POST/v1/companies/{company_id}/seriesdocument_type required; 409 SERIES_FORMAT_OVERLAPS
  • PATCH/v1/companies/{company_id}/series/{series_id}409 SERIES_FORMAT_OVERLAPS; UNASSIGNED no longer accepted
  • POST/v1/companies/{company_id}/invoices/{invoice_id}/issue400 SERIES_NUMBER_COLLISION; 422 when the number does not fit the AEAT
  • PATCH/v1/accounts/{account_id}/webhooks/{webhook_id}Turning a subscription back on counts towards the limit of 10
  • POST/v1/companies/{company_id}/customers422 ALTERNATIVE_ID_VAT_REQUIRES_EU_COUNTRY / ALTERNATIVE_ID_VAT_INVALID_FORMAT / ALTERNATIVE_ID_COUNTRY_REQUIRED
  • GET/v1/companies/{company_id}/invoicesNew payment_method filter
  • POST/v1/companies/{company_id}/invoices/{invoice_id}/send202 while the PDF does not exist yet; 400 INVOICE_DRAFT_NOT_SENDABLE (draft or scheduled) / INVOICE_NOT_REGISTERED_NO_PDF; 422 INVOICE_EMAIL_NO_RECIPIENTS
  • GET/v1/companies/{company_id}/invoices/{invoice_id}/pdf400 INVOICE_NOT_REGISTERED_NO_PDF; the PDF of an issued invoice never changes
  • POST/v1/companies/{company_id}/invoices/{invoice_id}/corrective422 CORRECTIVE_RECIPIENT_NOT_ACCEPTED and specific CORRECTIVE_* codes
  • GET/v1/companies/{company_id}/invoicesUnknown sort_by and empty list elements answer 400; new sort field due_date
  • PATCH/v1/companies/{company_id}/recurring-invoices/{recurring_invoice_id}Accepts invoice_type; 422 SERIES_INCOMPATIBLE_DOC_TYPE

Where to go next