Skip to content

Forwarding Process

Philip Helger edited this page Mar 27, 2026 · 8 revisions

Forwarding Process

Overview

After an inbound document is received and stored, the AP forwards it to the Receiver Backend (C4). The forwarding mode is selected once per AP instance via the forwarding.mode configuration property. Four modes are available, each with different trade-offs regarding delivery confirmation, infrastructure requirements, and reporting flow.

Forwarding Modes

Mode Config value Transport Delivery confirmation Reporting trigger
HTTP POST Async http_post_async HTTP POST Yes (HTTP 200) Asynchronous callback
HTTP POST Sync http_post_sync HTTP POST Yes (HTTP 200 + JSON) Synchronous (C4 country code in response)
S3 s3_link Amazon S3 PutObject No Asynchronous callback
SFTP sftp SFTP file upload No Asynchronous callback

The delivery confirmation flag determines the MLS response code sent back to C2 on successful forwarding:

  • With delivery confirmation (HTTP modes): MLS response code AP (Approved)
  • Without delivery confirmation (S3, SFTP): MLS response code AB (Accepted Blind)

HTTP POST Async (http_post_async)

The SBD is POSTed to the Receiver Backend. The AP only checks for a successful HTTP status code (2xx). The response body is ignored.

Request

  • Method: POST
  • URL: Configured via forwarding.http.endpoint
  • Content-Type: application/xml
  • Body: The raw SBD XML (SBDH envelope + business document)

Expected Response

  • HTTP Status: 2xx (any successful status)
  • Body: Ignored

Reporting

The Receiver Backend must call back to the AP's inbound reporting API to provide the C4 country code:

POST /api/inbound/report?sbdhInstanceID={id}&c4CountryCode={code}

with the X-Token header set to the configured API token. This triggers the Peppol Reporting record creation.

Configuration

Property Required Description
forwarding.mode Yes http_post_async
forwarding.http.endpoint Yes Target URL of the Receiver Backend

HTTP POST Sync (http_post_sync)

The SBD is POSTed to the Receiver Backend. The response must contain a JSON object with the C4 country code, enabling immediate Peppol Reporting without a separate callback.

Request

  • Method: POST
  • URL: Configured via forwarding.http.endpoint
  • Content-Type: application/xml
  • Body: The raw SBD XML (SBDH envelope + business document)

Expected Response

  • HTTP Status: 2xx
  • Content-Type: application/json
  • Body: A JSON object containing at minimum the countryCodeC4 field

Minimal response example

{
  "countryCodeC4": "DE"
}

Full response example (as returned by the phoss-ap test backend)

{
  "countryCodeC4": "AT",
  "documentId": "a4f7e2b1-8c3d-4e5f-9a6b-1c2d3e4f5a6b",
  "status": "received"
}

Only the countryCodeC4 field is read by the AP. Any additional fields are ignored.

Error response example

If the Receiver Backend cannot process the document, it should return an HTTP error status (4xx or 5xx). Optionally, a JSON body can be included:

{
  "error-source": "Sbdh parsing",
  "error-message": "Invalid SBDH envelope",
  "status": "rejected"
}

The AP treats any non-2xx status as a forwarding failure and schedules a retry.

Reporting

Triggered immediately and automatically by the AP when the forwarding response contains a countryCodeC4 value. No separate callback is needed.

Configuration

Property Required Description
forwarding.mode Yes http_post_sync
forwarding.http.endpoint Yes Target URL of the Receiver Backend

S3 (s3_link)

The SBD is uploaded to an Amazon S3 bucket. The Receiver Backend is expected to pick up documents from S3 independently (e.g., via S3 event notifications or polling).

Upload Details

  • Bucket: Configured via forwarding.s3.bucket
  • Key: {key-prefix}{sbdhInstanceID}.xml (e.g., inbound/550e8400-e29b-41d4-a716-446655440000.xml)
  • Content-Type: application/xml
  • Authentication: Either explicit access key + secret, or IAM role-based (if credentials are omitted)

Reporting

The Receiver Backend must call back to the AP's inbound reporting API after processing:

POST /api/inbound/report?sbdhInstanceID={id}&c4CountryCode={code}

Configuration

