Skip to content

API Specification

Philip Helger edited this page Sep 4, 2026 · 35 revisions

API Specification

Overview

The AP exposes REST APIs for outbound document submission, status queries, inbound reporting, and operational tasks (history, payload retrieval, replay). All APIs are provided via Spring Boot.

OpenAPI Specification (since v0.9.2)

A machine-readable OpenAPI 3 description of all REST endpoints is served by the running application:

Format Path
JSON GET /openapi/v3/api-docs
YAML GET /openapi/v3/api-docs.yaml

The spec is generated at runtime by springdoc-openapi from the controller annotations — no Swagger UI is bundled. The base path can be changed via the springdoc.api-docs.path property if needed.

The OpenAPI document is suitable for:

  • Generating typed client SDKs in any language (via openapi-generator or similar).
  • Importing into API tooling (Postman, Insomnia, Bruno, …) to get an endpoint catalog with parameter hints.
  • Validating requests/responses in integration tests.

The spec declares an API-key security scheme named ApiToken on the X-Token header — all /api/** endpoints reference it; /management/** endpoints do not.

If you need a hosted spec for a particular release, fetch /openapi/v3/api-docs.yaml from a running instance of that version — the document version reported in info.version matches the application build version.


Outbound APIs

POST — Submit Document (raw business document + metadata)

Path: /api/outbound/submit/{senderID}/{receiverID}/{docTypeID}/{processID}/{c1CountryCode}

Submits a raw business document for outbound transmission. The AP creates the SBDH envelope.

Path parameters (required):

  • senderID — Peppol Participant ID of the sender
  • receiverID — Peppol Participant ID of the receiver
  • docTypeID — Peppol Document Type Identifier
  • processID — Peppol Process Identifier
  • c1CountryCode — Country code of the sender (C1)

Query parameters (optional):

  • sbdhInstanceID — Custom SBDH Instance Identifier. When omitted, a random UUID-based identifier is generated.

  • mlsTo — Alternative Peppol participant ID to receive MLS responses

  • sbdhStandard — SBDH Standard override for non-XML payloads (e.g., urn:peppol:doctype:pdf+xml for PDF). When omitted, auto-derived from the document type identifier.

  • sbdhTypeVersion — SBDH TypeVersion override (e.g., 0). When omitted, auto-derived from the document type identifier.

  • sbdhType — SBDH Type override (e.g., factur-x). When omitted, auto-derived from the document type identifier.

  • payloadMimeType — MIME type for binary payloads (e.g., application/pdf). When set, the payload is treated as binary content and wrapped in <BinaryContent> instead of XML. When omitted, the payload is treated as XML.

    Mandatory for non-XML document types: if the document type identifier has a non-XML syntax specific ID (no :: separating an XML root namespace URI from a local name — e.g. urn:peppol:doctype:pdf+xml), then all four of payloadMimeType, sbdhStandard, sbdhTypeVersion and sbdhType must be provided, because none of them can be derived from the document type identifier. Missing ones are rejected with 400 since 0.12.0 — see Peppol Specifics and #70.

  • custom1, custom2, custom3 — Optional free-text custom fields (max 255 characters each) stored with the transaction and returned by the status APIs. Values longer than 255 characters are rejected with 400. Since 0.11.0. See #64.

Request body: The raw business document (e.g., UBL Invoice XML) or binary payload (e.g., PDF bytes when payloadMimeType is set)

Response: The document is sent synchronously — the response is only returned after the AS4 transmission has finished. The body is the phase4 Phase4PeppolSendingReport as JSON, containing among others sbdhInstanceIdentifier, senderId, receiverId, docTypeId, processId, countryC1, c3SmpUrl, c3EndpointUrl, as4MessageId, sendingResult, sendingError, sendingDurationMillis, overallDurationMillis, sendingSuccess and overallSuccess.

Status codes:

  • 200 OK — Sent successfully (overallSuccess is true)
  • 400 Bad Request — Invalid Peppol identifier (sender, receiver, document type or process), a custom field longer than 255 characters, or a missing SBDH parameter for a non-XML document type. The body is a JSON string with the error message.
  • 404 Not Found — Sending is disabled in the configuration (peppol.sending.enabled=false); empty body
  • 422 Unprocessable Content — The transaction could not be created (body: the submit error object), or sending failed (body: the sending report with overallSuccess set to false). Document validation failures — see verification.outbound.enabled — end up here as well.

Submit error body (since v0.12.0)

When an outbound submission fails before sending, the body is a JSON object (before v0.12.0 it was a bare JSON string with a generic message such as "Failed to submit outbound transaction"):

{
  "errorMessage": "Document validation failed",
  "verifierName": "PhormDocumentVerifier",
  "verificationPerformed": true,
  "verificationIssues": [
    {
      "level": "error",
      "type": "business_rule",
      "code": "PEPPOL-EN16931-R001",
      "location": "/Invoice/cbc:ID",
      "description": "An Invoice shall have an Invoice number"
    }
  ]
}
  • errorMessage — always present
  • verifierName — only present for a verification failure: which of the registered verifiers objected. Useful when more than one is configured
  • verificationPerformed — only present for a verification failure. true means the document was inspected and found invalid; false means a verifier could not make a verdict at all (e.g. the validation service was unreachable). Outbound verification has no fail mode, so the submission is blocked either way, but a client should retry the second case and not the first
  • verificationIssues — only present if the verifier provided individual findings. code is the machine-readable rule identifier and is what a client should branch on; location and code are omitted when unknown. See Inbound Verification Result for the value ranges

A rejected outbound document does not create an outbound transaction, so there is no transaction ID to query afterwards — the document is never sent.

When verification accepts the document but produces warnings, the successful sending report gains a verificationWarnings array with the same issue objects. If there are no warnings the response is unchanged. The inbound counterpart reports such warnings to C2 as line responses of the positive MLS — see Message Level Status.

POST — Submit Document (from S3 reference)

Path: /api/outbound/submit-s3

Since v0.1.1.

Submits a document for outbound transmission by referencing an S3 object instead of inlining the payload. The Sender Backend uploads the document to S3 first, then calls this endpoint with the S3 reference and Peppol metadata. The AP fetches the document from S3 and processes it through the normal outbound pipeline.

Requires outbound.s3.enabled=true in configuration.

Request body (JSON):

  • senderID (required) — Peppol Participant ID of the sender

  • receiverID (required) — Peppol Participant ID of the receiver

  • docTypeID (required) — Peppol Document Type Identifier

  • processID (required) — Peppol Process Identifier

  • c1CountryCode (required) — Country code of the sender (C1)

  • s3Key (required) — The S3 object key of the uploaded document

  • s3Bucket (optional) — The S3 bucket where the document was uploaded. Defaults to the configured outbound.s3.bucket.

  • sbdhInstanceID (optional) — Custom SBDH Instance Identifier. When omitted, a random UUID-based identifier is generated.

  • mlsTo (optional) — Alternative Peppol participant ID to receive MLS responses

  • sbdhStandard (optional) — SBDH Standard override for non-XML payloads

  • sbdhTypeVersion (optional) — SBDH TypeVersion override

  • sbdhType (optional) — SBDH Type override

  • payloadMimeType (optional) — MIME type for binary payloads (e.g., application/pdf)

    The four fields above are mandatory — and rejected with 400 when missing, since 0.12.0 — if the document type identifier has a non-XML syntax specific ID (e.g. urn:peppol:doctype:pdf+xml), exactly like on the /submit/... endpoint.

  • custom1, custom2, custom3 (optional) — Free-text custom fields (max 255 characters each) stored with the transaction and returned by the status APIs. Values longer than 255 characters are rejected with 400. Since 0.11.0. See #64.

Response: Same as the raw document submission — the Phase4PeppolSendingReport as JSON.

Status codes:

  • 200 OK — Sent successfully
  • 400 Bad Request — Outbound S3 submission is disabled (outbound.s3.enabled=false), a required field is missing, an identifier is invalid, an SBDH parameter for a non-XML document type is missing, the S3 region or bucket configuration is incomplete, or the S3 object could not be fetched
  • 404 Not Found — Sending is disabled in the configuration; empty body
  • 422 Unprocessable Content — The transaction could not be created (e.g. outbound verification rejected the document; body: the submit error object), or sending failed and the body is the sending report

POST — Submit Document (pre-built SBD)

Path: /api/outbound/submit-sbd

Submits a complete Standard Business Document (with SBDH already present).

Query parameters (optional):

  • mlsTo — Alternative Peppol participant ID to receive MLS responses
  • custom1, custom2, custom3 — Free-text custom fields (max 255 characters each) stored with the transaction and returned by the status APIs. Values longer than 255 characters are rejected with 400. Since 0.11.0. See #64.

Request body: The complete SBD (SBDH + business document)

Response: Same as the raw document submission — the Phase4PeppolSendingReport as JSON.

All metadata (sender ID, receiver ID, document type, process, C1 country code) is extracted from the SBDH.

Status codes:

  • 200 OK — Sent successfully
  • 400 Bad Request — The SBD could not be submitted (unparsable SBD, invalid identifiers, custom field longer than 255 characters) or document validation failed when verification is enabled — see verification.outbound.enabled (since 0.9.0)
  • 404 Not Found — Sending is disabled in the configuration; empty body
  • 422 Unprocessable Content — Sending failed; the body is the sending report

POST — Submit Document (auto-detect document type) (since v0.2.0)

Path: /api/outbound/submit-auto/{senderID}/{receiverID}/{c1CountryCode}

Submits a raw business document for outbound transmission with automatic document type and process detection. The AP uses the DDD (Document Details Determinator) library to analyze the XML and determine the Peppol Document Type Identifier and Process Identifier automatically.

Path parameters (required):

  • senderID — Peppol Participant ID of the sender
  • receiverID — Peppol Participant ID of the receiver
  • c1CountryCode — Country code of the sender (C1)

Query parameters (optional):

  • sbdhInstanceID — Custom SBDH Instance Identifier. When omitted, a random UUID-based identifier is generated.
  • mlsTo — Alternative Peppol participant ID to receive MLS responses
  • custom1, custom2, custom3 — Free-text custom fields (max 255 characters each) stored with the transaction and returned by the status APIs. Values longer than 255 characters are rejected with 400. Since 0.11.0. See #64.

Request body: The raw business document (e.g., UBL Invoice XML, CII CrossIndustryInvoice XML)

Response: Same as the raw document submission — the Phase4PeppolSendingReport as JSON.

Status codes:

  • 200 OK — Sent successfully
  • 400 Bad Request — Empty request body, body is not valid XML, the document type could not be determined from the XML (the document matches no known Peppol format), DDD determined the document type but no process identifier, an invalid sender or receiver participant ID, a custom field longer than 255 characters, or the transaction could not be created (e.g. outbound verification rejected the document)
  • 404 Not Found — Sending is disabled in the configuration; empty body
  • 422 Unprocessable Content — Sending failed; the body is the sending report

Note: Unlike the full /api/outbound/submit/... endpoint, this endpoint does not support binary payloads or SBDH overrides (sbdhStandard, sbdhTypeVersion, sbdhType, payloadMimeType) — it is designed for standard Peppol XML documents where the document type can be inferred from the XML content.

GET — Query Outbound Transaction Status

Path: /api/outbound/status/{sbdhInstanceID}

Returns the current status of a specific outbound transaction. By default only the active outbound_transaction table is searched. To also consider already archived transactions, pass includeArchive=true (since 0.9.0).

Parameters:

  • sbdhInstanceID — The SBDH Instance Identifier

Query parameters (optional):

  • includeArchive (boolean, default false, since 0.9.0) — When true, the outbound_transaction_archive table is consulted if the transaction is no longer present in the active table. See #29.

Response: The JSON serialization of OutboundTransactionResponse. All fields below are always present; those without a value are serialized as null.

  • ID — Internal transaction ID assigned by the AP
  • transactionTypebusiness_document or mls_response
  • senderID — Peppol Participant ID of the sender
  • receiverID — Peppol Participant ID of the receiver
  • docTypeID — Peppol Document Type Identifier
  • processID — Peppol Process Identifier
  • sbdhInstanceID — Peppol SBDH Instance Identifier
  • status — Current transaction status (pending, rejected, sending, sent, failed, permanently_failed)
  • attemptCount — Total number of sending attempts so far
  • createdDT — When the transaction was created (ISO-8601, UTC)
  • completedDT — When successfully completed (null if not yet)
  • reportingStatus — Whether reporting has been triggered (pending, reported, excluded)
  • nextRetryDT — Planned date/time of the next sending retry (null unless status is failed)
  • errorDetails — Summary error from the last failed attempt (null on success)
  • mlsStatus — MLS response reception status: pending, received_ap, received_ab, received_re, not_applicable (only for business_document)
  • custom1, custom2, custom3 — Free-text custom fields supplied at submission (null if not set). Since 0.11.0. See #64.

Note the capitalization of ID: the JSON property names are derived from the JavaBean getters, and a name starting with two upper case letters is left untouched.

Data stored on the transaction but not contained in this response: source type, document size, document hash, C1 country code, MLS_TO target, MLS reception date/time, MLS message ID and the individual sending attempts.

GET — List Outbound Transactions In Transmission

Path: /api/outbound/in-transmission

Returns all outbound transactions that are not yet in a final state.

Response: List of outbound transactions (same fields as the outbound status response).

This includes transactions with status: pending, sending, failed (awaiting retry). It excludes rejected, sent, and permanently_failed.


Inbound APIs

POST — Report Inbound Message for Peppol Reporting

Path: /api/inbound/report

Triggers the creation of a Peppol Reporting record for a previously received inbound message. Called by the Receiver Backend after it has successfully processed the document.

Query parameters (required):

  • sbdhInstanceID — The SBDH Instance Identifier of the inbound message
  • c4CountryCode — Country code of the final receiver (C4)

Response (200 OK):

  • transactionID — Internal ID of the inbound transaction
  • status — Always updated
  • messageC4 country code set to '<code>'

Status codes:

  • 200 OK — Country code stored and reporting record created
  • 400 Bad Request — The transaction already has a C4 country code stored; empty body
  • 404 Not Found — No inbound transaction with the given SBDH Instance ID; empty body

The response is 200 even if storing the Peppol Reporting item itself failed — such a failure is only logged and passed to the notification handlers, and reporting_status then stays pending.

Behavior:

  1. Looks up the inbound_transaction by SBDH Instance ID.
  2. Stores the C4 country code on the transaction.
  3. Creates the reporting record using the stored SBDH data + C4 country code.
  4. Updates reporting_status to reported - or to excluded if the sender or the receiver is excluded from Peppol Reporting via peppol.reporting.exclude.participant-ids, in which case no reporting record is created at all (since 0.13.0).

GET — Query Inbound Transaction Status

Path: /api/inbound/status/{sbdhInstanceID}

Returns the current status of a specific inbound transaction. By default only the active inbound_transaction table is searched. To also consider already archived transactions, pass includeArchive=true (since 0.9.0).

Parameters:

  • sbdhInstanceID — The SBDH Instance Identifier

Query parameters (optional):

  • includeArchive (boolean, default false, since 0.9.0) — When true, the inbound_transaction_archive table is consulted if the transaction is no longer present in the active table. See #29.

Response: The JSON serialization of InboundTransactionResponse. All fields below are always present; those without a value are serialized as null.

  • ID — Internal transaction ID assigned by the AP
  • senderID — Peppol Participant ID of the sender
  • receiverID — Peppol Participant ID of the receiver
  • docTypeID — Peppol Document Type Identifier
  • processID — Peppol Process Identifier
  • AS4MessageID — The AS4 Message ID from the inbound message
  • sbdhInstanceID — Peppol SBDH Instance Identifier
  • c2SeatID — Peppol Seat ID of the sending AP (C2). Since 0.10.2.
  • c3SeatID — Peppol Seat ID of the receiving AP (C3). Since 0.10.2.
  • status — Current transaction status (received, rejected, verification_deferred, forwarding, forwarded, forward_failed, permanently_failed)
  • attemptCount — Total number of forwarding attempts
  • receivedDT — When the message was received (ISO-8601, UTC)
  • completedDT — When successfully completed (null if not yet)
  • reportingStatus — Whether reporting has been triggered (pending, reported, excluded)
  • nextRetryDT — Planned date/time of the next forwarding retry (status forward_failed) or of the next re-verification (status verification_deferred)
  • errorDetails — Summary error from the last failed forwarding attempt, or the reason of a deferred verification (VERIFIER_UNAVAILABLE [...]) or of a verification rejection (VERIFICATION_REJECTED [...]); null on success
  • c4CountryCode — C4 country code (null if not yet reported). Since 0.1.3.
  • duplicateAS4 — Duplicate detected on AS4 Message ID level
  • duplicateSBDH — Duplicate detected on SBDH Instance Identifier level
  • mlsResponseCode — MLS response code sent or to be sent (RE, AP, AB, null if not yet determined)
  • verificationResult — Verdict of the inbound document verification (passed, rejected, unverified; null if not verified yet). Deliberately independent of status, so it also survives a forwarding. Since 0.12.0.
  • verificationDetails — The findings of the verification as a JSON array string, each element a VerificationIssue (level, type, code, location, description) — the same shape the outbound submit API returns. null if the verifier provided no individual findings. On a passed verification these are warnings. Since 0.12.0.

Note the naming of ID, AS4MessageID, duplicateAS4 and duplicateSBDH: the JSON property names are derived from the JavaBean getters, so a name starting with two upper case letters is left untouched, and the is prefix of a boolean getter is not part of the name. The JSON metadata sidecar files written by the filesystem, SFTP and S3 forwarders contain the same data, but use id, as4MessageID, isDuplicateAS4 and isDuplicateSBDH instead, and omit all fields without a value.

Data stored on the transaction but not contained in this response: phase4 Incoming ID, signing certificate CN, AS4 timestamp, document size, document hash, C1 country code, MLS_TO target, MLS type, the ID of the MLS outbound transaction and the individual forwarding attempts.

GET — List Inbound Transactions In Processing

Path: /api/inbound/in-processing

Returns all inbound transactions that are not yet in a final state.

Response: List of inbound transactions (same fields as the inbound status response).

This includes transactions with status: received, verification_deferred (awaiting re-verification, since 0.12.0), forwarding and forward_failed (awaiting retry). It excludes rejected, forwarded, and permanently_failed.

GET — List Inbound Transactions Missing C4 Country Code (since v0.1.3)

Path: /api/inbound/missing-c4-country-code

Returns all forwarded inbound transactions for which the C4 country code has not yet been determined. Only includes transactions in status forwarded where reporting is still pending.

Response: List of inbound transactions (same fields as the inbound status response). Check the c4CountryCode field — it will be null for all returned entries.

Useful for monitoring whether all forwarded transactions have received a C4 country code (either via automatic determination or the async reporting API).

GET — Check Specific Transaction for Missing C4 Country Code (since v0.1.3)

Path: /api/inbound/missing-c4-country-code/{sbdhInstanceID}

Checks whether a specific inbound transaction is still missing a C4 country code.

Parameters:

  • sbdhInstanceID — The SBDH Instance Identifier

Response:

  • 200 OK — The C4 country code is still missing. Returns the full inbound transaction details (same fields as the inbound status response).
  • 204 No Content — The transaction exists and the C4 country code is already set (not missing).
  • 404 Not Found — No transaction with this SBDH Instance ID exists.

MLS APIs

POST — Trigger the MLS of an Inbound Transaction (since v0.13.0)

Path: /api/mls/send

Creates, persists and sends the MLS of a previously received inbound business document. Called by the Receiver Backend once it knows the outcome of the delivery to C4.

This is the counterpart of mls.sending.trigger=api, in which the AP does not send the positive MLS on its own after a successful forwarding. The endpoint also works in the trigger mode auto, as long as no MLS was determined for the transaction yet — a backend that wants to report a rejection for a document it received but cannot process can use it there as well.

Request body (application/json):

{
  "sbdhInstanceID": "550e8400-e29b-41d4-a716-446655440000",
  "responseCode": "AP",
  "responseText": "Delivered to C4 backend, order 4711",
  "issues": []
}

Rejection example:

{
  "sbdhInstanceID": "550e8400-e29b-41d4-a716-446655440000",
  "responseCode": "RE",
  "responseText": "C4 rejected the invoice",
  "issues": [
    {
      "statusReasonCode": "BV",
      "errorField": "cac:AccountingCustomerParty",
      "description": "Unknown customer number 12345"
    }
  ]
}
Field Required Meaning
sbdhInstanceID yes SBDH Instance Identifier of the received business document
responseCode yes AP (acceptance), AB (acknowledging) or RE (rejection)
responseText no Human-readable response text of the MLS
issues[].statusReasonCode yes, per issue BV, BW, FD or SV
issues[].errorField yes, per issue XPath expression of the error location, or NA
issues[].description yes, per issue Human-readable description

issues is mandatory for RE — a rejection must name at least one reason — and optional for AP and AB, because MLS allows line responses on a positive response code as well.

Response (ReportResponse):

  • transactionID — Internal ID of the inbound transaction
  • status — Machine-readable outcome keyword, see below
  • message — Human-readable description; for sent it names the created outbound MLS transaction ID

Status codes:

  • 200 OK, status = sent — The MLS was created, mls_response_code and mls_outbound_transaction_id were stored on the inbound transaction, and the outbound transaction was handed to the sender.
  • 200 OK, status = recorded — The transaction's mls_type is FAILURE_ONLY and the submitted code is AP or AB, so the response code was recorded but nothing is sent. That is the existing FAILURE_ONLY semantics, unchanged.
  • 400 Bad Request, status = invalid — Missing sbdhInstanceID, unknown responseCode, unknown statusReasonCode, an issue without errorField or description, or RE without any issue.
  • 404 Not Found, status = not-found — No active inbound transaction with this SBDH Instance Identifier.
  • 409 Conflict, status = conflict — An MLS was already determined for this transaction (mls_response_code is set), or the document was rejected by the verification and C2 already received the negative MLS of that rejection. Peppol expects exactly one MLS per business document.
  • 422 Unprocessable Entity, status = not-eligible — The transaction is itself an MLS or an MLR document, which is never answered with an MLS.
  • 500 Internal Server Error, status = failed — The MLS could not be built, serialized or persisted; details are in the server log.
  • 503 Service Unavailable, status = disabledmls.sending.enabled=false.

Notes:

  • The endpoint returns as soon as the outbound MLS transaction is persisted. The AS4 transmission and its retries happen in the background through the normal outbound path, so a 200 means "the MLS was created and queued", not "C2 received it". Track the outbound transaction ID from the message to follow the actual sending.
  • The lookup deliberately does not include the archive table — an archived transaction is done.
  • Idempotency is enforced by the mls_response_code of the transaction: a second call is answered with 409 and sends nothing.

GET — List Inbound Transactions Missing MLS Response

Path: /api/mls/missing

Returns all inbound business document transactions for which no MLS response has been sent yet (mls_response_code IS NULL). Excludes incoming MLS messages themselves.

Response: List of inbound transactions (same fields as the inbound status response).

Useful for monitoring whether all received business documents have been properly acknowledged via MLS.

GET — MLS-1 SLA Report (Receiving Side)

Path: /api/mls/sla/mls1

Returns the MLS-1 SLA report measuring M2 - M1: the time between receiving the original business document at this AP (M1) and successfully sending the MLS response back to C2 (M2). Per Peppol Network Policy, 99.5% must be within 20 minutes.

Response:

  • totalCount — Total number of MLS responses measured
  • withinSlaCount — Number of responses within the 20-minute threshold
  • compliancePercent — Actual compliance percentage
  • targetPercent — Required target (99.5)
  • thresholdSeconds — SLA threshold in seconds (1200)
  • meetingSla — Whether the target is met (true / false)
  • entries — List of individual measurements, each with:
    • sbdhInstanceID — SBDH Instance Identifier of the original business document
    • m1 — M1 timestamp (AS4 timestamp of the received business document)
    • m2OrM3 — M2 timestamp (AS4 timestamp of the successful MLS response sending attempt)
    • durationSeconds — Duration in seconds (M2 - M1)
    • withinSla — Whether this entry is within the threshold

GET — MLS-2 SLA Report (Sending Side)

Path: /api/mls/sla/mls2

Returns the MLS-2 SLA report measuring M3 - M1: the time between successfully sending a business document from this AP (M1) and receiving the MLS response from C3 (M3). Per Peppol Network Policy, 99.5% must be within 25 minutes.

Response: Same structure as MLS-1 report, but with thresholdSeconds = 1500 (25 minutes) and m2OrM3 representing M3 (the MLS reception timestamp).


Peppol Reporting APIs

All three endpoints take the reporting period as {year}/{month} path variables and require a Peppol Reporting backend to be configured. The values are clamped instead of being rejected: a year below 2024 is raised to 2024, and a month outside 1-12 is moved into that range. A period in the future is rejected with 500 and the generic Spring error JSON, because the resulting IllegalArgumentException is not mapped to a dedicated status code.

GET — Create TSR (Transaction Statistics Report)

Path: /api/reporting/create-tsr/{year}/{month}

Creates a TSR (Transaction Statistics Report) for the provided year/month. Returns the report as XML.

Path parameters (required):

  • year — The year (e.g., 2026)
  • month — The month (e.g., 3)

Response:

  • 200 OK — The TSR XML document (application/xml)
  • 500 Internal Server Error — Body Failed to read Peppol Reporting backend data, e.g. when the reporting backend is not reachable

GET — Create EUSR (End User Statistics Report)

Path: /api/reporting/create-eusr/{year}/{month}

Creates an EUSR (End User Statistics Report) for the provided year/month. Returns the report as XML.

Path parameters (required):

  • year — The year (e.g., 2026)
  • month — The month (e.g., 3)

Response:

  • 200 OK — The EUSR XML document (application/xml)
  • 500 Internal Server Error — Body Failed to read Peppol Reporting backend data, e.g. when the reporting backend is not reachable

GET — Create, Validate, Store and Send Reports

Path: /api/reporting/do-peppol-reporting/{year}/{month}

Creates, validates, stores and sends both TSR and EUSR reports for the provided year/month in one call.

Path parameters (required):

  • year — The year (e.g., 2026)
  • month — The month (e.g., 3)

Response: Not a document, but a plain text status line (the content type is nevertheless declared as application/xml):

  • 200 OK — Body Done - check report storage; the created reports are in the configured report storage
  • 500 Internal Server Error — Body Error creating or sending Peppol Reports

Operations APIs (since v0.11.0)

Operational endpoints for transaction history auditing, payload inspection and manual re-forwarding. All of them are located below /api/ops and — like every other /api/** endpoint — require the X-Token header. In the OpenAPI document they are grouped under the Operations tag.

These endpoints return full document payloads and can re-trigger delivery to the Receiver Backend, so they are more sensitive than the other query APIs — see Security Considerations before exposing them beyond the operations team.

See #69 - thx @dmaus2018

GET — List Inbound Transaction History

Path: /api/ops/inbound/history

Returns inbound transactions page-wise, newest first (ordered by received_dt descending). Only the active inbound_transaction table is queried — already archived transactions are not included.

Query parameters (optional):

  • offset (int, default 0) — Number of rows to skip. Must be ≥ 0.
  • limit (int, default 50) — Maximum number of rows to return. Must be ≥ 0.

Response: List of inbound transactions (same fields as the inbound status response).

Negative offset or limit values are rejected as an internal error — there is no dedicated 400 mapping. There is no database index on received_dt, so keep the page size moderate on large tables.

GET — Count Inbound Transactions

Path: /api/ops/inbound/size

Returns the number of rows in the active inbound_transaction table as a plain JSON number (e.g. 42). Archived rows are not counted, so combined with the history endpoint this is the total number of pageable rows.

GET — Download Inbound Payload

Path: /api/ops/inbound/{sbdhInstanceID}/payload

Returns the stored raw document bytes of an inbound transaction as application/octet-stream. The transaction is looked up in the active inbound_transaction table first, and in inbound_transaction_archive if it is not found there.

Path parameters (required):

  • sbdhInstanceID — The SBDH Instance Identifier

Response:

  • 200 OK — The raw document content, read from the configured document storage backend.
  • 404 Not Found — No transaction with this SBDH Instance ID exists, or the payload could not be read (e.g. the document file was already cleaned up, or the storage backend failed). The underlying cause is logged.

POST — Replay Inbound Transaction

Path: /api/ops/inbound/{sbdhInstanceID}/replay

Forces a re-forwarding of an already received inbound transaction to the configured Receiver Backend. The regular forwarding pipeline is used — circuit breaker, forwarding attempt recording, retry scheduling and the lifecycle/notification handlers all behave as for an automatic retry. Log entries of a replay are prefixed with API Replay: .

Path parameters (required):

  • sbdhInstanceID — The SBDH Instance Identifier

Response:

  • 200 OK — Forwarding succeeded. Body: the inbound transaction as re-read after the replay (same fields as the inbound status response).
  • 500 Internal Server Error — Forwarding failed. Body: the inbound transaction as re-read after the replay, including the error details of the failed attempt.
  • 404 Not Found — No transaction with this SBDH Instance ID exists.

Notes:

  • The document is delivered to the Receiver Backend again — the backend must be able to cope with receiving the same document twice.
  • The lookup includes the archive table, but all state updates (status, forwarding attempt records) are written to the active tables only. Replaying an already archived transaction therefore re-forwards the document without updating its stored state.

POST — Re-verify and Forward Inbound Transaction (since v0.12.0)

Path: /api/ops/inbound/{sbdhInstanceID}/reverify-and-forward

Manually resumes an inbound transaction whose verification was deferred (status verification_deferred), without waiting for the next scheduled re-verification. All registered inbound document verifiers are evaluated again against the stored payload; if they all accept the document, the processing continues where it was interrupted — an incoming MLS is correlated, the document is forwarded to C4 and the positive MLS is sent to C2. Log entries are prefixed with API ReverifyAndForward: .

Path parameters (required):

  • sbdhInstanceID — The SBDH Instance Identifier

Response:

  • 200 OK — The document was verified and forwarded. Body: the inbound transaction as re-read afterwards (same fields as the inbound status response).
  • 409 Conflict — The transaction is not in status verification_deferred. Body: the inbound transaction as it is.
  • 500 Internal Server Error — The verification failed, is still deferred, or the forwarding failed. Body: the inbound transaction as re-read afterwards, including its error details.
  • 404 Not Found — No active transaction with this SBDH Instance ID exists.

Notes:

  • Unlike the replay endpoint, the lookup does not include the archive table — an archived transaction is done.
  • Only a transaction in status verification_deferred may be resumed; any other status is answered with 409. Re-verifying an already forwarded document would deliver it to the Receiver Backend a second time and could produce an MLS that contradicts the one already sent for that document. Use the replay endpoint if a deliberate re-delivery is wanted.
  • If a verifier is still unavailable, the transaction stays in verification_deferred (or is rejected once verification.deferred.max-duration is exceeded) and the endpoint returns 500.

GET — List Outbound Transaction History

Path: /api/ops/outbound/history

Returns outbound transactions page-wise, newest first (ordered by created_dt descending). Only the active outbound_transaction table is queried — already archived transactions are not included.

Query parameters (optional):

  • offset (int, default 0) — Number of rows to skip. Must be ≥ 0.
  • limit (int, default 50) — Maximum number of rows to return. Must be ≥ 0.

Response: List of outbound transactions (same fields as the outbound status response).

The same restrictions as for the inbound history apply: negative values are rejected as an internal error, and there is no database index on created_dt.

GET — Count Outbound Transactions

Path: /api/ops/outbound/size

Returns the number of rows in the active outbound_transaction table as a plain JSON number (e.g. 42). Archived rows are not counted.

GET — Download Outbound Payload

Path: /api/ops/outbound/{sbdhInstanceID}/payload

Returns the stored raw document bytes of an outbound transaction as application/octet-stream. The transaction is looked up in the active outbound_transaction table first, and in outbound_transaction_archive if it is not found there.

Path parameters (required):

  • sbdhInstanceID — The SBDH Instance Identifier

Response:

  • 200 OK — The raw document content, read from the configured document storage backend.
  • 404 Not Found — No transaction with this SBDH Instance ID exists, or the payload could not be read. The underlying cause is logged.

There is no outbound replay endpoint — outbound retries are driven by the retry scheduler (see Retry and Resilience Patterns).


Management APIs

GET — Status

Path: /management/status

Returns non-sensitive configuration values, version information, and runtime metadata as a JSON object. Intended for health probes, dashboards, and operational tooling.

Authentication: None — the management path is not protected by the API token. Restrict access at the reverse proxy level if needed.

Response: application/json

Example response (abbreviated):

{
  "build.version": "0.1.2",
  "build.timestamp": "2026-03-27T14:30:00Z",
  "startup.datetime": "2026-03-27T14:31:05+00:00",
  "status.datetime": "2026-03-27T15:00:00+00:00",
  "version.java": "21.0.2",
  "version.phase4": "4.4.1",
  "version.peppol-commons": "12.4.0",
  "version.ddd": "0.8.10",
  "database.type": "POSTGRESQL",
  "peppol.stage": "test",
  "peppol.owner.seatid": "POP000001",
  "peppol.owner.countrycode": "AT",
  "peppol.identifier.mode": "strict",
  "peppol.sending.enabled": true,
  "peppol.receiving.enabled": true,
  "forwarding.mode": "http_post_sync",
  "storage.mode": "filesystem",
  "mls.sending.enabled": true,
  "mls.type": "ALWAYS_SEND",
  "verification.inbound.enabled": false,
  "verification.outbound.enabled": false,
  "peppol.reporting.schedule.enabled": true,
  "proxy.http.configured": false,
  "proxy.http.username.configured": false,
  "duplicate.detection.as4.mode": "reject",
  "duplicate.detection.sbdh.mode": "reject",
  "sentry.enabled": false,
  "otel.enabled": false,
  "dns.config.servers": ["/192.168.0.1:53"]
}

startup.datetime, peppol.stage and peppol.owner.seatid are only contained if they have a value; all other keys are always present.

When the status endpoint is disabled via management.status.enabled=false, the response is:

{
  "status.enabled": false
}

Notes

  • All /api/* endpoints require the X-Token header matching the configured phase4.api.requiredtoken value. If the token is not configured, API authentication is disabled. A missing or wrong token results in 401 with the body {"error":"Invalid or missing API token"}.
  • The /management/* endpoints do not require API token authentication.
  • All responses are JSON, except the Peppol Reporting APIs (XML) and the Operations payload endpoints (raw bytes).
  • The AS4 receiving endpoint is registered at /as4 by the phase4 servlet — see Receiving Process. It is not part of these REST APIs and is not protected by the X-Token header, because it uses AS4 message level security instead.

Clone this wiki locally