Skip to content

Document Status

Track the delivery lifecycle of sent invoices and credit notes on the Peppol network.

GET/v1/invoices/:id#

Track the lifecycle of sent documents. After calling send(), use getStatus() to check delivery progress at any time.

import { Peppol } from "@getpeppr/sdk";

const peppol = new Peppol({ apiKey: "sk_live_..." });

// Send an invoice
const result = await peppol.invoices.send(invoiceData);
console.log(result.status); // "submitted"

// Check status later
const status = await peppol.invoices.getStatus(result.id);
console.log(status.status);

// Status flow:
//   "submitted" → "delivered" → "accepted" → "paid"
//                              → "rejected"
//               → "failed"

// The Peppol AS4 message ID is read from the network on demand, so ask for it
// explicitly. Without ?include=evidence the field is simply absent.
const withEvidence = await peppol.invoices.getStatus(result.id, { includeEvidence: true });
if (withEvidence.peppolMessageId) {
  console.log(`Peppol AS4 ID: ${withEvidence.peppolMessageId}`);
}

Example Response

200 OK
{
  "id": "d7d09754-864d-458c-ad00-35811861608c",
  "submissionId": "8b3f2c1d-4a5e-4f6b-9c8d-7e6f5a4b3c2d",
  "providerDocumentId": "d7d09754-864d-458c-ad00-35811861608c",
  "number": "INV-2026-001",
  "status": "delivered",
  "currency": "EUR",
  "totalAmount": 12100,
  "createdAt": "2026-03-01T10:00:00Z",
  "updatedAt": "2026-03-01T10:04:12Z"
}

Peppol AS4 message ID

The AS4 message ID is the receipt the receiving access point signed for your document. getpeppr reads it from the network on request rather than storing it, so it is opt-in: pass include=evidence (or { includeEvidence: true } with the SDK). If the document has not gone out yet, the field is simply absent — the rest of the response is unchanged.

200 OK — ?include=evidence
{
  "id": "d7d09754-864d-458c-ad00-35811861608c",
  "status": "delivered",
  "peppolMessageId": "4381dde6-28b7-4d7b-a728-2148e1d470c9@phase4",
  "createdAt": "2026-03-01T10:00:00Z"
}

French invoices: the DGFiP leg

An invoice sent under the French regime travels twice: its data (flow 1) is reported to the DGFiP, and the invoice itself is delivered to the buyer's platform over Peppol. With include=evidence, such an invoice also carries clearing, the proof of the first leg. It says the administration was informed, never that the buyer received the invoice — that is peppolMessageId and the invoice.sent webhook.

The public status of such an invoice stays cleared after delivery: cleared ranks above delivered in the status precedence. Read the two proofs, not the status, to know whether each leg happened.

200 OK — ?include=evidence (French invoice)
{
  "id": "8d6cc27e-9ae5-4e0c-ae91-9da03c2d63d8",
  "status": "cleared",
  "peppolMessageId": "8ee4f581-9830-491c-a87e-8b0708c0045b@phase4",
  "clearing": {
    "authority": "FR-DGFiP",
    "network": "fr-dgfip",
    "reference": "FFE0111A_PPF250_PPF2502026092714051000739"
  }
}

The flow itself is served by GET /v1/invoices/:id/as/clearing, or by invoices.getAs(id, "clearing") with the SDK: the XML the administration received. Every other invoice answers 404 on that format.

Status Flow

A document usually progresses submitted → delivered → accepted → paid, but the full vocabulary is wider: buyers, intermediaries and tax authorities can emit states that do not fit a straight line. The complete set your integration can receive:

submitted→delivered→accepted→paid

After delivered, a document can also transition to:

rejected

At any point after submitted, a final failure results in:

failedno_action

Status Details

  • submitted — document submitted to the Peppol network
  • delivered — confirmed received by buyer's access point
  • accepted — buyer accepted the document
  • rejected — buyer rejected the document
  • paid — buyer marked the invoice as paid
  • failed — the invoice failed: delivery failed (network/protocol error), or the recipient's accredited platform rejected it after receiving it (France: Rejetée, 213). Correct it and send again; detail.platformFiscal tells which
  • no_action — not deliverable: no receiving capability found for the recipient on the Peppol network. Terminal for waitFor() and the CLI --watch — nothing ever follows it; register or correct the recipient, then send again
  • cleared — cleared by a tax authority (clearance regimes, e.g. KSA, PT)
  • acknowledged — receipt acknowledged on the buyer's side
  • in_process — the buyer or an intermediary is processing the document
  • under_query — the buyer raised a question; clarification is expected before acceptance
  • conditionally_accepted — the buyer accepted the document subject to conditions
  • partially_paid — the buyer confirmed a partial payment
  • unknown — the gateway emitted a status this SDK version does not recognise; rawStatus preserves the original value
Use Webhooks for real-time status updates instead of polling getStatus().

Raw status and structured detail (SDK 2.2.0, required since 4.0.0)

  • rawStatus — the raw status string exactly as the gateway sent it, before any SDK coercion. When the gateway emits a status the SDK doesn't recognize, status falls back to unknown but rawStatus preserves the original value. Available on SendResult (e.g. getStatus()) and on invoices.list() rows. Since SDK 4.0.0 it is required on both, not optional: the one case where it used to be missing — a 2xx response carrying no status at all — is now refused outright.
  • detail — structured national status detail, one entry per axis (platformFiscal, delivery, businessDisposition, settlement), each carrying the native code and label of the jurisdiction (e.g. a French DGFiP lifecycle code) plus its codeSystem/codeVersion. Present when the jurisdiction provides one; exposed as the detail field on GET /api/v1/invoices list items and parsed by the SDK.

waitFor() terminal semantics

  • failed, rejected, no_action — terminal failures: waitFor() throws immediately instead of polling until the timeout (unless the terminal itself is the awaited target).
  • paid — terminal success. Since SDK 2.2.0, if the document reaches paid while you are waiting for an earlier progress status (e.g. delivered), waitFor() resolves with the real result (result.status === "paid") — the target was passed, not missed. Previously this path ended in a timeout error.