# HybridmailPingenSpecification.md ## 1. Document Status **Document:** HybridmailPingenSpecification.md **Project:** hybridmail-connect **Provider Flavor:** Pingen **Parent Specification:** HybridmailAdapterSpecification.md **Target Integration:** coordination-engine **Adapter Contract:** AdapterInterfaceSpecification.md v1.0 **Status:** Draft v1.0 **Primary Scope:** Pingen-specific hybrid-mail provider flavor for letter upload, letter creation, validation, auto-send/manual-send control, Track & Trace, webhooks, return mail, delivered/undeliverable status mapping, and idempotent API usage. ## 2. Purpose This document specifies the Pingen provider flavor for `hybridmail-connect`. The Pingen flavor implements the generic hybrid-mail adapter model for the Pingen Letter API. It maps Pingen’s API concepts into the provider-neutral hybrid-mail model used by `coordination-engine`. The purpose of this document is to capture: * Pingen-specific provider capabilities. * Pingen-specific workflow stages. * Pingen letter, file, address, validation, send, status, Track & Trace, and webhook concepts. * Pingen return-mail and undeliverable processing. * Pingen delivery-confirmation semantics. * Pingen idempotency and rate-limit behavior. * Pingen evidence event mapping. * Pingen-specific limitations and assumptions. * Pingen-specific MVP boundaries. The Pingen flavor MUST remain compatible with `HybridmailAdapterSpecification.md` and MUST NOT leak Pingen-specific terminology into the core coordination model except through mapped provider metadata. ## 3. Grounding Summary The public Pingen material indicates the following relevant features: * Pingen provides a Letter API for automated postal-letter sending. * Pingen offers SDKs, including PHP, Python, Go, .NET, and code examples. * API authentication uses OAuth-style client credentials in common tooling/examples. * Pingen supports Idempotency-Key headers. * Pingen supports rate limiting and configurable send limits. * Pingen offers staging/sandbox or full-feature simulation. * Pingen supports document validation. * Pingen supports configurable address position. * Pingen supports auto-send control. * Pingen offers Track & Trace for real-time progress transparency. * Pingen provides webhooks for status changes. * Public webhook categories include letter issues, sent letters, undeliverable letters, and delivered letters. * Pingen automates return-mail processing and can provide return details via email and webhook. * Pingen distinguishes ordinary postal dispatch from actual delivery-confirmation products. Ordinary letters may not receive final post-office delivery confirmation. These facts define the baseline for this provider flavor. ## 4. Provider Flavor Identity ```yaml HybridmailProviderFlavorDescriptor: provider_name: pingen provider_label: Pingen Letter API provider_family: hybridmail adapter_contract_version: "1.0" parent_specification: HybridmailAdapterSpecification.md deployment_mode: external ``` ## 5. Pingen Capability Descriptor The Pingen flavor MUST expose a capability descriptor. ```yaml HybridmailProviderFlavorDescriptor: provider_name: pingen provider_version: provider_specific supported_file_types: - pdf supported_attachment_file_types: - provider_specific supports_single_letter: true supports_bulk: true supports_serial_letters: true supports_attachments: provider_specific supports_preview: true supports_webhooks: true supports_polling: true supports_track_and_trace: true supports_registered_mail: true supports_delivery_confirmation: product_dependent supports_return_mail: true supports_address_correction: provider_specific supports_international_mail: true supports_cancellation: provider_specific supports_staging: true supports_full_feature_simulation: true supports_idempotency: true supports_rate_limits: true supports_configurable_send_limits: true supports_auto_send: true supports_address_position_configuration: true supports_document_validation: true supported_print_options: color_mode: provider_specific simplex_duplex: provider_specific envelope_selection: provider_specific address_position: true supported_postal_products: - standard - registered - delivery_confirmation - international - provider_specific limitations: - Ordinary postal letters usually do not provide final post-office delivery confirmation. - Delivered-letter webhook semantics must be interpreted according to selected product and Pingen status details. - Return-mail information may arrive late. - Public documentation confirms webhook categories, but exact event payload fields must be implemented against the live Pingen API documentation. - Provider-native status names must be preserved in metadata and mapped conservatively. ``` ## 6. Pingen-Specific Conceptual Workflow The Pingen flavor uses a file/letter-centric workflow. ```text authenticate → upload file / create letter → configure address position and sending options → validate letter → optionally auto-send or manually trigger send → Track & Trace status changes → webhook or polling status ingestion → sent-letter evidence → undeliverable/return-mail evidence where applicable → delivered-letter evidence where product/status supports it ``` The adapter MUST preserve this distinction: ```text Letter creation is not sending. Validation success is not postal dispatch. Sent status is not necessarily final delivery. Ordinary postal dispatch is not proof of human awareness. Return-mail events may arrive late and can change the delivery assessment. ``` ## 7. Pingen Object Mapping ## 7.1 Generic to Pingen Mapping | Generic hybrid-mail concept | Pingen flavor concept | | --------------------------- | --------------------------------------------------------------------------------------------- | | `HybridmailLetter` | Pingen letter | | `HybridmailDocumentRef` | Uploaded file / letter document | | `HybridmailAttempt` | Pingen letter creation/send operation | | `HybridmailBatch` | Bulk or serial letter group where used | | `PostalRecipient` | Address extracted from document or configured via address position / metadata where supported | | `HybridmailOptions` | Pingen send, print, address, and delivery options | | `HybridmailPreview` | Pingen preview or validation representation where supported | | `HybridmailTimeline` | Pingen Track & Trace and webhook timeline | ## 7.2 PingenLetter ```yaml PingenLetter: pingen_letter_id: string hybridmail_letter_id: string? coordination_case_id: string? participant_id: string? provider_status: string? normalized_status: string file_ref: ResourceRef page_count: integer? sheet_count: integer? address_position: PingenAddressPosition? options: PingenLetterOptions? validation_result: PingenValidationResult? track_and_trace_ref: string? webhook_event_refs: - string created_at: timestamp updated_at: timestamp? metadata: object? ``` ## 7.3 PingenFile ```yaml PingenFile: pingen_file_id: string? hybridmail_letter_id: string? original_file_ref: ResourceRef file_type: pdf | unknown file_size_bytes: integer? integrity_hash: string? upload_state: pending | uploaded | rejected | failed | unknown created_at: timestamp metadata: object? ``` ## 7.4 PingenAttempt ```yaml PingenAttempt: pingen_attempt_id: string hybridmail_letter_id: string pingen_letter_id: string? provider_operation_id: string? idempotency_key: string operation_type: create_letter | validate_letter | send_letter | cancel_letter | status_poll | webhook_ingest state: pending | accepted | rejected | failed | completed | duplicate | unknown created_at: timestamp updated_at: timestamp? raw_provider_response_ref: string? ``` ## 7.5 PingenBatch ```yaml PingenBatch: pingen_batch_id: string? hybridmail_batch_id: string coordination_case_id: string? batch_type: bulk | serial | campaign | mixed | unknown letters: - hybridmail_letter_id state: pending | partial | completed | failed | unknown created_at: timestamp updated_at: timestamp? ``` Batch-level events MUST NOT be treated as success for every child letter unless Pingen provides per-letter status or policy explicitly accepts batch-level evidence. ## 8. Pingen Address Model Pingen supports configurable address position. The Pingen flavor MUST model address positioning explicitly. ```yaml PingenAddressPosition: source: configured | detected | provider_default | unknown position: left | right | provider_specific | unknown page_number: integer? bounding_box: object? confidence: low | medium | high ``` Generic postal recipient mapping: ```yaml PostalRecipient: recipient_id: string? name_lines: - string organization: string? street: string? house_number: string? address_addition: string? postal_code: string? city: string? region: string? country_code: string? country_name: string? address_source: extracted_from_document | provided_metadata | provider_detected | manual | unknown address_quality: PostalAddressQuality? ``` If Pingen validation detects address issues, they MUST be mapped into `PingenValidationIssue` and then into generic `HybridmailValidationIssue`. ## 9. Pingen Validation Model Pingen validation results MUST be mapped into generic `HybridmailValidationResult`. ```yaml PingenValidationResult: pingen_letter_id: string provider_validation_state: string? validation_state: pending | passed | action_required | failed | unknown issues: - PingenValidationIssue validated_at: timestamp? raw_provider_response_ref: string? ``` ```yaml PingenValidationIssue: provider_issue_code: string? issue_code: string severity: info | warning | action_required | fatal category: format | page_size | pdf | address | address_position | restricted_area | margins | file_size | page_count | postage | country | provider_policy | unknown message: string page_number: integer? bounding_box: object? fixable: boolean? ``` ## 10. Pingen Validation Issue Mapping | Pingen validation concern | Generic validation category | Normalized issue code | | --------------------------------- | --------------------------- | ------------------------------- | | Invalid PDF | `pdf` / `format` | `invalid_pdf` | | Unsupported file type | `format` | `unsupported_file_type` | | Address not found or unreadable | `address` | `address_not_detected` | | Address position invalid | `address_position` | `address_position_invalid` | | Address format invalid | `address` | `address_format_invalid` | | Unsupported destination country | `country` | `unsupported_country` | | Page or file constraints violated | `page_count` / `file_size` | `document_constraint_violation` | | Postal product invalid | `postage` | `unsupported_product` | | Provider policy rejection | `provider_policy` | `provider_policy_rejected` | | Unknown validation issue | `unknown` | `unknown_validation_issue` | Validation failure MUST emit: ```text payload.validation_failed ``` Validation success MUST emit: ```text payload.validation_passed ``` If Pingen classifies an issue as fixable/action-required, the adapter SHOULD include severity `action_required` rather than immediately treating the coordination case as permanently failed. ## 11. Pingen Letter Options The Pingen flavor MUST map Pingen options into the generic `HybridmailOptions`. ```yaml PingenLetterOptions: color_mode: color | grayscale | provider_default | unknown print_sides: simplex | duplex | provider_default | unknown address_position: left | right | provider_default | unknown postage_product: standard | registered | delivery_confirmation | international | provider_specific country_scope: domestic | international | unknown auto_send: boolean? dispatch_mode: manual_review | auto_send | submit_later | scheduled desired_dispatch_date: date? return_mail_handling: none | provider_processed | scan_return | digital_return_info | physical_return | unknown delivery_confirmation_requested: boolean? metadata: object? ``` Provider-native option names MUST be preserved in metadata. The adapter MUST NOT assume support for a postal product unless the Pingen capability descriptor confirms it. ## 12. Pingen Auto-Send Handling Pingen supports auto-send behavior according to public API materials. The adapter MUST explicitly model auto-send, because it affects the transition from creation/validation to physical dispatch. ```yaml PingenAutoSendPolicy: auto_send: boolean send_after_validation: boolean? require_adapter_approval: boolean? require_coordination_policy_approval: boolean? ``` Recommended safety rule: ```text For high-assurance or expensive physical-mail scenarios, auto_send SHOULD default to false unless explicitly enabled by policy. ``` If `auto_send=true`, the adapter MUST make duplicate-send prevention especially strict. ## 13. Pingen Track & Trace Model Pingen provides Track & Trace progress updates. The adapter MUST represent those as a timeline. ```yaml PingenTrackAndTraceEvent: event_id: string pingen_letter_id: string provider_status: string normalized_status: string event_category: letter_issue | sent_letter | undeliverable_letter | delivered_letter | return_mail | processing | unknown occurred_at: timestamp? observed_at: timestamp raw_provider_response_ref: string? metadata: object? ``` Track & Trace events should map to the strongest safe generic event. If the event only indicates process progress, it MUST NOT be mapped to postal delivery confirmation. ## 14. Pingen Webhook Model Pingen public help identifies webhook categories: ```text Letter issues Sent letters Undeliverable letters Delivered letters ``` The adapter MUST support these categories where available. ```yaml PingenWebhookEvent: pingen_webhook_event_id: string webhook_category: letter_issue | sent_letter | undeliverable_letter | delivered_letter | unknown pingen_letter_id: string? provider_event_id: string? occurred_at: timestamp? observed_at: timestamp raw_payload_ref: string? normalized_events: - EvidenceEvent ``` ## 15. Pingen Webhook Category Mapping | Pingen webhook category | Generic event | Evidence interpretation | | ----------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------- | | Letter issues | `payload.validation_failed` or `delivery.payload.rejected` | Strong issue/failure/action-required evidence depending payload | | Sent letters | `delivery.postal.handed_over` or `delivery.payload.submitted` | Strong dispatch evidence only if semantics confirm physical send/handover | | Undeliverable letters | `delivery.postal.undeliverable` or `delivery.postal.return_received` | Strong negative delivery evidence | | Delivered letters | `delivery.postal.delivery_confirmed` | Strong delivery evidence if product/status supports delivery confirmation | Important: ```text A "sent letters" event should not be interpreted as "recipient received the letter". A "delivered letters" event must be interpreted according to the postal product and provider semantics. ``` ## 16. Pingen Return Mail Model Pingen supports automated return-mail processing and provides return details via channels including webhooks. ```yaml PingenReturnEvent: return_event_id: string pingen_letter_id: string provider_return_id: string? return_type: undeliverable | address_unknown | recipient_moved | refused | insufficient_address | other | unknown return_details: string? address_correction: PostalRecipient? return_document_ref: ResourceRef? occurred_at: timestamp? observed_at: timestamp raw_event_ref: string? ``` Return events usually create strong negative delivery evidence: ```text delivery.postal.return_received delivery.postal.undeliverable ``` Return events may arrive late and MUST be preserved even if the coordination case has already progressed. ## 17. Pingen Delivery Confirmation Model Pingen delivery-confirmation evidence is product-dependent. ```yaml PingenDeliveryConfirmation: confirmation_id: string pingen_letter_id: string product_type: delivery_confirmation | registered | provider_specific provider_status: string? normalized_status: delivered | attempted | refused | undeliverable | returned | unknown occurred_at: timestamp? observed_at: timestamp raw_event_ref: string? ``` The adapter MUST only emit: ```text delivery.postal.delivery_confirmed ``` when the Pingen status and selected postal product semantically support delivery confirmation. Ordinary letter dispatch MUST NOT be mapped to `delivery.postal.delivery_confirmed`. ## 18. Pingen Status Model The Pingen flavor MUST preserve raw provider status and map it to normalized status. ```yaml PingenStatusRecord: status_record_id: string pingen_letter_id: string provider_status: string normalized_status: string status_scope: letter | validation | sending | track_and_trace | webhook | return_mail | unknown observed_at: timestamp raw_provider_response_ref: string? ``` ## 19. Normalized Pingen Status Categories The Pingen flavor SHOULD map native statuses into these normalized categories: ```text letter.created letter.uploaded letter.validation_pending letter.validation_passed letter.validation_failed letter.action_required letter.ready_for_sending letter.submitted_for_sending letter.processing letter.handed_to_postal_service letter.sent letter.delivery_confirmed letter.undeliverable letter.return_received letter.cancelled letter.failed letter.status_unknown ``` Where Pingen status granularity is insufficient, the adapter MUST use the nearest weaker event and preserve the raw status in metadata. ## 20. Pingen-to-Generic Event Mapping | Pingen workflow event | Generic event | Evidence interpretation | | -------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------- | | File/letter accepted | `delivery.payload.accepted` | Digital submission accepted | | Letter rejected | `delivery.payload.rejected` | Attempt failed | | Validation passed | `payload.validation_passed` | Provider-processable | | Letter issue | `payload.validation_failed` or `delivery.payload.rejected` | Issue/action-required/failure | | Ready for sending | `delivery.payload.available` | Operational readiness | | Auto-send triggered | `delivery.payload.submitted` | Provider instructed to send | | Manual send triggered | `delivery.payload.submitted` | Provider instructed to send | | Processing | `delivery.production.started` | Provider processing began | | Sent-letter event | `delivery.postal.handed_over` if semantics support; otherwise `delivery.payload.submitted` | Strong dispatch or send-process evidence | | Delivered-letter event | `delivery.postal.delivery_confirmed` if product/status supports | Strong delivery evidence | | Undeliverable-letter event | `delivery.postal.undeliverable` | Strong negative evidence | | Return details received | `delivery.postal.return_received` | Strong negative evidence | | Track & Trace update | status-specific mapped event | Preserve raw status | | Cancellation | `delivery.payload.failed` or cancellation metadata | Attempt stopped | ## 21. Evidence Grading Rules ### 21.1 Pingen Letter Accepted ```yaml event_type: delivery.payload.accepted evidence_grade: strength: weak actor_certainty: none authority_certainty: none payload_certainty: medium interaction_certainty: none timing_certainty: medium channel_certainty: medium non_repudiation_strength: low notes: - Pingen accepted the letter or file into the API workflow. - This does not prove validation, sending, postal handover, or delivery. ``` ### 21.2 Pingen Validation Passed ```yaml event_type: payload.validation_passed evidence_grade: strength: medium actor_certainty: none authority_certainty: none payload_certainty: high interaction_certainty: none timing_certainty: high channel_certainty: high non_repudiation_strength: low notes: - Pingen validation passed. - The letter can proceed to sending according to provider rules. ``` ### 21.3 Pingen Letter Issue / Validation Failed ```yaml event_type: payload.validation_failed evidence_grade: strength: negative actor_certainty: none authority_certainty: none payload_certainty: high interaction_certainty: none timing_certainty: high channel_certainty: high non_repudiation_strength: low notes: - Pingen reported a letter issue or validation failure. - The issue should be categorized and preserved. ``` ### 21.4 Pingen Sent-Letter Event ```yaml event_type: delivery.postal.handed_over evidence_grade: strength: strong actor_certainty: none authority_certainty: none payload_certainty: high interaction_certainty: none timing_certainty: high channel_certainty: high non_repudiation_strength: medium notes: - Pingen reports the letter as sent or handed into the postal process. - This is strong dispatch evidence. - It does not prove that the recipient received or read the letter. ``` If the native status only means “submitted for sending” and not postal handover, the adapter MUST instead map to: ```text delivery.payload.submitted ``` ### 21.5 Pingen Delivered-Letter Event ```yaml event_type: delivery.postal.delivery_confirmed evidence_grade: strength: strong actor_certainty: low authority_certainty: none payload_certainty: high interaction_certainty: low timing_certainty: high channel_certainty: high non_repudiation_strength: medium notes: - Pingen reports delivered-letter evidence. - This event is product- and status-dependent. - It may still not prove that the intended human personally read the payload. ``` ### 21.6 Pingen Undeliverable / Return Mail ```yaml event_type: delivery.postal.return_received evidence_grade: strength: negative actor_certainty: none authority_certainty: none payload_certainty: high interaction_certainty: none timing_certainty: high channel_certainty: high non_repudiation_strength: medium notes: - Pingen reports undeliverable or return-mail processing. - This is strong negative evidence for the physical delivery attempt. ``` ## 22. Pingen Evidence Assessment The Pingen flavor SHOULD provide a Pingen-native evidence assessment. ```yaml PingenEvidenceAssessment: hybridmail_letter_id: string pingen_letter_id: string? participant_id: string? category: success | fail | undef subclass: string confidence: low | medium | high strongest_signal: string? evidence_summary: - string recommended_coordination_interpretation: string? ``` ### 22.1 Pingen Adapter Success Subclasses ```text success.letter_created success.validation_passed success.ready_for_sending success.submitted_for_sending success.sent_letter success.handed_to_postal_service success.delivery_confirmed success.return_info_processed ``` ### 22.2 Pingen Adapter Fail Subclasses ```text fail.upload_rejected fail.validation_failed fail.letter_issue fail.address_position_invalid fail.address_invalid fail.unsupported_file_type fail.unsupported_country fail.product_invalid fail.send_limit_exceeded fail.provider_rejected fail.cancelled fail.undeliverable fail.return_received ``` ### 22.3 Pingen Adapter Undef Subclasses ```text undef.pending undef.uploaded_only undef.validation_pending undef.action_required undef.ready_but_not_sent undef.auto_send_pending undef.submitted_for_sending undef.processing undef.sent_but_delivery_unproven undef.handed_to_postal_service_but_delivery_unproven undef.track_and_trace_pending undef.return_mail_pending undef.no_final_status_expected undef.delivered_status_semantics_unclear undef.conflicting_evidence undef.channel_degraded ``` ## 23. Pingen Workflow States The Pingen flavor SHOULD support these normalized workflow states: ```text created upload_requested uploaded upload_failed validation_pending validation_passed validation_failed action_required ready_for_sending auto_send_pending send_requested submitted_for_sending processing sent handed_to_postal_service delivery_confirmed undeliverable return_received cancelled failed unknown ``` These are Pingen-flavor states, not coordination result states. ## 24. Pingen Idempotency and Duplicate Send Prevention Pingen publicly supports Idempotency-Key headers. The Pingen flavor MUST use them for externally visible create/send operations where supported. The adapter MUST also maintain local idempotency records to prevent duplicate physical sends. Idempotency scope SHOULD include: ```text coordination_case_id participant_id delivery_id payload_id payload_integrity_hash provider_flavor postal recipient letter options auto_send flag ``` A repeated `delivery.submit_letter` request with the same idempotency key MUST NOT create a second physical send. If the repeated request refers to a changed payload, address, auto-send setting, or delivery product, the adapter MUST reject it as an idempotency conflict unless a new idempotency key is provided. ## 25. Pingen Rate Limits and Send Limits Pingen publicly advertises rate limiting and configurable send limits. The adapter SHOULD model rate-limit and send-limit events. ```yaml PingenLimitState: provider_account_ref: string? rate_limit_status: available | limited | exhausted | unknown send_limit_status: available | limited | exhausted | unknown reset_at: timestamp? metadata: object? ``` Relevant events: ```text system.provider.degraded delivery.payload.rejected ``` If a send limit blocks dispatch, the adapter SHOULD classify the letter as: ```text undef.pending ``` if retry/continuation is expected, or: ```text fail.provider_rejected ``` if the provider rejects the request as final. ## 26. Pingen Webhook Handling The Pingen flavor SHOULD prefer webhooks for status changes where available and use polling as a fallback. Webhook handling MUST: * verify authenticity where supported. * deduplicate events. * preserve raw payload references. * map categories conservatively. * accept late return-mail events. * emit per-letter evidence events. * avoid treating webhook delivery to the adapter as postal delivery to the recipient. ```yaml PingenWebhookHandling: enabled: boolean verification_mode: signature | shared_secret | ip_allowlist | none | unknown subscribed_categories: - letter_issue - sent_letter - undeliverable_letter - delivered_letter fallback_polling_enabled: boolean ``` ## 27. Pingen Status Polling The Pingen flavor MUST support polling if the API exposes status retrieval. ```yaml PingenStatusPollingConfig: enabled: boolean initial_delay_seconds: integer interval_seconds: integer max_duration_seconds: integer terminal_statuses: - delivered - undeliverable - returned - failed - cancelled no_final_status_expected_after_seconds: integer? ``` Polling MUST preserve status history and MUST NOT overwrite raw evidence. ## 28. Pingen Raw Event Preservation The Pingen flavor SHOULD preserve raw provider responses or references to them. ```yaml RawPingenEventRef: raw_event_id: string source: api_response | status_poll | webhook | track_and_trace | validation | operator endpoint: string? storage_ref: string? received_at: timestamp redacted: boolean ``` Normalized events SHOULD reference raw Pingen event data where available. ## 29. Pingen Channel Health ```yaml PingenChannelHealth: provider_name: pingen provider_account_ref: string? status: healthy | degraded | failing | unknown authentication_status: valid | expired | missing | insufficient | unknown api_status: healthy | degraded | unavailable | unknown upload_status: healthy | degraded | unavailable | unknown validation_status: healthy | degraded | unavailable | unknown track_and_trace_status: healthy | degraded | unavailable | unknown webhook_status: healthy | degraded | unavailable | not_configured | unknown rate_limit_status: available | limited | exhausted | unknown send_limit_status: available | limited | exhausted | unknown staging_status: healthy | degraded | unavailable | not_configured | unknown known_degradations: - string ``` Health events SHOULD map to: ```text system.provider.degraded system.provider.unavailable system.adapter.health_changed ``` ## 30. Security Requirements The Pingen flavor MUST: * protect Pingen API credentials. * use secure transport. * use provider Idempotency-Key support where available. * maintain local idempotency records. * avoid duplicate physical sends. * verify webhooks where supported. * avoid logging document contents. * protect postal recipient data. * protect return-mail details. * separate staging/sandbox and production configuration. * support tenant/account separation where applicable. ## 31. Privacy Requirements The Pingen flavor SHOULD: * store payload references instead of payload content where possible. * support metadata-only mode after provider submission. * mask postal address data in logs. * support configurable retention of raw provider responses. * support configurable retention of return-mail details. * separate operational diagnostics from coordination evidence. * document provider-side retention limitations. * support deletion or anonymization workflows where legally possible. ## 32. Reliability Requirements The Pingen flavor MUST support: * idempotent create/send requests. * duplicate webhook event detection. * out-of-order status handling. * polling-based status recovery. * late return-mail events. * late delivered-letter events. * retryable upload failures. * non-retryable validation failures. * provider timeout handling. * validation issue preservation. * rate-limit handling. * send-limit handling. * Track & Trace history preservation. * correlation preservation. * dead-letter handling for unprocessable provider responses. ## 33. Minimal API Surface The Pingen flavor SHOULD implement or expose these conceptual operations through `hybridmail-connect`. ### 33.1 Adapter Contract Operations ```text GET /adapter/descriptor GET /adapter/health POST /adapter/actions POST /adapter/events/provider GET /adapter/events GET /adapter/letters/{id}/timeline GET /adapter/letters/{id}/assessment ``` ### 33.2 Pingen Flavor Operations ```text POST /pingen/letters POST /pingen/letters/{id}/send POST /pingen/letters/{id}/cancel GET /pingen/letters/{id} GET /pingen/letters/{id}/status GET /pingen/letters/{id}/track-and-trace GET /pingen/letters/{id}/return-info GET /pingen/letters/{id}/delivery-confirmation GET /pingen/letters/{id}/timeline GET /pingen/letters/{id}/assessment POST /pingen/webhooks GET /pingen/channel-health ``` The exact provider endpoint names MUST be implemented according to the live Pingen API documentation. The conceptual operations above define the adapter-facing semantic model. ## 34. Example End-to-End Flow ### 34.1 Single Pingen Letter with Manual Send 1. `coordination-engine` creates a coordination case. 2. A PDF payload is registered. 3. Policy selects `hybridmail-connect` with provider flavor `pingen`. 4. `coordination-engine` sends `delivery.submit_letter`. 5. `hybridmail-connect` creates a Pingen letter with `auto_send=false`. 6. Pingen accepts the letter. 7. The adapter emits `delivery.payload.accepted`. 8. Pingen validates the letter. 9. If validation passes, the adapter emits `payload.validation_passed`. 10. Policy approves sending. 11. The adapter sends or submits the letter. 12. The adapter emits `delivery.payload.submitted`. 13. Pingen Track & Trace reports sent-letter progress. 14. The adapter emits `delivery.postal.handed_over` only if the native event semantics support physical dispatch/handover. 15. `coordination-engine` evaluates whether dispatch evidence is sufficient for the case policy. ### 34.2 Auto-Send Letter 1. `coordination-engine` submits a letter with `auto_send=true`. 2. The adapter validates idempotency and payload identity. 3. Pingen accepts the request. 4. Validation and send may proceed without separate manual approval. 5. The adapter emits distinct events for validation and send stages as they become observable. 6. If validation fails, the adapter emits `payload.validation_failed`. 7. If sending proceeds, the adapter emits `delivery.payload.submitted` and later stronger status events. ### 34.3 Pingen Letter Issue 1. Pingen reports a letter issue through webhook or status polling. 2. The adapter classifies the issue. 3. The adapter emits `payload.validation_failed` or `delivery.payload.rejected`. 4. `coordination-engine` marks the participant attempt as action-required or failed. 5. Policy requests correction, alternate document generation, or fallback. ### 34.4 Undeliverable / Return Mail 1. A letter was sent. 2. Pingen later reports it as undeliverable or return mail is processed. 3. The adapter emits `delivery.postal.undeliverable` or `delivery.postal.return_received`. 4. `coordination-engine` updates the participant delivery assessment. 5. Policy may trigger address correction, alternate channel, or manual review. ### 34.5 Delivered-Letter Product 1. A letter is sent using a delivery-confirmation-capable product. 2. Pingen reports delivered-letter evidence. 3. The adapter confirms the product/status semantics. 4. The adapter emits `delivery.postal.delivery_confirmed`. 5. `coordination-engine` evaluates whether this satisfies the intended result. ## 35. Pingen MVP Scope The first Pingen flavor implementation should include: 1. Pingen provider flavor descriptor. 2. Adapter descriptor integration. 3. Pingen credential/config handling. 4. Letter creation or upload. 5. Auto-send flag support. 6. Address-position option mapping. 7. Validation result mapping. 8. Sending submission. 9. Track & Trace status polling. 10. Webhook ingestion for supported categories. 11. Return-mail / undeliverable event mapping. 12. Delivered-letter event mapping, product-dependent. 13. Evidence event generation. 14. Letter timeline. 15. Pingen evidence assessment. 16. Provider idempotency header usage. 17. Local idempotency protection. ### MVP Required Pingen Events ```text delivery.payload.accepted delivery.payload.rejected payload.validation_passed payload.validation_failed delivery.payload.available delivery.payload.submitted delivery.production.started delivery.postal.handed_over delivery.postal.delivery_confirmed delivery.postal.undeliverable delivery.postal.return_received delivery.postal.status_unknown system.provider.degraded system.provider.unavailable ``` Where a provider status is unavailable or semantically unclear, the adapter MUST emit the weakest safe event and preserve the raw status. ## 36. Pingen MVP Acceptance Criteria The Pingen flavor MVP is acceptable when it can: 1. Accept a coordination-compatible `delivery.submit_letter` request. 2. Create or simulate a Pingen letter. 3. Preserve correlation and idempotency. 4. Use Pingen Idempotency-Key support where available. 5. Prevent duplicate physical sends for duplicate idempotency keys. 6. Support auto-send configuration. 7. Map validation success to `payload.validation_passed`. 8. Map letter issues or validation failure to `payload.validation_failed`. 9. Map send submission to `delivery.payload.submitted`. 10. Map sent-letter evidence conservatively. 11. Map delivered-letter evidence only when provider/product semantics support delivery confirmation. 12. Map undeliverable/return-mail evidence to strong negative delivery evidence. 13. Provide a Pingen letter timeline. 14. Provide a Pingen evidence assessment. 15. Integrate with `coordination-engine` without overclaiming physical delivery or human awareness. ## 37. Open Questions 1. Which exact Pingen native statuses map to `delivery.production.started`, `delivery.postal.handed_over`, and `delivery.postal.delivery_confirmed`? 2. Which event payload fields are present for Pingen webhook categories in the target API version? 3. How does the target Pingen account expose delivery-confirmation products? 4. Which return-mail details are available through API versus email/webhook? 5. Does the implementation need organization/workspace selection before letter creation? 6. Which Pingen country/product combinations are enabled for the target account? 7. How should configurable send limits be surfaced to coordination-engine policy? 8. How should staging/full-feature simulation be represented in evidence grades? 9. Which address-position values are available in the live API? 10. Should Pingen return details update a shared postal address quality registry? ## 38. Non-Goals The Pingen flavor is not: * a document authoring system. * a PDF renderer by default. * a legal notice system by itself. * a postal carrier. * a general Pingen UI replacement. * the owner of coordination case success. * the owner of contract, payment, or signature result semantics. It integrates Pingen hybrid-mail capabilities into the coordination framework. ## 39. Summary `HybridmailPingenSpecification.md` defines the Pingen provider flavor for `hybridmail-connect`. The Pingen flavor models Pingen as a letter-centric hybrid-mail provider with: * letter/file creation * validation * configurable address position * auto-send/manual-send control * Track & Trace * webhook status categories * sent-letter evidence * delivered-letter evidence where product-supported * undeliverable and return-mail evidence * idempotency and rate-limit support * staging/simulation support The key rule is: > Pingen events are provider, production, postal-chain, Track & Trace, and return-mail evidence. They are not automatic coordination result satisfaction. hybridmail-connect reports Pingen-channel facts and uncertainty. coordination-engine evaluates intended results.