Message Level Status - phax/phoss-ap GitHub Wiki
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) |
Since v0.12.0 a transaction with verification_result = rejected gets exactly one MLS, the RE of the rejection. Such a transaction only exists if a rejected document is nevertheless forwarded to C4 via verification.inbound.rejection-forwarding — see Forwarding a rejected document. Any later MLS for it is suppressed, because it would contradict the RE that was already sent: neither the positive AP / AB after a successful forwarding, nor the "forwarding failed" MLS when the retries are exhausted, is sent. The suppression is derived from the persisted verdict, so it applies identically to the initial receive path, the retry scheduler and the operations endpoints, and mls_response_code stays RE.
The table above equates "the forwarding HTTP call returned 2xx" with "the document was delivered". For a C3 that forwards directly to C4 that is true. For a C3 that forwards to a middleware in front of C4 it is not: the middleware accepted the document, but the delivery to C4 is still pending. The AP sent at that moment states a fact that has not happened yet, and a later failure between middleware and C4 can no longer be reported, because the MLS for that message is already closed.
The mls.sending.trigger property decides who makes that statement:
| Value | Behaviour |
|---|---|
auto (default) |
The positive MLS is sent immediately after the successful forwarding. Byte for byte the behaviour of all versions before v0.13.0. |
api |
The positive MLS is deferred. The Receiver Backend reports the real outcome via POST /api/mls/send, and only then is the MLS built and sent. |
Only the success path is deferred. Everything that happens before the document reaches the backend stays automatic, because the backend cannot report on a document it never received:
- the
REof a failed inbound verification, and - the
ABafter exhausted forwarding retries.
Blocking the forwarding call until C4 confirms is not an alternative to this mode: the forwarding runs synchronously inside the AS4 request, so while the HTTP response is pending no AS4 Receipt is returned to C2 and C2's response timeout expires, which makes it retransmit the message.
A backend that stays silent must not push the AP out of MLS-1 compliance — 99.5 % of all MLS responses within 20 minutes, measured from the reception of the business document. In the trigger mode api the retry scheduler therefore runs a fourth cycle: every transaction in status forwarded without an mls_response_code, whose as4_timestamp is older than mls.sending.api.timeout (default 5m), is answered with the fallback MLS mls.sending.api.timeout.code (default AB).
The window is measured from the reception of the document and not from its forwarding, because that is where the MLS-1 clock starts. Anchoring on the forwarding would add the forwarding duration on top of the SLA budget instead of consuming it from it — a document that only reached C4 after 15 minutes of forwarding retries would then be answered at minute 20 at the earliest. A slow forwarding therefore shortens the window of the backend, and in the extreme case the fallback goes out with the first cycle after the forwarding, which is the correct answer when there is no budget left.
Excluded from the watchdog, exactly as from the endpoint, are inbound MLS and MLR documents — they are never answered with an MLS — and documents with verification_result = rejected, which already got the RE of their rejection. Excluded as well is everything with the MLS type FAILURE_ONLY: such a transaction never gets a positive MLS at all, so recording a fallback response code for it would put nothing on the wire and only block the rejection the backend may still report. No database migration is needed for this; the existing columns carry all the information.
The check is part of the existing retry scheduler cycle, so it needs no interval of its own and the effective delay is up to one retry.scheduler.interval longer than the configured timeout. It emits the scheduler span phoss.ap.scheduler.cycle with phoss.ap.scheduler.name = mls-fallback.
mls.sending.trigger |
MLS type (SBDH) | Successful forwarding | API call | Result |
|---|---|---|---|---|
auto |
ALWAYS_SEND |
yes | -- |
AP / AB immediately |
auto |
FAILURE_ONLY |
yes | -- | no MLS |
api |
ALWAYS_SEND |
yes | AP |
AP when the backend reports |
api |
ALWAYS_SEND |
yes | none within the timeout | fallback AB
|
api |
ALWAYS_SEND |
yes |
RE + issues |
RE with issues |
api |
FAILURE_ONLY |
yes |
AP / AB
|
recorded only, not sent |
api |
FAILURE_ONLY |
yes |
RE + issues |
RE sent, whenever the backend reports it |
api |
FAILURE_ONLY |
yes | none within the timeout | nothing — the watchdog skips FAILURE_ONLY, so a later RE is still possible |
api |
any | permanently failed | -- | automatic AB, as always |
POST /api/mls/send is not limited to the trigger mode api. In auto mode it works too, as long as no MLS was determined for the transaction yet — which is what lets a backend report an RE for a document it received but cannot process.
Five parties can want to answer the same inbound transaction: the receive path after a successful forwarding, the permanent failure of the forwarding retries, the verification rejection, the MLS watchdog and the Receiver Backend through POST /api/mls/send. Since v0.13.0 they all create their MLS through the same method, and that method claims the MLS slot of the transaction atomically before it builds anything: UPDATE ... SET mls_response_code=? WHERE id=? AND mls_response_code IS NULL. The first writer wins, every later one is told that the MLS is already decided, and nothing is built or sent for it.
The claim is what a check of the current response code could never be. The transaction the receive path works with is a snapshot that was read before the forwarding call, so a backend that reported an outcome in the meantime is simply not visible in it. The concrete case: the middleware receives and processes the document but the HTTP response is lost, the AP goes to forward_failed and schedules a retry, the backend — which does have the document — reports the outcome through the endpoint, and the retries then exhaust. Without the claim the exhausted retries would send a second, contradicting MLS.
If the creation fails after the claim was won, the claim is given back, so a later attempt can still answer C2. A response code that already belongs to a real MLS outbound transaction can never be erased by that.
For inbound documents, the MLS_TYPE of the received SBDH controls when MLS responses are sent. If it is missing or invalid, FAILURE_ONLY applies. The mls.type configuration property is not used for this - it only sets the MLS_TYPE of outbound documents.
| 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.
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)
When acting as C3, the AP determines where to send the MLS response following the Peppol MLS SPOG section 5.1 fallback chain:
- Use the
MLS_TOvalue from the incoming SBDH only if it is present, syntactically a valid0242SPIS participant ID, and its 6-digit Main ID correlates to the sending C2's SPID Main ID (derived from the C2 signing certificate'sCN). Redirecting an MLS to a different Service Provider is not allowed, so a non-correlatingMLS_TOis treated as invalid and ignored. - Otherwise fall back to the default SPID receiver
0242:<C2 Main ID>, derived from the C2 Seat ID.
The validation happens at reception time in InboundOrchestrator.getValidMlsTo(...): a valid, correlating MLS_TO is persisted on the inbound transaction (mls_to), while an absent, syntactically invalid, or non-correlating value is dropped to NULL (with a warning logged) so the default SPID is used.
Before the MLS is sent, the effective MLS receiver is resolved via an SMP lookup (OutboundOrchestrator). Per MLS SPOG section 5.4, an unreachable receiver is handled differently for MLS than for regular business documents:
-
Custom
MLS_TOnot reachable — if the effective receiver is a customMLS_TO(i.e. it differs from the default SPID) and it cannot be resolved in SMP (participant or MLS service not registered), the AP fires theonSpecialMlsToNotReachable()notification and retries the lookup against the default SPID receiver. Because a validMLS_TOalways shares C2's Main ID, the default SPID is0242:<Main ID>and is derivable from the receiver itself. -
Default SPID not reachable — if the default SPID receiver itself cannot be resolved, the MLS transaction is queued and retried (per PNP Rule MLS-4) instead of being marked as permanently failed. Retries (which go through the generic two-argument sending path) reconstruct the same fallback automatically for
mls_responsetransactions, so the behaviour is identical to the first attempt.
In contrast, for a regular business_document transaction an unresolvable receiver remains a permanent failure. Transient SMP errors (retry-feasible errors, circuit-breaker rejection) always queue a retry against the same receiver, independent of MLS.
sequenceDiagram
participant AP as AP (C3)
participant DB as DB
participant P4 as phase4
participant RA as Remote AP (C2)
participant C4 as MLS copy sink
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)
opt forwarding.mls-copy.enabled
AP-->>C4: Fire-and-forget copy of the MLS (kind = outbound-mls-copy)
end
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.
An MLS received from another AP is forwarded to C4 like any other inbound document. The MLS that this AP generates itself used to stay inside the AP -- so a downstream consumer that needs the validation detail, e.g. to build a Tax Data Summary, never saw it.
Setting forwarding.mls-copy.enabled=true hands a copy of every AP, AB and RE to a separately configurable sink; see MLS Copy Forwarding. Three properties of the dispatch are worth knowing:
- It happens once the outbound transaction has been created, before the AS4 transmission. The copy is therefore the MLS the AP generated, not proof that C2 received it -- it goes out even if the transmission to C2 subsequently fails.
- It is fire-and-forget: a failing sink never affects the MLS sending to C2, never touches the inbound transaction status and is never retried.
- The payload is the bare
ApplicationResponseas it is stored, not an SBD. A received MLS arrives as a full SBD, so a consumer that handles both distinguishes them by thekindof the forwardable document (inbound-mlsversusoutbound-mls-copy).
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.
Breaking change in v0.12.0.
verifyInboundDocumentreturnsVerificationOutcomeinstead of@Nullable MlsOutcome.VerificationOutcome.rejected (aMlsOutcome)keeps the issue mapping described here unchanged;VerificationOutcome.serviceUnavailable (sMessage)states that the document could not be inspected at all, which is handled perverification.verifier-fail-modeand is never an implicit rejection. For a rejection without MLS details — and for an unavailability that ends up as a rejection — the AP generates a singleFDline response naming the verifier.
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).
Since v0.12.0, line responses are not limited to rejections. MLS permits details on the positive response codes as well, so the warnings of a verification that accepted the document are sent to C2 as line responses of the AP / AB response — previously they were discarded. Whether the MLS is a rejection is decided by the AP's verdict and by the severity of the findings, not by their mere presence:
| Verification result | MLS response code | Line responses |
|---|---|---|
| Passed, no findings |
AP / AB
|
none |
| Passed, warnings only |
AP / AB
|
one BW per warning |
| Rejected, at least one error | RE |
one per finding (SV / BV / BW) |
| Rejected, warnings only (contradictory) | RE |
a synthesized BV/NA naming the verifier, plus the warnings |
AP versus AB is decided by the forwarder, not by the verification: AP for delivery with confirmation, AB without.
Verifier implementations report VerificationIssues; the status reason of each MLS line response is derived from them when the MLS is built. The bundled PhormDocumentVerifier in phoss-ap-validation maps phive ValidationResultLists to issues via PhiveToVerificationMapper, and MlsOutcomeIssue.fromVerificationIssue (...) projects them onto MLS:
First, phive is mapped to a VerificationIssue (since v0.12.0):
| phive source | phive severity | type |
level |
|---|---|---|---|
XSD validation artefact (EValidationBaseType.XSD) |
ERROR / FATAL_ERROR
|
syntax |
error |
| XSD validation artefact | WARN |
syntax |
warning |
| Schematron / other artefact |
ERROR / FATAL_ERROR
|
business_rule |
error |
| Schematron / other artefact | WARN |
business_rule |
warning |
| any | below WARN
|
dropped | dropped |
Then the issue is projected onto MLS. The projection is severity-first, because reporting a warning as a fatal violation would tell C2 that the document is unusable:
level |
type |
MLS status reason |
|---|---|---|
error |
syntax |
SV (Syntax Violation) |
error |
business_rule |
BV (Business Rule Violation, fatal) |
warning |
any |
BW (Business Rule Warning) |
Changed in v0.12.0. An XSD entry with severity
WARNpreviously becameSV, i.e. a fatal syntax violation. It is now reported asBW. Fatal entries are unaffected, so the MLS produced for a genuinely invalid document is unchanged.
The remaining MLS fields are derived as before:
| MLS field | Derived from |
|---|---|
errorField |
VerificationIssue.getLocation() — the failed Schematron assertion's XPath context if present, else the formatted IError.getErrorLocation(); NA (CPeppolMLS.LINE_ID_NOT_AVAILABLE) when the issue has no location |
description |
[code] + VerificationIssue.getDescription() when a rule ID is present, otherwise the description alone; falls back to "Validation failure"
|
The rule ID is a separate field (code) on the neutral issue and is only folded into the description here, because MLS has nowhere else to put it. Consumers of the outbound submit API get it as its own JSON field — see API Specification.
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 |
|---|---|---|
| POST | /api/mls/send |
Trigger the MLS of an inbound transaction from the Receiver Backend (since v0.13.0) |
| 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 type of the received SBDH, FAILURE_ONLY if missing or invalid (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. |
mls.sending.trigger |
auto |
What triggers the positive MLS of a successfully forwarded document: auto (immediately after the forwarding) or api (only when the Receiver Backend calls POST /api/mls/send). Since v0.13.0. |
mls.sending.api.timeout |
5m |
Only with trigger=api: after this duration without a reported status, the watchdog sends the fallback MLS. Since v0.13.0. |
mls.sending.api.timeout.code |
AB |
The response code of that fallback MLS — AB or AP. Since v0.13.0. |
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) |
| Special MLS_TO not reachable (since v0.11.0) | onSpecialMlsToNotReachable() |
An outbound MLS could not be delivered to a custom MLS_TO receiver because it was not resolvable via SMP, and sending falls back to the default SPID receiver (see SMP lookup fallback) |
The onSpecialMlsToNotReachable() callback carries:
| Parameter | Type | Description |
|---|---|---|
sOutboundTransactionID |
String |
Transaction ID of the outbound MLS being sent |
sReferencedSbdhInstanceID |
String |
SBDH Instance Identifier of the original business document the MLS refers to |
sAttemptedMlsToParticipantID |
String |
The custom MLS_TO receiver participant ID that could not be reached |
sFallbackDefaultSpidParticipantID |
String |
The default SPID receiver participant ID used as the fallback target |
The OpenTelemetry metrics handler additionally increments the phoss.ap.outbound.mls.special_to_not_reachable counter.