Property Required Description
forwarding.mode Yes s3_link
forwarding.s3.bucket Yes S3 bucket name
forwarding.s3.region Yes AWS region (e.g., eu-central-1)
forwarding.s3.access-key-id No AWS access key. If empty, IAM role-based auth is used.
forwarding.s3.secret-access-key No AWS secret key. Paired with access-key-id.
forwarding.s3.key-prefix No Optional prefix for S3 keys (e.g., inbound/). A trailing / is auto-appended if missing.

SFTP (sftp)

The SBD is uploaded to the Receiver Backend via SFTP. An atomic write pattern is used: the file is first written with a .tmp extension and renamed after the transfer completes, so the Receiver Backend never sees partial files.

Upload Details

  • Target directory: Configured via forwarding.sftp.server-directory-upload
  • Filename: {yyyyMMddHHmmss}_{incomingID}.xml (e.g., 20260327143052_abc123.xml)
  • Transfer pattern: Write to filename.xml.tmp, then rename to filename.xml

Reporting

The Receiver Backend must call back to the AP's inbound reporting API after processing:

POST /api/inbound/report?sbdhInstanceID={id}&c4CountryCode={code}

Configuration

Property Required Description
forwarding.mode Yes sftp
forwarding.sftp.host Yes SFTP server hostname
forwarding.sftp.port Yes SFTP server port (typically 22)
forwarding.sftp.username Yes SFTP username
forwarding.sftp.password Yes SFTP password
forwarding.sftp.server-directory-upload Yes Remote directory for uploads

Forwarding Lifecycle

Every forwarding attempt follows the same lifecycle regardless of mode:

RECEIVED  ──>  FORWARDING  ──>  FORWARDED           (success)
                    │
                    └──>  FORWARD_FAILED             (retry scheduled)
                              │
                              └──>  PERMANENTLY_FAILED  (max retries exhausted)
  1. RECEIVED — Document stored, awaiting forwarding.
  2. FORWARDING — Forwarding attempt in progress.
  3. FORWARDED — Successfully delivered to Receiver Backend.
  4. FORWARD_FAILED — Attempt failed, retry scheduled with exponential backoff.
  5. PERMANENTLY_FAILED — Maximum retries exhausted. An MLS rejection (RE) with status reason FD (Failure of Delivery) is sent back to C2.

Each attempt (success or failure) is recorded in the inbound_forwarding_attempt table with:

  • attempt_dt — When the attempt occurred
  • attempt_statussuccess or failed
  • error_code — Machine-readable error classification (null on success)
  • error_details — Human-readable error description (null on success)

Error Codes

Each forwarding mode produces specific error codes on failure:

Mode Error code Meaning
HTTP http_status Non-2xx HTTP status code
HTTP http_response_error Response body could not be parsed as JSON (sync mode)
HTTP http_io_error Network or I/O error during HTTP communication
HTTP http_error Unexpected exception during HTTP forwarding
HTTP http_configuration_error Invalid forwarding mode configuration
S3 s3-error S3 SDK HTTP response indicates failure
S3 s3_error Exception during S3 upload
SFTP sftp_execution SFTP upload command failed
SFTP sftp_error JSch connection or transmission error
SFTP sftp_exception Unexpected exception during SFTP upload
General forward_exception Internal exception caught by the orchestrator

Retry Configuration

Failed forwarding attempts are retried with exponential backoff. A periodic scheduler checks for retry-eligible transactions.

Property Default Description
retry.forwarding.max-attempts 3 Maximum number of forwarding attempts before permanent failure
retry.forwarding.initial-backoff.ms 60000 (1 min) Delay before the first retry
retry.forwarding.backoff-multiplier 2.0 Multiplier applied to the backoff for each subsequent retry
retry.forwarding.max-backoff.ms 3600000 (1 hour) Upper bound on retry delay
retry.scheduler.interval.ms 60000 (1 min) How often the scheduler checks for retry-eligible transactions

The retry scheduler uses SELECT ... FOR UPDATE SKIP LOCKED to safely support multiple AP instances processing retries concurrently.

Circuit Breaker

Forwarding is protected by a circuit breaker (phoss-ap-forwarder) that temporarily halts forwarding attempts when repeated failures are detected, preventing cascading failures.

Property Default Description
circuit-breaker.failure-threshold 5 Consecutive failures before the circuit opens
circuit-breaker.open-duration.ms 60000 (1 min) How long the circuit stays open before allowing a test request
circuit-breaker.half-open-max-attempts 1 Number of test requests allowed in half-open state

Clone this wiki locally