/public/v2/e-invoicing/ routes to read the setup, set customer identifiers, and follow each filing.
This guide shows the full flow: check the subscription, read the setup, set the customer identifiers, post the invoice, follow the filing, act on a stopped filing, and send the invoice. For the request and response shapes and the permission of each route, see the E-invoicing section of the V2 API reference.
The e-invoicing routes are in V2 and V3-alpha. They are not in V1. All examples in this guide use V2.
Prerequisites
Before you start, make sure that you have these items:- V2 API (
/public/v2/...) X-API-KEYheader with your organization’s API key (authenticating your requests)- An API key with the permissions of the routes that you call (each route in the V2 API reference names its permission; see also giving an integration access)
- The e-invoicing subscription on the organization (see The subscription rule)
- A company that files e-invoices in a supported country, turned on by a user in the DualEntry app
Supported countries
DualEntry files e-invoices in seven countries. In four of them, the authority must clear the e-invoice before the customer gets it. In the Peppol countries, DualEntry delivers the e-invoice to the inbox of the customer on the Peppol network.GET /public/v2/e-invoicing/countries/ returns the same list, with the identity fields that each country asks for. enabled_for_organization is true when a company of your organization files in that country. Read the countries from this route, not from the table above, because DualEntry can add a country.
The subscription rule
The e-invoicing subscription controls the e-invoicing routes. It does not control invoicing: without it, invoices and customer credits post, send, and archive as before, and DualEntry files nothing. Every e-invoicing route exceptGET /public/v2/e-invoicing/ then answers 402, before it checks the permission.
A record that DualEntry filed before the subscription ended keeps its filing. For that record, the filing route (GET /public/v2/e-invoicing/invoices/{number}/ or GET /public/v2/e-invoicing/customer-credits/{number}/) and its files/{attachment_id}/ route still answer. The send rules of that filing also stay: an invoice that waits for clearance still answers 422 e_invoicing_send_held on send, and the e_invoice field stays on the invoice or customer credit.
The 402 body has no error_code:
Step 1: Check the subscription and the setup
Read the subscription first, then the setup of each company. The setup is read-only on the API. A user changes it in the DualEntry app. Check the subscription:{"subscription_enabled": true} or {"subscription_enabled": false}. Stop here when the value is false.
Then read the setup:
Each company country has a
decision (enabled, disabled, or null until a user decides) and a list of checks. Each check has a code, passed, a message, and params. The registration shows status, the company identifiers, the missing required fields, the agency faults, and waiting_for (decision or fields).
blocked_reason is set while a broken setup of the country holds the registration. DualEntry sends the held registration after the setup is fixed.
Step 2: Set the customer identifiers
Each country asks for identity fields of the customer, such as a tax ID. Read the field keys, patterns, and allowed values fromidentifier_fields.customer of GET /public/v2/e-invoicing/countries/. Then write them with one call:
- Country row:
scopeisregimeandcountryis the country code.identifiersholds the fields of that country. - Network row:
scopeisnetwork:peppolandcountryisnull. The Peppol participant ID (peppol_id, in the form<scheme>:<code>) applies in every Peppol country.
peppol_id in a country row goes to the Peppol network row.
An unknown field or a value that does not match its pattern answers 422 with error_code e_invoicing_error. DualEntry then changes nothing. Give each country and each network one time only.
Read the identifiers with GET /public/v2/e-invoicing/customers/{customer_id}/identifiers/. A network row also shows the participant that DualEntry last found on the network. Vendor identifiers are read-only on the API: GET /public/v2/e-invoicing/vendors/{vendor_id}/identifiers/.
Step 3: Check a Peppol customer before you post
In a Peppol country, DualEntry delivers the e-invoice only to a customer that is on the Peppol network. Check the customer before you post the invoice:country, network, participant_id, and status:
The route needs the
invoice:create permission. A company or a customer that your key does not reach answers 404.
Step 4: Post the invoice
Post the invoice withPOST /public/v2/invoices/, as you do without e-invoicing. When the company files e-invoices in its country, DualEntry checks the e-invoice with the authority and queues the filing. The check can take up to 10 seconds.
A post that e-invoicing refuses answers 422. The reasons are in the e_invoicing key of errors, for example a missing required identifier of the customer. When the body has error_code, the value is e_invoicing_validation. Fix the record or the customer identifiers, then post again.
A customer credit for an invoice that DualEntry filed becomes a credit note filing. A credit to an invoice that the authority has not cleared answers 422 with the reason in the e_invoicing key. Post the credit after the authority clears the invoice.
A filed record has locks. A change or an archive of an invoice whose filing runs, or that the authority cleared, answers 422 with the reason in the e_invoicing key. These record save refusals have no error_code.
Step 5: Follow the filing
Each invoice and customer credit in V2 has ane_invoice field. It is null when DualEntry did not file the record. Otherwise it holds a summary of the filing, including its status and needs_attention. The invoice and customer credit list routes also take an e_invoice_status filter: one or more filing statuses, needs_attention, or none (records without a filing).
The status of a filing is one of these values. The same values appear in from_status and to_status of events and webhooks.
lifecycle tells what a cleared filing means: authority_clearance and ordered_records are confirmed by the authority, and network_delivery reached the inbox of the receiver. blocked_reason is one of workspace_missing, workspace_rejected, workflow_missing, or configuration.
For the full filing, read GET /public/v2/e-invoicing/invoices/{number}/ or GET /public/v2/e-invoicing/customer-credits/{number}/. {number} is the record number. These are the fields that you act on:
faults: why the filing stopped, each withcode,message, andpathsactions: what your key can do now (retryorrefresh)filesandlegal_pdf: the legal files, such as the authority XML and PDF
- Subscribe to the
core.e_invoice_filing/updatedwebhook (see The filing webhook). - List the events of the organization with
GET /public/v2/e-invoicing/events/?needs_attention=true, newest first. - List the events of one record with
GET /public/v2/e-invoicing/invoices/{number}/events/, oldest first. - Filter the invoice or customer credit list with
?e_invoice_status=needs_attention.
Step 6: Act on a stopped filing
A filing infailed or rejected status stops. Read faults to find the cause, fix the record, the customer identifiers, or the setup, then retry:
invoice:edit or customer_credit:edit). A filing in any other status, or a record without a filing, answers 422 with error_code e_invoicing_not_retryable. Check actions before you call: it lists retry only when the retry route accepts it for your key.
DualEntry reads the result of a started filing on its own. When you cannot wait, call POST .../refresh/ to read the newest result from the authority now. A filing that DualEntry has not yet started to send stays as it is.
A filing with blocked_reason set waits for a fix of the country setup. You do not have to retry it. DualEntry sends it after the setup is fixed.
Step 7: Send the invoice
Send the invoice by email withPOST /public/v2/invoices/{record_number}/send/. In Argentina, Italy, Mexico, and Spain, the authority must clear the e-invoice before the customer gets it. Until then, the send answers 422:
send_held and send_held_reason on the filing tell you before you call. send_held_reason is one of awaiting_clearance, waiting_for_setup, filing_failed, or filing_rejected.
To download a legal file, read its attachment_id from files, then call GET /public/v2/e-invoicing/invoices/{number}/files/{attachment_id}/. The response holds a short-lived url.
Step 8: Correct a cleared invoice
The authority does not accept a change to a cleared invoice. Whencorrections on the invoice filing holds credit_note, post a customer credit for that invoice with POST /public/v2/customer-credits/. DualEntry files the credit as a credit note. Follow the credit note filing with GET /public/v2/e-invoicing/customer-credits/{number}/.
After these steps, each invoice that you post shows its filing in e_invoice, and you send it once the filing allows.
The filing webhook
DualEntry sends thecore.e_invoice_filing/updated webhook for each filing event: the first queued state, each status change, and each new setup hold. A hold keeps the status, so its from_status equals its to_status.
Subscribe with POST /public/v2/webhooks/ and put core.e_invoice_filing/updated in topics. GET /public/v2/webhooks/topics/ lists every topic.
The delivery body is the standard webhook envelope. object_type is core.EInvoiceSubmission, and object holds the filing event:
object fields:
The payload is small on purpose. Read the full filing with
GET /public/v2/e-invoicing/invoices/{record_number}/ or GET /public/v2/e-invoicing/customer-credits/{record_number}/. A redelivery of the same filing event keeps its envelope event_id, so remove duplicates by that value.
Troubleshooting
A refused e-invoicing request has a stableerror_code next to errors. Branch on error_code, not on the message text. For the error body format, see the error handling guide.
These answers have no
error_code:
402: no e-invoicing subscription on the organization (see The subscription rule)404: a record, company, or customer that your key does not reach, for example because a key that the CLI sign-in made reaches only the companies of its user422: a record save that e-invoicing refuses, with the reason under thee_invoicingkey, such as a change of a cleared invoice or a credit to an invoice that the authority has not cleared
error_code, fault code, or enum value as a general value. Stored filings keep old fault codes, and DualEntry can add values.