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.
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_typeis required when you create a series, onPOST /v1/companies/{company_id}/series, onPOST /v1/configuration/seriesand inoptions.series[]of the managed-account import and its preview. Without it the request answers422 VALIDATION_ERROR.UNASSIGNEDis no longer accepted for a new series, nor as the new type of an existing one:422 SERIES_UNASSIGNED_TYPE_NOT_ALLOWED. Series that wereUNASSIGNEDnow number only one type: see a series numbers only its own type.- A
NIF_IVAmust be one AEAT accepts. Analternative_idof typeNIF_IVAis 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 (ELfor 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 upREJECTEDwith 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, not400, and on every path.SIMPLIFIED_INVOICE_FORBIDS_IDENTIFIED_RECIPIENTnow answers422when you create aSIMPLIFIEDinvoice whose recipient carriesniforalternative_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 becomesSTANDARDor loses the identifier. When you read invoices, an older simplified invoice may still carry anif. - Specific codes instead of generic ones. Rejections that answered
BUSINESS_RULE_VIOLATIONorVALIDATION_ERRORnow carry a code of their own, with the same HTTP status: if you branch onerror.code, they reach a different branch. Sending an invoice:400 INVOICE_DRAFT_NOT_SENDABLEfor a draft or a scheduled invoice and422 INVOICE_EMAIL_NO_RECIPIENTSwith 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_REQUIREDandORIGINAL_REFERENCE_ONLY_ON_CORRECTIVE. An invoice with no recipient:RECIPIENT_NOT_PROVIDED. Fields:FIELD_TOO_LONG,FIELD_OUT_OF_RANGE, andINVALID_IRPF/INVALID_SURCHARGEon products as on invoice lines. Managed accounts:PROVISIONING_TAX_PROFILE_REQUIRED,PROVISIONING_EMAIL_REQUIRED,PROVISIONING_INVALID_CURSORandCLAIM_TOKEN_EMAIL_REQUIRED. The VeriFactu representation:REPRESENTATION_NOT_FOUND,REPRESENTATION_DOCUMENT_NOT_STORED,REPRESENTATION_ALREADY_ACTIVE,REPRESENTATION_NOT_ACTIVEandNIF_NOT_CONFIGURED. SERIES_DOCUMENT_TYPE_INCOMPATIBLEis retired. A series that does not fit the document's type answers422 SERIES_INCOMPATIBLE_DOC_TYPEeverywhere: when you create or edit an invoice, when you issue it, and when a recurring invoice template changes its type or series.customer_idtogether with recipient data answers422 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
recipientanswers422 CORRECTIVE_RECIPIENT_NOT_ACCEPTEDand 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 anR4: see corrective invoices and voids. alternative_id.country_codeis required, except forPASSPORTandNOT_REGISTERED, which takeESwhen you omit it. Any other type without it answers422 ALTERNATIVE_ID_COUNTRY_REQUIRED, naming the field, on a customer and on an invoice recipient. Until now an invoice recipient without it answered400 VALIDATION_ERROR, even for a passport, and a customer without it was judged as Spanish and answeredALTERNATIVE_ID_SPAIN_INVALID_TYPEorALTERNATIVE_ID_VAT_REQUIRES_EU_COUNTRY.- An unknown
sort_byon the invoice lists answers400 VALIDATION_ERROR, namingsort_byand the accepted fields, as customers and products already did. Until now it was ignored and the list came back in the default order.due_dateis a new accepted field. - An empty element in a list filter answers
400 VALIDATION_ERRORnaming the parameter, on every list:status=ISSUED,orstatus=ISSUED&status=. It used to fail with a500. 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_atin the202is when the request was accepted, andinvoice.email.senttells 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 answers202withRetry-Afterin 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_PDFat once: its registration was rejected before reaching the AEAT, or it was voided without being registered.verifactu.error_messagesays why.
Does this affect you?
- If you create series from code, add
document_typeto the request. - If you parse error messages or depend on their language, send
Accept-Languageexplicitly. - If your customers have both
emailandbilling_emails, their invoices now go tobilling_emails. - If you relied on
initial_numberrestarting 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: useOTHER_DOCUMENTorCOUNTRY_IDoutside the EU, and the full number with its prefix inside it. - If you branch on the status of
SIMPLIFIED_INVOICE_FORBIDS_IDENTIFIED_RECIPIENT, expect422; better, compareerror.code. - If you branch on
error.codebeingBUSINESS_RULE_VIOLATIONorVALIDATION_ERROR, or onSERIES_DOCUMENT_TYPE_INCOMPATIBLE, add the new codes to those branches. - If you send
customer_idtogether with recipient fields, or arecipienton a corrective, remove them. - If you send
alternative_idwithoutcountry_code, add it. - If you pass
sort_byto 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
/sendright after issuing, handle202: 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 ofes,enandca, gets its messages in English. - A missing scope answers
403first. A key without the scope of the operation gets403 INSUFFICIENT_SCOPEbefore the body or the parameters are validated, instead of a422or400about them. - Invoice emails go to
billing_emailsbeforeemail. Withoutrecipientsin the request or in the invoice'semail_config, an invoice goes to the customer'sbilling_emails, and toemailonly when there are none. It applies to/send, to the automatic send on issue and to recurring invoices. A corrective withsend_automatically: trueuses 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 answers400 SERIES_NUMBER_COLLISION, without issuing or using a number. initial_numberapplies to the first period only. With anANNUALorMONTHLYreset, 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=, answers422when you create or edit it; an invoice number that breaks the rule at issue answers422without using the number. - VeriFactu: fewer, clearer webhooks.
verifactu.status.updatedis sent only when the public status changes. A temporary AEAT error keeps the invoicePENDING, with noerror_codeorerror_message, while BeeL. retries it. A submission refused before it reached AEAT, or one BeeL. stopped waiting for, isREJECTEDand now also sends the webhook; itserror_messageis BeeL.'s own text. - Addresses:
country_codedecides the country.countryis accepted only as a real ISO code or the country's official name in Spanish, English or Catalan (UKanswers422 COUNTRY_CODE_REQUIRED; the code isGB), and acountrythat contradictscountry_codeanswers422 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, notACTIVE, in the account, the list and itsstatusfilter. Every account is born with one company, soPOST /v1/accountsreturns acompany_idwith or without atax_profile. - Companies. Creating a company keeps the trade name and the whole address, and answers
502 EXTERNAL_SERVICE_ERRORwhen the AEAT census cannot be reached, storing nothing. A Spanish postal code without 5 digits answers422 POSTAL_CODE_INVALID_ES. Changingnif,entity_typeorlegal_formanswers422 IMMUTABLE_…, and alegal_namechange with the census down answers200and is checked again later. - Imports. A Holded file over 5,000 contacts or 10 MB is rejected whole with
400 TOO_MANY_RECORDSorCSV_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);NONEalso matches invoices with no payment method stored. - Provisioning again keeps a valid claim link. Resending the
external_refof an unclaimed account, or re-importing it, no longer replaces a claim link that is still valid:claim_tokenandclaim_urlcome backnullwithclaim_link_already_issued: true, and no email is sent. A fresh link is issued only when none is valid; to replace one on purpose, usePOST /v1/accounts/{account_id}/claim-tokens. - The VeriFactu records of an invoice list a registration refused before it reached AEAT as
REJECTEDuntil the invoice is sent again, when the new attempt takes its place.error_messageis BeeL.'s own text when AEAT gave no answer, anderror_codetravels 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
TOTALone, does not change the PDF or add a watermark: the status is instatusandverifactu.submission_status. In the sandbox every PDF keeps the test-invoice watermark. invoice.pdf.generatedfires once for an issued invoice and carriesdata.generation, which numbers the stored PDF (1for the first). It only goes past1in 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 as1.- The simplified-invoice cap is checked on every path. A
SIMPLIFIEDinvoice over 3,000 € (VAT included) answers400 SIMPLIFIED_INVOICE_EXCEEDS_LEGAL_LIMITwhen 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 a500. The reference said422for this code: the status was always400, and the reference now says so. - A recurring invoice template can change its type.
PATCHacceptsinvoice_type; the change is judged on the resulting template with the rules of the new type, so send the newseries_id, andcustomer_id: nullwhen moving toSIMPLIFIED, in the same call. - Creating a company returns the whole company. The
201carries every fieldGET /v1/companies/{company_id}returns, with the same values, plusseries. - 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 noerror_codeand anerror_messagethat 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 answers502or503and nothing is saved, instead of blaming the rows. POST /v1/nif/validateanswers a NIF with bad syntax with200andstatus: INVALID, as its reference says, instead of an error. Itsmessagecomes 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
Tax rates are judged by the operation date and the withholding by the issuer
Temporary VAT and surcharge rates only on operations of their period, `0.625` becomes `0.62`, companies cannot withhold individuals' IRPF rates, and some regime keys are refused.
CLI 0.3.0: commands follow the routes under the NIF
CLI 0.3.0 calls only the routes that name the company, so most commands change name: `beel companies list-invoices <company_id>` is now `beel invoices list`.