Skip to main content
Every paper notification goes through two distinct phases before it reaches the postal operator: Prepare and Send. The Prepare phase resolves the correct delivery address and calculates costs asynchronously. The Send phase submits the job to External Channel and records the final cost.

The requestId and idempotency

The requestId is the primary key of every delivery request. It is set by the caller (pn-delivery-push) and is stored in the requestId column of the RequestDeliveryDynamoTable DynamoDB table (PnDeliveryRequest.COL_REQUEST_ID). All endpoints are idempotent on requestId:
  • A second POST /paper-deliveries-prepare/{requestId} with the same body returns 200 with the last known state instead of creating a duplicate.
  • A second POST /paper-deliveries-send/{requestId} with the same body returns 200 with the previously calculated SendResponse.
  • A request that conflicts with a stored one (different body, same requestId) returns 409.
For a second delivery attempt (e.g. after a failed first delivery), the caller supplies a new requestId and sets relatedRequestId to the original requestId. Paper Channel uses this to retrieve the first-attempt address and run the second-attempt address resolution flow.

Two-phase async flow

1

Prepare – phase 1: address validation

The caller POSTs to /paper-channel-private/v1/b2b/paper-deliveries-prepare/{requestId} with a PrepareRequest.PaperMessagesServiceImpl.preparePaperSync() runs synchronously:
  1. Validates the request against any existing PnDeliveryRequest in DynamoDB.
  2. Creates a new PnDeliveryRequest with status IN_PROCESSING (PC000) and saves the receiver address as a PnAddress entity.
  3. Pushes an internal SQS event that triggers PreparePhaseOneAsyncServiceImpl.preparePhaseOneAsync().
  4. Returns 204 to the caller (the address resolution happens asynchronously).
Phase 1 (PreparePhaseOneAsyncServiceImpl) then:
  • Calls PaperAddressService.getCorrectAddress() to pick the right address (see Address resolution).
  • Hashes the resolved address and updates PnDeliveryRequest.addressHash.
  • Transitions the request to SEND_TO_DELAYER (PC006).
  • If the address is domestic, looks up cost via PaperTenderService.getSimplifiedCost() and pushes the result to the phase-1 output queue → the request moves to TAKING_CHARGE (PC001).
  • If the address is international, routes to the phase-2 queue for further attachment processing.
2

Prepare – phase 2: attachment processing and National Registry fallback

PreparePhaseTwoAsyncServiceImpl.prepareAsyncPhaseTwo() handles:
  • Retrieving the resolved address from DynamoDB (AddressTypeEnum.RECEIVER_ADDRESS).
  • Filtering attachments through CheckCoverageAreaService.
  • If F24 attachments are present, delegating PDF generation to F24Service.preparePDF(); on error, retrying via prepareFlowStarter.redrivePreparePhaseTwoAfterF24Error().
  • For regular documents, calling SafeStorageService to retrieve page counts and dates from each PDF.
  • On success, updating the request to TAKING_CHARGE (PC001) and sending a PrepareEvent (status OK) to pn-delivery-push via SQS.
When no address is available and a National Registry lookup is needed:
  • The request is set to NATIONAL_REGISTRY_WAITING (PC002).
  • NationalRegistryService.finderAddressFromNationalRegistries() is called with the fiscal code and receiver type.
  • When the registry responds, a correlationId is used to look up the delivery request and resume phase 1.
3

Send: submission to External Channel

After a successful Prepare the caller POSTs to /paper-channel-private/v1/b2b/paper-deliveries-send/{requestId} with a SendRequest.PaperMessagesServiceImpl.executionPaper() runs synchronously:
  1. Loads the PnDeliveryRequest with strong consistency.
  2. Validates the SendRequest against the stored entity (idempotency check).
  3. Calls PaperCalculatorUtils.calculator() to compute the final shipping cost (CostWithDriver).
  4. Compares the calculated cost against the cost stored during Prepare (PnDeliveryRequest.cost). If they differ, sets reworkNeeded = true and returns 422.
  5. Transitions the request to READY_TO_SEND (PC003).
  6. Calls PcRetryUtils.callInitTrackingAndEcSendEngage() to submit to External Channel.
  7. Persists driverCode and tenderCode on the PnDeliveryRequest.
  8. Returns SendResponse with amount (eurocents), numberOfPages, and envelopeWeight.

Request status machine

The statusCode field on PnDeliveryRequest is set by RequestDeliveryMapper.changeState(). The statusDetail value is either PROGRESS, OK, or KO.
CodeEnum nameDescriptionDetail
PC000IN_PROCESSINGRequest received, processing startedPROGRESS
PC001TAKING_CHARGEPicked up by paper channel / new send requestPROGRESS
PC002NATIONAL_REGISTRY_WAITINGWaiting for address from National RegistryPROGRESS
PC003READY_TO_SENDAddress resolved, ready for External ChannelPROGRESS
PC005NATIONAL_REGISTRY_ERRORError retrieving address from National RegistryPROGRESS
PC006SEND_TO_DELAYERRequest sent to the phase-2 delayer queuePROGRESS
PC010UNTRACEABLERecipient completely untraceableKO
PC011PAPER_CHANNEL_DEFAULT_ERRORGeneric errorKO
PC012PAPER_CHANNEL_ASYNC_ERRORError during prepare phaseKO
PC013SAFE_STORAGE_IN_ERRORError fetching attachments from Safe StorageKO
PC014DEFAULT_ERRORGeneric errorKO
PC015F24_WAITINGWaiting for F24 PDF generationPROGRESS
PC016F24_ERRORError during F24 PDF generationPROGRESS
PNALL001DEDUPLICATES_ERROR_RESPONSENormalisation errorPROGRESS
001PRINTEDPrinted by postal operatorPROGRESS
002DELIVERY_DRIVER_AVAILABLEAvailable to delivery driverPROGRESS
003DELIVERY_DRIVER_IN_CHARGETaken in charge by delivery driverPROGRESS
004DELIVEREDDelivered to recipientOK
005DELIVERY_MISSINGDelivery failedOK
006LOST_DAMAGELost, stolen, or damagedOK
007DELIVERED_POST_OFFICEDelivered at post officeOK
008DELIVERY_MISSING_POST_OFFICENot picked up at post officeOK
009IN_STOCKIn storage (compiuta giacenza)PROGRESS
Statuses PC001 through PC016 are internal to pn-paper-channel. Statuses 001009 are reported by External Channel and forwarded to pn-delivery-push.

Key DynamoDB fields on PnDeliveryRequest

PnDeliveryRequest is stored in RequestDeliveryDynamoTable with requestId as the partition key. A secondary index on correlationId (correlation-index) supports National Registry callback lookups.
FieldTypePurpose
requestIdString (PK)Unique request identifier
relatedRequestIdStringPoints to the first-attempt requestId for second attempts
correlationIdString (GSI)National Registry callback correlation
statusCodeStringCurrent lifecycle status (see table above)
statusDetailStringPROGRESS, OK, or KO
addressHashStringSHA-256 hash of the resolved delivery address
costIntegerDelivery cost in eurocents calculated during Prepare
tenderCodeStringActive tender used for cost calculation
driverCodeStringDelivery driver code assigned
reworkNeededBooleanSet to true when Prepare cost differs from Send cost
productTypeStringPostal product (AR, RS, 890, RIR, RIS)
attachmentsListAttachment metadata including page count and file key
refinedBooleanWhether refinement (perfezionamento) has been applied

Build docs developers (and LLMs) love