-
Notifications
You must be signed in to change notification settings - Fork 18
Message Level Status
MLS is a Peppol-defined asynchronous feedback mechanism that allows the receiving Access Point (C3) to inform the sending Access Point (C2) about the processing outcome of a received business document. MLS messages are themselves regular Peppol documents sent via AS4, reusing the existing outbound transaction infrastructure including retry logic.
The AP handles MLS in both roles:
- As C3 (receiving): After determining the forwarding outcome, the AP generates and sends an MLS response back to C2.
- As C2 (sending): The AP receives MLS responses asynchronously via AS4, correlates them with outbound transactions, and updates the MLS status.
| Code | Name | When sent |
|---|---|---|
| AP | Approved | Successful delivery with confirmation (forwarding mode http_post_sync or http_post_async) |
| AB | Accepted Blind | Delivered without delivery confirmation (forwarding mode sftp, s3_link, or filesystem) |
| RE | Rejection | Document failed verification (status reason SV, BV and/or BW — see verifier-driven issue mapping) or permanent forwarding failure to C4 (status reason FD) |
The choice between AP and AB depends on the forwarding mode: HTTP modes provide delivery confirmation, while SFTP, S3, and Filesystem do not.
Included in MLS rejection (RE) response line items.
| Code | Name | Description |
|---|---|---|
| BV | Business Rule Violation (Fatal) | Fatal business rule violation -- document does not conform to required business rules |
| BW | Business Rule Warning | Non-fatal issue, may accompany a BV but cannot trigger rejection alone |
| FD | Failure of Delivery | Permanent inability to forward the document to C4 (error field is NA) |
| SV | Syntax Violation | XML schema or syntax validation failure |
When the AP receives an inbound business document and determines the forwarding outcome, it generates an MLS response and sends it back to C2.
| Trigger | MLS response code | Status reason |
|---|---|---|
| Successful forwarding via HTTP | AP (Approved) | -- |
| Successful forwarding via SFTP, S3, or Filesystem | AB (Accepted Blind) | -- |
| Document verification failure | RE (Rejection) |
SV, BV and/or BW per issue — chosen by the verifier (see below) |
| Permanent forwarding failure (max retries exhausted) | RE (Rejection) | FD (Failure of Delivery) |
The mls.type configuration property controls when MLS responses are sent:
| Value | Behavior |
|---|---|
ALWAYS_SEND |
MLS response is sent for all outcomes (RE, AP, or AB) |
FAILURE_ONLY (default) |
MLS response is sent only on rejection (RE) |
The active strategy is captured per inbound transaction at reception time, so changing the configuration only affects newly received documents.
MLS sending (as C3) can be globally disabled via mls.sending.enabled=false -- see Configuration.
By default, the MLS response is sent to C2's Peppol Seat ID. The Sender Backend can override this per outbound transaction by providing the mlsTo query parameter on the submission API, which places an alternative Peppol participant ID in the SBDH extension. This is useful when C2 operates multiple access points.
The mlsTo value can be set on:
-
POST /api/outbound/submit/{senderID}/{receiverID}/{docTypeID}/{processID}/{c1CountryCode}-- via themlsToquery parameter (see API Specification) -
POST /api/outbound/submit-auto/{senderID}/{receiverID}/{c1CountryCode}-- via themlsToquery parameter (see API Specification) -
POST /api/outbound/submit-s3-- via themlsTofield in the JSON body (see API Specification)
sequenceDiagram
participant AP as AP (C3)
participant DB as DB
participant P4 as phase4
participant RA as Remote AP (C2)
AP->>AP: Determine MLS response code (RE, AP, or AB)
AP->>AP: Check MLS_TYPE (ALWAYS_SEND or FAILURE_ONLY)
note over AP: If FAILURE_ONLY and outcome is success: skip MLS
AP->>DB: Create outbound_transaction (transaction_type = mls_response)
AP->>DB: Update inbound_transaction (mls_response_code, mls_outbound_transaction_id)
AP->>P4: Send MLS via AS4
P4->>RA: AS4 UserMessage (MLS)
RA-->>P4: AS4 Receipt
P4-->>AP: AS4 result
AP->>DB: Insert outbound_sending_attempt
AP->>DB: Update outbound_transaction status
The MLS sending is recorded as an outbound_transaction with transaction_type = mls_response, linked to the original inbound transaction via mls_inbound_transaction_id. The inbound transaction's mls_outbound_transaction_id is set to reference it back.
MLS response transactions follow the same retry logic as business document transactions -- see Retry and Resilience Patterns.
Breaking change in v0.10.0. The SPI signature changed:
IInboundDocumentVerifierSPI.verifyInboundDocumentnow returns@Nullable MlsOutcomeinstead ofESuccess. Anullreturn (or any non-REJECTIONoutcome) means the verifier passes; a non-nullMlsOutcomewith response codeREJECTIONcauses rejection and the containedMlsOutcomeIssueentries flow into the MLS line responses. See News and noteworthy for the migration note.
Rejection MLS responses now contain a line response per failed validation entry instead of a single generic BV/NA placeholder. Issues are grouped by errorField into MLS line responses (one line response per distinct error field).
Verifier implementations decide the status reason of each issue. The bundled PhormDocumentVerifier in phoss-ap-validation maps phive ValidationResultLists to MlsOutcome via PhiveToMlsMapper:
| phive source | phive severity | MLS status reason |
|---|---|---|
XSD validation artefact (EValidationBaseType.XSD) |
any failure |
SV (Syntax Violation) |
| Schematron / other artefact |
ERROR or FATAL_ERROR
|
BV (Business Rule Violation, fatal) |
| Schematron / other artefact | WARN |
BW (Business Rule Warning) |
| any | below WARN
|
dropped |
Each issue's errorField and description are derived from the phive IError:
| MLS field | Derived from |
|---|---|
errorField |
IError.getErrorFieldName() (the failed Schematron assertion's XPath context) if present; else the formatted IError.getErrorLocation(); else NA (CPeppolMLS.LINE_ID_NOT_AVAILABLE) |
description |
[ruleID] + localised IError.getErrorText(locale) when both are present; otherwise whichever is available; falls back to "Validation failure"
|
If the call to the configured Phorm Validation Service itself fails (network error, malformed response, missing token), the verifier produces a single BV/NA rejection with description "Phorm validation service call failed - see server log for details". FD is reserved for permanent inability to forward to C4 and is not used for verifier-call failures.
When the Phorm URL is not configured (verification.phorm.url empty), the verifier returns null — the inbound document passes the verification step.
When the AP sends a business document to a remote C3, it expects an MLS response back. The MLS response arrives asynchronously as a separate AS4 message.
sequenceDiagram
participant RA as Remote AP (C3)
participant PS as phase4 Servlet
participant DB as DB
RA->>PS: AS4 UserMessage (MLS response)
PS->>PS: Parse MLS, extract SBDH Instance ID reference
PS->>DB: Lookup outbound_transaction by SBDH Instance ID
DB-->>PS: original transaction
PS->>DB: Update outbound_transaction MLS fields
PS-->>RA: AS4 Receipt
The AP correlates the incoming MLS response with the original outbound business document transaction using the SBDH Instance Identifier referenced in the MLS message. On successful correlation, the outbound transaction is updated with:
| Field | Value |
|---|---|
mls_status |
received_ap, received_ab, or received_re (mapped from the MLS response code) |
mls_received_dt |
The AS4 timestamp when the MLS response was received |
mls_id |
The MLS document's own ID (cbc:ID) |
mls_inbound_transaction_id |
FK to the inbound transaction representing the received MLS message |
If no matching outbound transaction is found, the AP logs a warning and triggers the onInboundMLSCorrelationError() notification.
Tracked on outbound business document transactions as mls_status:
| Value | Description |
|---|---|
pending |
MLS response has not yet been received |
received_ap |
MLS response received with code AP (Approved) |
received_ab |
MLS response received with code AB (Accepted Blind) |
received_re |
MLS response received with code RE (Rejection) |
not_applicable |
MLS is not expected for this transaction (e.g., MLS response transactions themselves) |
The Peppol Network Policy defines Service Level Requirements (SLRs) for MLS timing, measured monthly for payloads smaller than 10 MB.
All milestones are based on AS4 MessageInfo/Timestamp values (UTC).
| Milestone | Definition | Where captured in phoss-ap |
|---|---|---|
| M1 | AS4 Timestamp when the business document transmission is initiated at C2 |
outbound_sending_attempt.as4_timestamp (outbound) or inbound_transaction.as4_timestamp (inbound) |
| M2 | AS4 Timestamp when the MLS message transmission is initiated at C3 |
outbound_sending_attempt.as4_timestamp on the MLS response's successful sending attempt |
| M3 | Date/time when the MLS message is received at C2 | outbound_transaction.mls_received_dt |
| SLR | Constraint | Target | Measurement |
|---|---|---|---|
| SLR MLS-1 (Latest MLS Sending) | M2 - M1 <= 20 minutes | 99.5% | This AP as C3: time from receiving a business document to successfully sending the MLS response |
| SLR MLS-2 (Latest MLS Reception) | M3 - M1 <= 25 minutes | 99.5% | This AP as C2: time from successfully sending a business document to receiving the MLS response |
The AP provides dedicated API endpoints for monitoring SLR compliance:
-
GET /api/mls/sla/mls1-- MLS-1 SLA report (this AP as C3, measuring M2 - M1). See API Specification. -
GET /api/mls/sla/mls2-- MLS-2 SLA report (this AP as C2, measuring M3 - M1). See API Specification.
Both endpoints return:
| Field | Description |
|---|---|
totalCount |
Total number of MLS responses measured |
withinSlaCount |
Number within the threshold |
compliancePercent |
Actual compliance percentage |
targetPercent |
Required target (99.5) |
thresholdSeconds |
SLA threshold in seconds (1200 for MLS-1, 1500 for MLS-2) |
meetingSla |
Whether the target is met |
entries |
Individual measurements with sbdhInstanceID, m1, m2OrM3, durationSeconds, withinSla
|
All MLS-related API endpoints. Full details in API Specification.
| Method | Path | Description |
|---|---|---|
| GET | /api/mls/missing |
List inbound transactions that have not yet received an MLS response |
| GET | /api/mls/sla/mls1 |
MLS-1 SLR compliance report (C3 side: M2 - M1) |
| GET | /api/mls/sla/mls2 |
MLS-2 SLR compliance report (C2 side: M3 - M1) |
MLS-related fields are also returned on existing transaction status endpoints:
| Method | Path | MLS fields |
|---|---|---|
| GET | /api/outbound/status/{sbdhInstanceID} |
mlsTo, mlsStatus, mlsReceivedDT, mlsId
|
| GET | /api/inbound/status/{sbdhInstanceID} |
mlsTo, mlsType, mlsResponseCode, mlsOutboundTransactionID
|
| Field | Description |
|---|---|
mls_to |
MLS_TO target participant ID (from SBDH extension) |
mls_type |
MLS sending strategy at time of reception (FAILURE_ONLY or ALWAYS_SEND) |
mls_response_code |
MLS response code sent or to be sent (RE, AP, AB) |
mls_outbound_transaction_id |
FK to the outbound_transaction representing the MLS sending |
| Field | Description |
|---|---|
mls_to |
MLS_TO override participant ID (only for business_document transactions) |
mls_status |
MLS reception status: pending, received_ap, received_ab, received_re, not_applicable
|
mls_received_dt |
When the MLS response was received |
mls_id |
The MLS message's own ID (cbc:ID) |
mls_inbound_transaction_id |
FK to the inbound_transaction that represents the received MLS message (only for mls_response transactions) |
| Field | Description |
|---|---|
as4_timestamp |
AS4 MessageInfo/Timestamp (UTC). For business_document attempts this is MLS Milestone M1; for mls_response attempts this is MLS Milestone M2. |
See Database Design Notes for the full schema.
All MLS configuration properties. See Configuration Properties for the full configuration reference.
| Property | Default | Description |
|---|---|---|
mls.sending.enabled |
true |
Global switch to enable or disable all outbound MLS sending. When false, no MLS responses are sent for any inbound transaction. |
mls.type |
ALWAYS_SEND |
MLS sending strategy: FAILURE_ONLY (only on rejection) or ALWAYS_SEND (for all outcomes). Captured per inbound transaction at reception time. |
The AP dispatches MLS-related events to two runtime extension SPIs, so integrations can react the moment an MLS is received instead of polling the MLS query API.
Success side — IAPLifecycleEventSPI:
| Event | Method | When |
|---|---|---|
| MLS received & correlated | onInboundMLSCorrelated() |
An inbound MLS response was successfully matched to the outbound business document it refers to |
The onInboundMLSCorrelated() callback carries:
| Parameter | Type | Description |
|---|---|---|
sMlsTransactionID |
String |
Transaction ID of the inbound MLS |
sReferencedSbdhInstanceID |
String |
SBDH Instance Identifier of the original outbound document |
sCorrelatedOutboundTransactionID |
String |
Transaction ID of the correlated outbound business document — use it to look up sender, receiver, document type and process |
eMlsResponseCode |
EPeppolMLSResponseCode |
Response code carried by the MLS (ACCEPTANCE / ACKNOWLEDGING / REJECTION) |
eMlsReceptionStatus |
EMlsReceptionStatus |
Resulting reception status persisted on the outbound transaction (RECEIVED_AP / RECEIVED_AB / RECEIVED_RE) |
sMlsDocumentID |
String (nullable) |
Document ID of the received MLS ApplicationResponse |
aMlsReceivedDT |
OffsetDateTime |
AS4 reception timestamp of the MLS |
aRoundTrip |
Duration (nullable) |
Wall-clock time from outbound send completion to MLS reception; powers the MLS-1 / MLS-2 SLA histograms |
Failure side — IAPNotificationHandlerSPI:
| Event | Method | When |
|---|---|---|
| MLS correlation error | onInboundMLSCorrelationError() |
An inbound MLS response cannot be correlated to any outbound business document (no matching SBDH Instance ID found) |
It is appreciated if you star the GitHub project if you like it.
Donation link: https://paypal.me/PhilipHelger
- Home
- News and noteworthy
- Running phoss AP
- Architecture Overview
- API Specification
- Configuration Properties
- Code Lists
- Database Design Notes
- Maven Module Structure
- Runtime Extensions
- OpenTelemetry Integration
- Security Considerations
- Peppol Specifics
- Testing Without Peppol Network
- Known Users
- Migrating from phase4-peppol-standalone
- Contributing