Skip to content

Receiving Process

Philip Helger edited this page Feb 25, 2026 · 21 revisions

Receiving Process

Overview

The receiving process handles inbound Peppol documents arriving at the AP (C3) via AS4 from a remote sending AP (C2) and forwards them to the Receiver Backend (an internal system within C3).

Process Steps

  1. Receive via AS4 - phase4 servlet receives the AS4 message. Duplicate detection is applied on three levels (AS4 Message ID, SBDH Instance Identifier, Business Document ID) — configurable: reject or store-and-flag.
  2. Verify receiver is serviced - Check whether this AP services the addressed receiver participant for the given document type and process. This is implemented via a pluggable SPI (IReceiverCheck or similar) — e.g., by querying the AP's own SMP registration or a local configuration. If the receiver is not serviced, an AS4 error with code EBMS:0004 / PEPPOL:NOT_SERVICED is returned to C2. No database entry is created.
  3. Store in database - The document, metadata, phase4 Incoming ID, and MLS configuration (MLS_TO from SBDH extension, MLS_TYPE from instance config) are persisted to the inbound_transaction table in PostgreSQL.
  4. If storage fails - Respond with an AS4 error to the remote sending AP (C2). The transaction is not recorded.
  5. Optional verification - The received document is validated via the pluggable verification SPI (IDocumentVerifier). If verification fails, the transaction status is set to rejected, a notification is triggered, and no forwarding attempt is made. An MLS response with code RE (Rejection) is sent back to C2.
  6. Forward to Receiver Backend - Deliver the SBD via the configured forwarding mode (per AP instance):
    • HTTP POST (async reporting)
    • HTTP POST with sync response (C4 country code in response)
    • S3 + link forwarding
    • SFTP
  7. Record forwarding attempt - Insert a row into inbound_forwarding_attempt with the outcome (success or failure + error details). Update the parent inbound_transaction status accordingly.
  8. If forwarding fails - The document will be retried by a periodic scheduler. Each retry creates a new inbound_forwarding_attempt row.
  9. Reporting - Either triggered synchronously (from HTTP POST response containing C4 country code) or asynchronously (Receiver Backend calls reporting API with SBDH Instance ID + C4 country code).
  10. On permanent forwarding failure - Notification via generic interface.
  11. Send MLS response - Based on the forwarding outcome and the MLS_TYPE configuration, the AP generates an MLS response (RE, AP, or AB) and sends it back to C2. The MLS sending is recorded as an outbound_transaction with transaction_type = mls_response, linked via mls_inbound_transaction_id. The inbound transaction's mls_outbound_transaction_id is set to reference it.

Sequence Diagram: Receiving and Forwarding

sequenceDiagram
    participant RA as Remote AP (C2)
    participant PS as phase4 Servlet
    participant RC as Receiver Check
    participant DB as DB
    participant FM as Forwarding Module
    participant RB as Receiver Backend

    RA->>PS: AS4 UserMessage

    PS->>DB: 1. Duplicate check (AS4, SBDH, Business)
    DB-->>PS: result
    note over PS: If duplicate and reject mode: return AS4 error

    PS->>RC: 2. Verify receiver is serviced
    RC-->>PS: result
    note over PS: If not serviced: return AS4 error PEPPOL:NOT_SERVICED

    PS->>DB: 3. Store doc in inbound_transaction (incl. MLS config)
    note over PS: If store fails: return AS4 error
    DB-->>PS: stored OK

    PS-->>RA: AS4 Receipt

    PS->>PS: 4. Verify doc (optional)
    note over PS: If rejected: update DB, notify, send MLS RE

    PS->>FM: 5. Forward SBD
    FM->>RB: Deliver SBD
    RB-->>FM: Response (sync mode: C4 country in response)

    PS->>DB: 6. Insert forwarding attempt row
    PS->>DB: 7. Update inbound_transaction status

    opt Sync reporting mode
        PS->>DB: Store reporting record
    end
Loading

MLS Response Sending Flow (after forwarding outcome is known)

sequenceDiagram
    participant AP as AP
    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
Loading

Async Reporting Flow (separate API call from Receiver Backend)

sequenceDiagram
    participant RB as Receiver Backend
    participant API as Reporting API
    participant DB as DB

    RB->>API: Report message (SBDH Instance ID, C4 country code)
    API->>DB: Lookup inbound transaction by SBDH Instance ID
    DB-->>API: transaction data
    API->>DB: Store reporting record (C4 country + data from SBDH)
    API-->>RB: OK
Loading

Forwarding Retry Flow (periodic scheduler)

sequenceDiagram
    participant S as Scheduler
    participant DB as DB
    participant FM as Forwarding Module
    participant RB as Receiver Backend

    S->>DB: Query inbound_transaction where status = failed
    DB-->>S: list of pending

    loop For each failed transaction
        S->>FM: Forward
        FM->>RB: Deliver SBD
        RB-->>FM: result
        FM-->>S: result

        S->>DB: Insert forwarding attempt row
        S->>DB: Update inbound_transaction status

        opt Max retries exhausted
            S->>S: Trigger notification
        end
    end
Loading

Inbound Message Metadata

inbound_transaction (one row per received document)

Field Description
incoming_id Unique ID assigned by phase4 on receipt
c2_seat_id Peppol Seat ID of the sending AP (C2)
c3_seat_id Peppol Seat ID of the receiving AP (C3, this AP)
signing_cert_cn Subject CN of the signing certificate from the AS4 message
sender_id Peppol Participant ID of the sender
receiver_id Peppol Participant ID of the receiver
doc_type_id Peppol Document Type Identifier
process_id Peppol Process Identifier
document_bytes The raw received SBD (SBDH + business document)
document_size Size of the received SBD in bytes
document_hash SHA-256 hash of the received SBD payload for integrity verification
as4_message_id AS4 Message ID from the inbound message
as4_timestamp AS4 MessageInfo/Timestamp from the incoming AS4 message (UTC). MLS Milestone M1 for business documents; M2 for MLS messages.
sbdh_instance_id Peppol SBDH Instance Identifier
business_document_id Optional: ID from the business document
c4_country_code Country of the final receiver (C4), set via reporting
is_duplicate_as4 Duplicate detected on AS4 Message ID level
is_duplicate_sbdh Duplicate detected on SBDH Instance Identifier level
is_duplicate_business Duplicate detected on Business Document ID level
status received, rejected, forwarding, forwarded, forward_failed, permanently_failed
attempt_count Total number of forwarding attempts (default 0)
received_dt When the message was received via AS4
completed_dt When the transaction reached its final successful state
reporting_status Whether reporting record has been created (pending, reported)
next_retry_dt Planned date/time of the next forwarding retry (null unless status is forward_failed)
error_details Summary error from the last failed forwarding attempt
mls_to MLS_TO target participant ID (from SBDH extension)
mls_type MLS sending strategy at time of reception (FAILURE_ONLY, 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

inbound_forwarding_attempt (one row per forwarding try)

Field Description
inbound_transaction_id FK to the parent inbound transaction
attempt_dt Date and time of this forwarding attempt
attempt_status Outcome of this attempt (success, failed)
error_details Error message or reason for failure

Clone this wiki locally