Asynchronous responses (202)

What a 202 Accepted means, how to resolve it to a final document status by polling the list endpoints, and why some countries need you to keep polling after a document has succeeded.

Not every document can be processed while you wait. When a country or route works asynchronously, or eezi cannot process your document serially, Create invoice and Create credit note return 202 Accepted instead of 200 OK. The document is stored and will be processed, but its final status is not yet known. This page describes the recommended way to pick that status up.

📘

Short answer

A 202 means accepted, not done. Do not treat it as success. Once an hour, call List invoices and List credit notes with a modified window covering the last hour, and process every document whose status has changed. Keep doing this for the life of your integration — in some countries a document's status changes again after it has already succeeded.

What a 202 looks like

The response body is the document as stored, with a pending … status. The id is the handle you use from now on:

{
  "id": "inv_01H3YH8RCFSK96H15ADYNQRVTD",
  "ref": "DC230613-003",
  "status": "pending response",
  "type": "Sent",
  "created": "2023-06-27T13:39:11.375Z",
  "modified": "2023-06-27T13:39:11.375Z",
  "routeRef": "mck_01H3YH8REVCFET714XZ2AB6Z5A"
  // …the rest of the document
}

The same applies to credit notes (crd_… ids).

Reading a document's status

status tells you where a document is in its life. The rule is simple:

StatusMeaningWhat to do
Anything starting with pendingStill in flight — eezi is waiting on a submission, a response from the authority or network, or a follow-up.Nothing yet. It will change.
successCleared, reported or delivered as required.Record the result, e.g. routeRef and any attachments.
errorFailed. error.name and error.message say why.Fix and resubmit, or handle the rejection. See Common Errors.
voidedCancelled after submission.Record it.
paid, rejectedA lifecycle update from the recipient, after the document had already succeeded.See Countries with status updates after success.

Never hard-code the list of pending … values. New in-flight states can appear as routes evolve; test for the pending prefix instead.

Resolving the 202

You could read each pending document by id (GET /invoice/{id}, GET /credit-note/{id}) until it settles. That works for a one-off check, but not for volume: it costs one request per document per attempt and you have to track every outstanding id yourself.

The recommended approach is to poll the list endpoints on a schedule and let the modified field tell you what changed. modified is updated whenever a document changes — including the moment a pending … status resolves. Every hour:

  1. Query GET /invoices and GET /credit-notes with q=modified:[start TO end], where the window covers the last hour.
  2. Page through the results with limit (max 500) and skip until you have read all total matches.
  3. For each document, look at status and act on it — skip anything still pending …, record success, handle error.
  4. Move the window forward and repeat.

Generic: everything that changed

The simplest query. You get every document touched in the window and decide what to do per status:

GET /invoices?q=modified:[2024-06-01T09:00:00.000Z TO 2024-06-01T09:59:59.999Z]&limit=500
GET /credit-notes?q=modified:[2024-06-01T09:00:00.000Z TO 2024-06-01T09:59:59.999Z]&limit=500

Specific: only the outcomes you care about

Add a status condition to receive only documents that have landed in a particular state. One query per outcome keeps each result set small and lets you route it to different handling:

# Succeeded in the last hour
GET /invoices?q=modified:[2024-06-01T09:00:00.000Z TO 2024-06-01T09:59:59.999Z] AND status:success&limit=500

# Failed in the last hour
GET /invoices?q=modified:[2024-06-01T09:00:00.000Z TO 2024-06-01T09:59:59.999Z] AND status:error&limit=500

# Paid in the last hour (countries with lifecycle updates)
GET /invoices?q=modified:[2024-06-01T09:00:00.000Z TO 2024-06-01T09:59:59.999Z] AND status:paid&limit=500

# Rejected by the recipient in the last hour
GET /invoices?q=modified:[2024-06-01T09:00:00.000Z TO 2024-06-01T09:59:59.999Z] AND status:rejected&limit=500

Swap /invoices for /credit-notes — the syntax is identical. Add AND type:Sent or AND type:Received to limit to one direction. The full filter syntax and every filterable field are on Listing filters.

🚧

Join conditions with AND. Without it the terms are treated as OR and the result set widens. Dates are ISO 8601 in UTC.

Making the poll reliable

  • Overlap the windows. Start each window a few minutes before the previous one ended, or from the modified value of the last document you processed. A missed run then heals itself on the next one.
  • Be idempotent. The same document can appear in two consecutive polls. Key your processing on id + status so seeing it twice is harmless.
  • Poll both resources. Invoices and credit notes are separate endpoints; a schedule that only reads /invoices misses credit note outcomes.
  • Hourly is the recommended cadence. It comfortably covers batch-scheduled submissions. If you need something closer to real time for a specific document, read it by id — but keep the hourly poll as your source of truth.

Countries with status updates after success

In most countries success and error are the end of the road. In some, the document keeps living after it has succeeded: the recipient, their platform or the authority sends lifecycle updates, and eezi applies them to the document. status changes again — for example from success to paid or rejected — and modified moves with it.

This means you cannot stop watching a document once it has succeeded. The hourly modified poll above already covers this: a document that becomes paid three weeks after clearing will show up in that hour's window like any other change. The only thing you must not do is filter these documents out of your integration because they were already "done".

CountryRouteStatuses after successDetails
FranceE-invoicing (UBL)paid, rejectedLifecycle status — includes rejectCode and rejectReason
FranceE-reportingpaidLifecycle status

The same mechanism works in the other direction: for documents you receive (type:Received) in these countries, you are the one expected to send the lifecycle update, using Update invoice status (POST /invoice/{id}/status) and Update credit note status (POST /credit-note/{id}/status).

🚨

France: polling is not optional

Under the French mandate a rejected invoice has been refused by the buyer and needs to be corrected or credited, and payment statuses are part of your e-reporting obligations. If you integrate for France, run the hourly modified poll indefinitely and handle rejected and paid on documents that had already reached success.



Did this page help you?