# Communication Hub (CommHub)

## Overview

Communication Hub turns a business event into a controlled, traceable notification workflow. A caller supplies a notification code and business payload; CommHub resolves recipients, selects content, applies delivery policies, submits messages through receiver services, and tracks the outcome.

Typical uses include policy confirmations, payment reminders, renewal notices and one-time passwords. Configuration authors maintain reusable definitions and resources. Integration developers connect business systems and channel receivers. Operators investigate delivery history and handle exceptions.

![CommHub capability map: configure, integrate, orchestrate, control, recover and operate](./image/comm_hub/capability-map.png)

*Figure 1. The capability map covers the complete workflow, from configuration to operational recovery.*

**API details:** [Connect Hub API Reference](https://docs.insuremo.com/tech/pro/sdk/api/connecthub) — endpoint parameters, request/response schemas and error responses.

### Read this guide by task

| Your task | Start here |
| --- | --- |
| Look up an API or schema | [Connect Hub API Reference](https://docs.insuremo.com/tech/pro/sdk/api/connecthub) |
| Understand the system and its boundaries | [Architecture](#architecture) |
| Configure a notification | [Business Guide](#business-guide) |
| Choose channels, timing and customer controls | [Delivery strategies](#delivery-strategies) and [Delivery policies](#delivery-policies) |
| Send from a business application or implement a receiver | [Notification acceptance](#notification-acceptance-sequence) and [Message submission](#message-submission-and-receipt-sequence) |
| Follow a complete business example | [Policy issuance to two roles](#typical-combination-policy-issuance-to-two-roles) |
| Understand snapshots, transactions and persistence | [Design](#design) |
| Understand timeouts, retries and fallback | [Timeout boundaries](#timeout-boundaries), [Retry decisions](#retry-hold-or-advance-fallback) and [Retry timing](#retry-timing-and-attempt-identity) |
| Investigate a message or operate recovery jobs | [Operations Guide](#operations-guide) |

### Architecture

CommHub runs in the standalone **`connect-hub`** application. Its business API base remains **`/comm/v1`**; the current OpenAPI group is **`connecthub`**. The Java SDK targets `connect-hub` by default, with a `comm-hub.app.name` override.

![System architecture with explicit request, submission, receipt and alert paths](./image/comm_hub/system-architecture.png)

*Figure 2. CommHub owns orchestration and history; receiver services own channel/provider integration. The return receipt closes the delivery loop.*

| Boundary | Responsibility |
| --- | --- |
| Business caller | Own the business event, supply current payload and DMS references, choose a stable RequestId. |
| CommHub | Resolve configuration and recipients; apply policies; retain accepted data; coordinate attempts, receipts, scheduling and recovery. |
| Receiver service | Durably accept work, deduplicate an attempt, implement the provider protocol and report evidence according to its receipt capability. |
| Provider | Perform the actual email, SMS, WhatsApp or push operation. Available evidence depends on the provider. |
| Platform services | Tenant routing, authentication, configuration/credentials, DMS, EventBridge, DataIO and Batch V2. |

The built-in delivery driver is `REST_WEBHOOK`. Additional drivers implement `CommDeliveryDriver`; listing a channel does not imply a built-in SMTP or SMS provider client. Credentials are resolved outside editable delivery settings and configuration snapshots.

---

## Business Guide

### Core concepts

| Concept | Meaning |
| --- | --- |
| **Notification** | One accepted business request, identified by RequestId and correlated to a business reference. |
| **Message** | One recipient/channel outcome within that notification. A notification can create several messages. |
| **Delivery Attempt** | One physical submission of a message, with its own callback identity and observed result. |
| **Communication Definition** | A versioned notification code with an effective period, context reference, delivery rules and policy defaults. |
| **Context Definition** | Reusable payload aliases, variable extraction and sensitive-field masking declarations. |
| **Delivery Rule** | Channel, recipient role, condition, recipient binding, template/service selection and optional policy overrides. |
| **Template** | Independently editable content identified by code, channel and exact language ID. |
| **Delivery Service** | A named driver configuration, including nonsecret settings, receipt mode and receipt timeout. |
| **Configuration Snapshot** | Immutable accepted execution configuration. Unchanged configuration is shared across sends; request-specific data remains separate. |

### Configuration relationships

![Definition version owns delivery rules and references shared context, template and service resources](./image/comm_hub/configuration-model.png)

*Figure 3. Rules belong to a selected Definition version. Shared resources remain independently maintained.*

A Definition version is selected at first acceptance using its enabled/published state and effective period: **`[EffectiveTime, ExpiryTime)`**. An absent expiry leaves the period open-ended. Selectable versions of the same notification code cannot overlap.

Published Definitions and their Rules remain editable, subject to validation. Contexts, Templates and Delivery Services can also be edited independently. **Newly accepted sends use the updated configuration; existing accepted sends keep their snapshots.** A Definition version therefore identifies a configuration selection, rather than an immutable release of all dependent content.

### Configure a policy-issued notification

The following is a coherent configuration example, not a sequence of complete administration API requests. Use the administration API/OpenAPI for generated IDs and full save envelopes.

1. **Create the Context.** Alias the customer object, declare reusable variables and mark sensitive display fields.
2. **Register Delivery Services.** Discover registered drivers, validate the current settings locally, and explicitly choose the receiver’s receipt capability.
3. **Create and activate Templates.** Provide content for each required channel and exact language ID. Preview current unsaved content with `previewContent`, using a saved Context or inline synthetic sample.
4. **Create the Definition version.** Set NotificationCode, Context, effective period, default language, delivery strategy and execution policies.
5. **Add Delivery Rules.** Bind the customer identity/address variables; reference the template and service for each channel; order fallback candidates. Use Rule preview to inspect the condition, masked recipient and effective policies before saving.
6. **Validate and enable.** Check references, expressions, language content and effective-window overlap before accepting traffic.

Example Context declarations:

```json
{
  "Contexts": { "Customer": "$.Policy.PolicyCustomer" },
  "Variables": {
    "CustomerId": "Context.Customer.PartyId",
    "CustomerEmail": "Context.Customer.Email",
    "CustomerMobile": "Context.Customer.Mobile",
    "CustomerName": "Context.Customer.Name",
    "PolicyNo": "Payload.Policy.PolicyNo"
  },
  "SensitiveFields": [
    { "Path": "Payload.Policy.PolicyCustomer.Email", "Method": "EMAIL" },
    { "Path": "Payload.Policy.PolicyCustomer.Mobile", "Method": "FULL" }
  ]
}
```

Example configuration selections:

| Object | Example settings |
| --- | --- |
| Definition | `POLICY_ISSUED`, version 1; `DefaultLanguageId=en_US`; `DeliveryStrategy=FALLBACK`; an effective period covering the intended acceptance time. |
| Email rule | Role `CUSTOMER`, channel `EMAIL`, OrderNo 1, CONTEXT source; identity `CustomerId`, address `CustomerEmail`; template `POLICY_ISSUED_EMAIL`; service `EMAIL_RECEIVER`. |
| SMS rule | Role `CUSTOMER`, channel `SMS`, OrderNo 2, CONTEXT source; identity `CustomerId`, address `CustomerMobile`; template `POLICY_ISSUED_SMS`; service `SMS_RECEIVER`. |
| Templates | Exact `LanguageId=en_US`; example body: `Hello ${CustomerName}, policy ${PolicyNo} has been issued.` |
| Services | `REST_WEBHOOK`; receiver-specific nonsecret Settings; `ReceiptMode=FINAL_RESULT` when final delivery evidence is available. |
| Controls | Explicitly choose RESPECT/BYPASS for preference, frequency and quiet hours. For an opted-in demonstration customer, all three can remain RESPECT. |

`en_US` must be a valid language ID in the target platform registry. Use the same identifier consistently in the selected resources and request.

Context extraction can be checked before saving with `POST /comm/v1/context-definitions/previewExtraction`. Supply `Payload`, `Contexts`, `Variables` and optional `SensitiveFields`. The response provides extracted values, source paths and diagnostics without sending a notification. `previewMasking` separately previews declared display masking.

### Delivery strategies

![ALL sends every eligible channel; FALLBACK progresses within an ordered recipient role](./image/comm_hub/channel-strategies.png)

*Figure 4. Read each FALLBACK row as an alternative outcome of the email candidate. Retry keeps its turn; uncertainty holds it; definite final failure permits promotion.*

| Strategy | Behavior | Example |
| --- | --- | --- |
| **ALL** | Evaluate every eligible rule. Each message has its own delivery outcome. | Send a policy email and a separate SMS confirmation. |
| **FALLBACK** | Try candidates in OrderNo order within one role. Advance after a definite final failure. | Try email first; use SMS if email definitively fails after applicable retries. |

A role may override the Definition's default strategy. Pending submission, final-result acceptance, scheduled retry and uncertain outcomes hold the current turn. Successful delivery stops the role's remaining candidates. An `ACCEPTANCE_ONLY` service also stops the chain on durable accepted handoff, while leaving customer delivery unconfirmed.

### Delivery policies

`NotificationClass` describes the business category, such as TRANSACTIONAL, OPERATIONAL or MARKETING. **It does not automatically bypass customer or timing controls.**

| Control | Configuration | Effect |
| --- | --- | --- |
| Customer preference | `PreferencePolicy` | RESPECT evaluates preferences/channel blocks; BYPASS is an explicit exception. |
| Frequency | `FrequencyCapPolicy` | RESPECT checks and reserves a logical-message quota at attempt claim, using the accepted cap configuration and current recipient/channel usage. |
| Quiet hours | `QuietHoursPolicy` | RESPECT delays dispatch during applicable tenant-local channel windows, including overnight windows. |
| Retry | Definition policy or whole Rule override | Controls maximum automatic attempts and capped exponential backoff. |

For each customer/timing control, a Rule override wins over the Definition default. The effective default is RESPECT. A Rule's retry override supplies the whole policy; otherwise it inherits the Definition policy.

![Delivery decisions showing preference, caps, expiry, scheduling and fallback outcomes](./image/comm_hub/delivery-decisions.png)

*Figure 5. Materialization prepares accepted data. Every new physical attempt then passes a separate dispatch gate; a scheduled or previously failed message can still be held or suppressed.*

Use these scenarios to select controls deliberately:

- **OTP:** set a short `ExpiresAt`. If delivery must be attempted outside quiet hours or ordinary preferences, configure the relevant BYPASS policies explicitly. A TRANSACTIONAL label alone changes none of these controls.
- **Renewal reminder:** use `SendAt` for a specific future time, and quiet hours for general channel protection. Scheduling is a lower bound, not an exact-time delivery promise.
- **Marketing:** respect opt-in preferences and frequency caps. Inspect SUPPRESSED outcomes rather than treating them as transport failures.
- **Channel opt-out:** a channel block applies across categories while preference policy is respected. A category subscription update does not implicitly remove that block.

`SendAt` and `ExpiresAt` accept ISO-8601 local or offset date-times; offsets are converted to the tenant timezone. When both are supplied, expiry must be later than SendAt. Expiry prevents new attempts. Unsent work and FAILED messages with a pending retry may retire as EXPIRED. A quiet-hour release at or after expiry also prevents the attempt. In-flight and uncertain outcomes retain their evidence; EXPIRED does not prove that no earlier attempt occurred.

---

## Developer Guide

### Integration paths

| Path | Contract |
| --- | --- |
| REST | `POST /comm/v1/notifications/send`. |
| Java SDK | Typed `CommHubRestfulService`, default application target `connect-hub`. |
| Other languages / iComposer | Generate or use clients from the current `connecthub` OpenAPI group; document endpoint `/v3/api-docs/connecthub` on an OpenAPI-enabled host. |
| Global transaction | Seata TCC accepts a prepared command, then materializes after Confirm. See [TCC](#global-transactions-with-tcc). |

Request and response fields follow the platform JSON naming convention, such as `RequestId`, `LanguageId`, `ExpiresAt` and `RecipientId`. Use the published [API reference](https://docs.insuremo.com/tech/pro/sdk/api/connecthub) for each operation’s binding and validation rules. `LanguageId` replaces the earlier `LANGUAGE_ID` examples; `Recipients[].RecipientId` is the explicit preference subject identifier. A business payload may still contain a caller-defined `PartyId`.

### Send a notification

```http
POST /comm/v1/notifications/send
Content-Type: application/json
```

```json
{
  "RequestId": "policy-issued-10001",
  "NotificationCode": "POLICY_ISSUED",
  "LanguageId": "en_US",
  "SenderApp": "policy-service",
  "RefType": "POLICY",
  "RefNumber": "P-10001",
  "Payload": {
    "Policy": {
      "PolicyNo": "P-10001",
      "PolicyCustomer": {
        "PartyId": "C-001",
        "Name": "Ada",
        "Email": "ada@example.com",
        "Mobile": "+12025550123"
      }
    }
  }
}
```

The example uses the [configuration selections](#configure-a-policy-issued-notification) above and an eligible customer preference. Business payload extraction remains configuration-driven; CommHub does not fetch missing policy/customer details from business services.

| Field | Contract |
| --- | --- |
| `RequestId` | Required tenant-local idempotency key, up to 64 characters. |
| `NotificationCode` | Required code whose enabled effective version is selected at acceptance. |
| `Payload` | Business JSON object, up to 64 KiB serialized. Omitted/null is normalized to an empty object; configured rules may still require values. |
| `LanguageId` | Optional requested platform language. Exact precedence: Rule `LanguageId` → request `LanguageId` → Definition `DefaultLanguageId`. No regional/base/English fallback. |
| `SenderApp`, `RefType`, `RefNumber` | Caller attribution and business correlation. |
| `SendAt`, `ExpiresAt` | Earliest dispatch time and expiry deadline. |
| `Recipients` | REQUEST-source entries contain `Role`, `Channel`, `RecipientId` and `Address`. Role/channel pairs must be unique; eligible FALLBACK candidates in one role must agree on nonblank RecipientId. CONTEXT rules retain their configured bindings. |
| `Attachments` | Up to five caller-owned DMS document references, subject to attachment size and channel rules. |
| `OriginalNotificationId` | Optional link for a new business submission. Requires a new RequestId/current Payload; does not copy or retry the original. |

Common send outcomes are **201** for first local materialization, **200** for equivalent replay, **202** for TCC command acceptance, **409** for conflicting content under the same key, **404** for unavailable definition selection, and **422** when every message fails synchronous validation. Validation/authentication failures have their own errors; consult the generated contract for the complete response schema.

For a REQUEST-source rule, an explicit recipient entry is:

```json
{ "Role": "CUSTOMER", "Channel": "EMAIL", "RecipientId": "C-001", "Address": "ada@example.com" }
```

`RecipientId` is required when the effective preference policy is RESPECT. A respected frequency cap also needs an identity; an anonymous message subject to such a cap is suppressed at claim. Setting preference BYPASS alone does not bypass the frequency cap.

### Notification, Message and Attempt

| Operation | Owner and meaning | Identity and effect |
| --- | --- | --- |
| **Send a Notification** | The business caller submits one business intent through `POST /notifications/send`. CommHub selects configuration and prepares recipient/channel messages. | One RequestId identifies the intent; one NotificationId identifies the materialized aggregate. |
| **Submit a Message** | CommHub claims eligible work and asks the Delivery Service receiver to take responsibility for one recipient/channel output. | The MessageId remains stable; a new AttemptId, ClientRequestId and CallbackId identify this physical submission. |
| **Report a receipt** | The receiver reports evidence for the current attempt through `POST /callbacks/receipt`. | CallbackId fences updates to the active attempt. ACCEPTED and DELIVERED have different meanings. |
| **Retry a Message** | An eligible automatic or manual retry makes another attempt using accepted data. | Same NotificationId and MessageId; new attempt identities. No new business notification is created. |

Routes in this section are relative to `/comm/v1`. The business caller does not receive a plan that it must turn into individual message tasks. CommHub owns message creation, attempt claims and its retry policy. The receiver may be an iComposer API that owns provider-specific request mapping and callback handling.

### Notification acceptance sequence

![Notification acceptance sequence showing idempotency, configuration selection, graph commit and the stored reply](./image/comm_hub/notification-acceptance.png)

*Figure 6. Local acceptance creates a durable Notification–Message graph. The stored reply is an acknowledgement of that request, not a live delivery result.*

1. Validate the envelope and claim the tenant-local RequestId. Equivalent replay returns the stored acknowledgement; conflicting content returns 409 without creating another notification.
2. Select the effective Definition and snapshot. Resolve context, rules, recipients, content and role strategy. Prepare each message as PLANNED, SCHEDULED, WAITING or a recorded validation/policy outcome.
3. Persist the Notification, Messages, attachment references and acknowledgement in the local transaction. Commit before any receiver call.
4. Dispatch eligible PLANNED messages after commit. Recovery workers release scheduled work and due retries. Query APIs report current progress independently of the stored send reply.

**Timing boundary:** after-commit dispatch can run on the submitting thread before the HTTP response finishes. The send API does not promise that the caller receives its reply before the receiver call starts. Provider completion remains separate. Under TCC, Try returns command acceptance instead; materialization waits for Confirm, as shown in [Global transactions](#global-transactions-with-tcc).

### Message submission and receipt sequence

![Physical message submission sequence showing committed attempt claim, receiver handoff, provider result and current-attempt receipt](./image/comm_hub/delivery-sequence.png)

*Figure 7. This FINAL_RESULT example separates the short attempt transaction, external submission and later delivery evidence.*

1. Claim one eligible Message under lock. Recheck dispatch policies, reserve quota and create an Attempt with a new ClientRequestId and CallbackId. Commit SUBMITTED and ActiveAttemptId before external work.
2. The driver sends the frozen outbound content and callback context to the receiver. No message row lock or local claim transaction spans that network call.
3. A 2xx response establishes durable receiver acceptance. CommHub applies ACCEPTED and the frozen receipt mode; FINAL_RESULT starts its receipt deadline, while ACCEPTANCE_ONLY completes tracking at ACCEPTED.
4. The receiver executes provider integration and reports the final outcome. CommHub fences the callback against the active attempt, records events, updates message/notification progress, and evaluates retry or FALLBACK when appropriate.

Provider work and callback delivery may race with the HTTP response. Attempt identity and receipt ordering protect the result. The illustrated order is a normal path, not a required network ordering.

### Caller replay, retry and fallback

| Intent | Operation | What stays the same | What changes |
| --- | --- | --- | --- |
| Caller lost the send response | Repeat send with equivalent content and the same RequestId. | Original accepted request and stored acknowledgement. | No new dispatch is requested by the replay. |
| Retry a known failed delivery | Due automatic retry, or an eligible `POST /messages/retry` with Reason. | Notification, Message, accepted recipient/content/configuration. | A new Attempt and callback identity, only when guards pass. |
| Try the next channel | Internal FALLBACK progression after an eligible definite outcome. | Notification and recipient role. | The next candidate Message takes the turn and creates its own attempt when claimed. |
| Send revised business content | New RequestId and current Payload; optional OriginalNotificationId link. | Optional business correlation to the earlier notification. | A new Notification and newly selected configuration. |

Use `GET /send-requests/getById` for live request progress, then notification/message details, attempts and timeline APIs for investigation. Replaying the send operation does not recalculate its response from today's delivery state.

### Implement a receiver

The `REST_WEBHOOK` driver sends accepted outbound data plus `ClientRequestId`, `CallbackId` and `CallbackPath`, authenticated using the platform tenant token. The receiver must durably take responsibility before acknowledging acceptance and must deduplicate repeated submissions of the same attempt.

| ReceiptMode | Meaning of durable ACCEPTED | Completion and fallback |
| --- | --- | --- |
| **FINAL_RESULT** | The receiver owns the work; CommHub awaits final evidence. | DELIVERED completes successfully. A final receipt deadline without evidence becomes uncertain, holding retry/fallback. |
| **ACCEPTANCE_ONLY** | The receiver owns the work but cannot promise final delivery evidence. | Tracking completes at ACCEPTED. Fallback stops; delivery remains unconfirmed. |

`ReceiptTimeoutSeconds` belongs to FINAL_RESULT mode. It is separate from `Settings.ConnectTimeoutMs` and `Settings.ReadTimeoutMs`, which govern the HTTP exchange. Explicit Settings values override `comm-hub.webhook.connect-timeout-ms` and `comm-hub.webhook.read-timeout-ms`; when omitted, the driver reads those global values at dispatch, with 5,000/10,000 ms defaults. A timeout is never proof of successful delivery.

Example authenticated final callback, correlated with the request's CallbackId:

```http
POST /comm/v1/callbacks/receipt
Content-Type: application/json
X-Comm-Callback-Id: <CallbackId from the outbound request>
```

```json
{ "Status": "DELIVERED", "ProviderRef": "smtp-8842", "Retryable": false }
```

Default webhook classification distinguishes these cases:

| Observation | Treatment |
| --- | --- |
| 2xx acceptance | ACCEPTED; apply the frozen receipt mode. |
| 4xx except 429 | Definite permanent rejection. |
| 429 or a recognized connect failure | Definite retryable failure, subject to accepted retry policy and dispatch eligibility. |
| 5xx | Outcome unknown: the server/gateway error does not establish whether downstream work was accepted. Hold automatic retry and fallback; verify evidence. |
| Read timeout after possible submission | Outcome unknown; alert and verify rather than automatically duplicate the send. |
| Late/duplicate/superseded receipt | Apply attempt identity and receipt ranking; ignored evidence is audited. |

Under the webhook contract, **429 means rejection before acceptance**. A receiver must not return it after taking responsibility. A downstream receiver must not report final failure while continuing its own sends. Otherwise CommHub may legitimately retry or advance fallback while the earlier provider job is still active.

### Timeout boundaries

![Three timeout clocks with separate failure classification and acceptance-only completion](./image/comm_hub/timeout-boundaries.png)

*Figure 8. Follow the clocks from connection to HTTP response to final receipt. A timeout changes the next permitted action according to the evidence available.*

| Clock | Scope and starting point | On timeout |
| --- | --- | --- |
| `Settings.ConnectTimeoutMs` | Establishing the HTTP connection; driver fallback 5,000 ms. | A **recognized** connect failure is a definite retryable failure. The automatic retry guards still apply. |
| `Settings.ReadTimeoutMs` | Waiting for the HTTP response; driver fallback 10,000 ms. | The receiver may already own the job. Mark outcome unknown, alert and hold retry/fallback. |
| `ReceiptTimeoutSeconds` | FINAL_RESULT only. Deadline = **hub time when ACCEPTED is applied + accepted timeout**; default 86,400 seconds. | Keep ACCEPTED, set DeliveryUncertain, clear automatic retry timing and alert. Missing evidence is not a definite failure. |
| `ExpiresAt` | The business notification's fixed deadline, independent of the three clocks. | Prevent new attempts. It does not cancel a provider job already in flight or infer its outcome. |

The driver identifies specific connect exceptions as safely retryable. Other transport errors, including unclassified connection/reset/TLS failures, can be uncertain. Do not classify every network exception as a known failure.

The built-in REST_WEBHOOK driver treats 2xx as ACCEPTED; it does not parse a `DELIVERED` value from a successful HTTP response body. Final-result integrations use the receipt contract. ACCEPTANCE_ONLY finishes tracking on durable acceptance and has no final-receipt deadline.

### Retry, hold or advance fallback

![Decision tree distinguishing unknown outcome, permanent rejection and eligible automatic retry](./image/comm_hub/retry-decision.png)

*Figure 9. An unsuccessful attempt must pass evidence classification and eligibility checks before a new automatic attempt can be created.*

Automatic retry requires all of the following at the due claim:

- FAILED state backed by a failed physical attempt, with a due `NextRetryTime`.
- Remaining automatic attempt budget and an unexpired notification.
- No delivery uncertainty or operator retry stop.
- Ownership of the current FALLBACK turn, when applicable.
- Each accepted RESPECT policy passes: current customer preference allows the send, quiet hours allow it now, and any applicable cap has quota available or already reserved. An explicit BYPASS skips its corresponding policy check.

Policy denial can suppress the message or defer it without creating an attempt. A new attempt is claimed under lock only after the guards allow it, and gets a new identity. It uses the original accepted recipient, content and configuration. A definite final failure can advance to the next FALLBACK candidate when eligible. A timeout with an unknown outcome cannot advance the chain, even if the attempt budget has been exhausted.

**Resolving uncertainty:** a valid receipt for the current attempt may establish acceptance, delivery or definite failure. A late ACCEPTED receipt resumes final-result waiting, or completes acceptance-only handoff. A late DELIVERED receipt completes delivery. An ordinary definite FAILED provider receipt may schedule retry if permitted. An operator-verified failure deliberately does not restart automatic retries. Superseded-attempt, regressive and duplicate receipts are fenced and audited; completed acceptance-only handoffs ignore later receipts.

### Retry timing and attempt identity

![Retry timeline with growing backoff, distinct callback identities and a separate manual path](./image/comm_hub/retry-timeline.png)

*Figure 10. MaxAttempts includes the first submission. Backoff runs from failure processing, and each retry is a new physical attempt of the same Message.*

After failed attempt number `n`, the delay in seconds is:

```text
delay(n) = ceil(min(RetryMaxDelaySeconds,
                    RetryInitialDelaySeconds × RetryBackoffMultiplier^(n−1)))
```

With MaxAttempts 3, initial delay 300 seconds and multiplier 2, the first failure waits at least 300 seconds before attempt 2; the second waits at least 600 seconds before attempt 3. Worker pickup adds latency. There is no automatic attempt 4. A calculated retry time must be strictly before ExpiresAt; otherwise no NextRetryTime is queued. Dispatch checks expiry again when the worker claims due work.

An eligible manual retry can exceed the automatic budget, but each explicit action authorizes at most one MANUAL attempt. The dispatch gate can still prevent creation: for example, a quiet-hour hold rejects the manual retry without creating an automatic schedule. A submission failure or FAILED receipt on a created MANUAL attempt does not restart automatic retries. Expiry, uncertainty, operator stops, fallback ownership and current customer policy checks remain enforced.

### Automatic retry sequence

![Automatic retry sequence from a definite 429 failure through due-time pickup, a new attempt and final receipt](./image/comm_hub/retry-sequence.png)

*Figure 11. The example retries the same Message after a definite 429 rejection. The delay starts when the failure is processed; the next attempt exists only after a successful due claim.*

The recovery worker selects due FAILED work and rechecks it under lock. If state, budget, expiry, ownership and dispatch policies allow it, CommHub commits attempt B, then submits the original accepted output with B's identities. Receiver deduplication applies within each attempt. Old attempt A receipts cannot overwrite B.

A 5xx or lost response takes the uncertainty path instead of this sequence. Quiet hours can move the due time without creating B. Exhausted automatic budget leaves no new automatic attempt; an operator action still requires its own eligibility and reason. See the decision diagram for all branches.

### Typical combination: policy issuance to two roles

This example follows a business-to-channel flow: an iComposer policy issuance API calls CommHub, CommHub orchestrates messages, and iComposer receiver APIs integrate with providers. An explicit receiver integration may use other platform services internally; the send contract does not require the caller to create an Integration Management task.

![Policy issuance combination showing recipient roles, fallback, parallel channels, provider adapters and separate feedback](./image/comm_hub/typical-combination.png)

*Figure 12. One business request combines FALLBACK for the policy holder with ALL for the insured. Channel retries stay within a Message; fallback moves to another Message.*

**Configuration selection** (illustrative values, not a complete administration save request):

| Component | Selection |
| --- | --- |
| Definition | `POLICY_ISSUED_MULTI_ROLE`; default DeliveryStrategy=FALLBACK; `RoleDeliveryStrategyOverrides={"INSURED":"ALL"}`. |
| Context | Extract PolicyNo and EffectiveDate from Payload; REQUEST-source rules take addresses and RecipientId from Recipients. Declare sensitive display fields where needed. |
| POLICY_HOLDER / EMAIL | OrderNo 1; HUB_MANAGED `POLICY_ISSUED_EMAIL`; email receiver; FINAL_RESULT; whole Rule retry override: MaxAttempts=2, RetryInitialDelaySeconds=300, RetryBackoffMultiplier=2, RetryMaxDelaySeconds=3600. |
| POLICY_HOLDER / SMS | OrderNo 2; HUB_MANAGED `POLICY_ISSUED_SMS`; SMS receiver; FINAL_RESULT. Initially WAITING while email owns the turn. |
| INSURED / WHATSAPP | PROVIDER_MANAGED approved template and extracted parameters; WhatsApp receiver; FINAL_RESULT. |
| INSURED / PUSH | HUB_MANAGED `POLICY_ISSUED_PUSH`; push receiver; ACCEPTANCE_ONLY where the receiver cannot provide final evidence. |
| Customer controls | RESPECT preference, cap and quiet hours. Both subjects have allowed preferences and available quota; this walkthrough occurs outside quiet hours and before expiry. |
| Retry and attachment | Email initial retry delay 300 seconds. Prepare the policy PDF in DMS; send a SECURE_LINK reference restricted to POLICY_HOLDER. Receiver/template configuration must support the selected content. |

**One request** (DMS DocId and receiver destinations must exist in the target environment):

```json
{
  "RequestId": "policy-issued-multi-10001",
  "NotificationCode": "POLICY_ISSUED_MULTI_ROLE",
  "LanguageId": "en_US",
  "SenderApp": "policy-service",
  "RefType": "POLICY",
  "RefNumber": "P-10001",
  "Payload": { "PolicyNo": "P-10001", "EffectiveDate": "2026-10-01" },
  "Recipients": [
    { "Role": "POLICY_HOLDER", "Channel": "EMAIL", "RecipientId": "C-001", "Address": "ada@example.com" },
    { "Role": "POLICY_HOLDER", "Channel": "SMS", "RecipientId": "C-001", "Address": "+12025550123" },
    { "Role": "INSURED", "Channel": "WHATSAPP", "RecipientId": "C-002", "Address": "+12025550124" },
    { "Role": "INSURED", "Channel": "PUSH", "RecipientId": "C-002", "Address": "example-device-token" }
  ],
  "Attachments": [
    { "DocId": 123456, "DeliveryMode": "SECURE_LINK", "ExpireDays": 7, "RoleFilter": "POLICY_HOLDER" }
  ]
}
```

**Example execution:**

| Step | What happens | Identity and next action |
| --- | --- | --- |
| 1. Accept | CommHub creates one Notification and four Messages. | Holder email is PLANNED, holder SMS is WAITING; both insured channels are PLANNED. |
| 2. First submissions | The holder email receiver returns 429 before accepting. WhatsApp reports DELIVERED; push durably accepts. | Email attempt 1 is a known failure with a due retry. SMS continues waiting. Insured messages complete independently. |
| 3. Retry email | At least 300 seconds later, the worker passes the full claim gate and submits email attempt 2. This example receives a definite permanent 4xx rejection other than 429. | Same email Message, new attempt identities. No pending retry or uncertainty remains. |
| 4. Promote SMS | The holder's FALLBACK chain gives SMS its turn. SMS passes its own dispatch gate and reports DELIVERED. | New physical attempt on the existing SMS Message. The holder role completes. |
| 5. Investigate or collect feedback | History retains both failed email attempts and all channel outcomes. A customer reply/opt-out enters an iComposer API, which calls the interaction/preference APIs. | Feedback has its own handling lifecycle and never substitutes for delivery evidence. |

This execution has **one Notification, four Messages and five physical Attempts**. Push remains ACCEPTED with tracking complete, so do not label it DELIVERED. If email attempt 1 had returned 5xx or lost its response, the holder chain would pause for verification: no automatic email attempt 2 and no SMS promotion. The insured role can continue independently.

### What each stage actually rechecks

Accepted content and configuration remain frozen; dispatch eligibility is evaluated again before every new physical attempt.

| Stage | Evaluated or enforced | Data used |
| --- | --- | --- |
| Initial materialization | Rule condition, recipient resolution, preference, template/service validity, expiry, SendAt/quiet hours and role strategy. | Resolve and snapshot accepted configuration; prepare message content. Frequency quota is reserved later, at claim. |
| Initial or scheduled claim | State/turn/due-time/TTL guards, then current preference, quiet hours and quota. | Live customer preferences; accepted policy modes, cap rules and time windows. Scheduled pickup rechecks the window at actual dispatch time. |
| FALLBACK promotion | Role completion/ownership, predecessor no longer blocking, operator stop and accepted quiet hours. | Select PLANNED or SCHEDULED; the next physical claim must still pass the full dispatch gate. |
| Automatic retry | Failed-attempt eligibility, due time, budget, expiry, uncertainty, stop and fallback ownership, then dispatch policies. | Reuse accepted recipient/content. Quiet hours keep FAILED and move NextRetryTime; preference/cap denial can suppress. |
| Manual retry | Failed-attempt eligibility, expiry, uncertainty, stop and fallback ownership, then dispatch policies. | May exceed automatic budget. Quiet hours deny the action without creating a new automatic retry schedule. |
| Final-receipt timeout sweep | Frozen receipt mode and stored receipt deadline. | Mark uncertainty and alert; do not produce a new attempt. |

**Frequency quota is per logical message, not per attempt.** The claim serializes the quota check and reservation for a tenant-local recipient/channel. A message with an existing reservation does not consume another slot for each retry. Definite rejection releases an uncommitted reservation; accepted or uncertain submission retains it. A later retry without a reservation must acquire one again. The selected category-specific cap (or `*` fallback) sets the limit/window, while usage is counted across the recipient/channel. Applicable RESPECT caps without RecipientId suppress with `FREQUENCY_IDENTITY_MISSING`.

**Quiet hours can change the next action.** Initial/scheduled work stays SCHEDULED; automatic retries remain FAILED with a later due time. If that release reaches or exceeds ExpiresAt, the message becomes EXPIRED. These checks use accepted window configuration, so editing a window does not retroactively replace an accepted snapshot.

### Templates, attachments and driver extensions

Templates use exact code/channel/language identity and DRAFT/ACTIVE/RETIRED status. Active content is editable. There is no TemplateVersion fixed/latest selection. Missing exact-language content fails explicitly. HUB_MANAGED rendering captures the output; PROVIDER_MANAGED delivery passes template identity/parameters and cannot guarantee the provider's final rendered content or version.

CommHub resolves caller-owned DMS references for secure links or supported attachments. The caller creates and maintains the underlying documents. Delayed materialization must still be able to resolve those references; use immutable/versioned references when document identity must remain fixed. Links also need an appropriate validity period for the intended delivery schedule.

Channels are tenant-configurable codes in the `CommHubChannel` code table, normalized to uppercase and limited to 40 characters (`[A-Z][A-Z0-9_]{0,39}`). EMAIL, SMS, WHATSAPP and PUSH are familiar examples, not a closed runtime enum. Register the code and a compatible Delivery Service before using a new channel.

REST_WEBHOOK services can declare capabilities in Settings:

| Setting | Contract |
| --- | --- |
| `SupportedChannels` | Optional nonempty array of canonical channel codes; the selected channel must be included. |
| `SupportedContentTypes` | Optional nonempty array containing TEXT and/or HTML. |
| `SupportsAttachments` | Optional boolean; false rejects outbound attachment descriptors. |

HTML and ATTACH support depend on the selected service’s declared capabilities rather than a hard-coded EMAIL-only rule. Omitted capability declarations leave interpretation to the receiver; they do not prove provider support.

Additional drivers implement `CommDeliveryDriver` (`driverType`, `validateSettings`, `deliver`) and are discovered as Spring beans. Keep provider protocol details behind that boundary and credentials in the platform configuration mechanism.

### Preview configuration before saving or sending

The configuration-preview API provides four authoring operations alongside Context extraction/masking previews. They do not create notifications. See the [API reference](https://docs.insuremo.com/tech/pro/sdk/api/connecthub) for complete request and response details.

![Authoring previews validate content, driver settings and rule behavior without executing delivery](./image/comm_hub/authoring-previews.png)

*Figure 13. Inspect each editor's current values, then save and enable through the normal configuration workflow. Runtime delivery guards remain separate.*

All routes below are relative to `/comm/v1`:

| API | Input | Result and boundary |
| --- | --- | --- |
| `POST /templates/previewContent` | Current `Channel`, `LanguageId`, `ContentType`, `Subject`, `Body`, plus saved or inline Context. | Returns `Subject`, `BodyText`, `BodyHtml`, `LanguageId`, `Diagnostics`; displays masked values and sanitizes HTML. No saved Template is required. |
| `GET /delivery-services/driverTypes` | No body. | Returns sorted `DriverTypes` registered on the server. No settings schema or credentials are returned. |
| `POST /delivery-services/validateSettings` | `DriverType`, optional `Channel`, `Settings`. | Returns `{"Valid":true}` on success; invalid settings use the platform error response. Local validation only, without a provider connection test or existing-reference/uniqueness checks. |
| `POST /delivery-rules/preview` | Saved `DefinitionId`, current unsaved `Rule`, optional `SamplePayload`, `LanguageId`, `Recipients`. | Returns condition, masked recipient, template selection, effective policies and diagnostics. `RuntimeGuardsEvaluated` is always false. |

**Choose the appropriate template preview:** `renderPreview` selects stored active content; `renderPreviewById` renders a stored template selected by its required `templateId` query parameter. Use `previewContent` to inspect edits before saving. Its Context input has two mutually exclusive modes:

- **Saved Context:** `ContextDefinitionId` and optional `SamplePayload`; omission of SamplePayload uses the stored sample.
- **Inline Context:** `Context: {Payload, Contexts, Variables, SensitiveFields}`. Do not combine it with ContextDefinitionId or top-level SamplePayload.

Example using unsaved content and inline sample data:

```http
POST /comm/v1/templates/previewContent
Content-Type: application/json
```

```json
{
  "Channel": "EMAIL",
  "LanguageId": "en_US",
  "ContentType": "TEXT",
  "Subject": "Policy ${PolicyNo}",
  "Body": "Hello ${CustomerName}, policy ${PolicyNo} has been issued.",
  "Context": {
    "Payload": {
      "PolicyNo": "P-10001",
      "Customer": { "Name": "Ada" }
    },
    "Contexts": { "Customer": "$.Customer" },
    "Variables": {
      "PolicyNo": "Payload.PolicyNo",
      "CustomerName": "Context.Customer.Name"
    },
    "SensitiveFields": [
      { "Path": "Payload.Customer.Name", "Method": "FULL" }
    ]
  }
}
```

The preview validates requirements against the original sample, but renders display content using masked values: the name appears as `***`. Inputs are not persisted. It does not resolve a provider-managed template or send an email.

**Read Rule preview results as configuration evidence:**

- `ConditionValid` distinguishes invalid expressions or undeclared variables from a valid condition that simply does not match; inspect `ConditionMatched` and `Diagnostics` separately.
- `Recipient` shows the resolved identity/address and skip reason; destination addresses are masked. Explicit recipient IDs remain correlation identifiers.
- `Template` shows exact language selection and local availability. `Available=null` for PROVIDER_MANAGED means the provider was not queried.
- `EffectivePolicies` shows inherited/overridden preference, frequency, quiet-hour, strategy and retry settings with their sources.
- Live preferences, frequency counts, quiet-hour decisions, TTL, other rules and fallback scheduling are **not executed**. Preview success therefore does not guarantee send eligibility or delivery.

REQUEST-source previews accept the same recipient entry shape as send: `Role`, `Channel`, optional `RecipientId`, and `Address`. For both preview and runtime resolution, the effective preference policy governs the subject requirement: inherited BYPASS must not accidentally behave as RESPECT because of the old PreferenceExempt field.

---

## Design

### Shared configuration and per-request history

![Mutable configuration becomes a shared immutable snapshot referenced by separate notifications](./image/comm_hub/snapshot-boundary.png)

*Figure 14. Definition version, ConfigurationSnapshotId and per-request execution data describe different things.*

At acceptance, CommHub captures a coherent execution configuration and reuses a tenant-local snapshot when its normalized content is unchanged. A changed Context, Template, Service or policy can lead to a different snapshot even if the Definition version stays the same.

Notifications and TCC commands retain snapshot references. Delayed dispatch, fallback and retry restore accepted configuration. Recipients, rendered output, calculated deadlines and observed customer-policy outcomes remain request-specific. The snapshot freezes policy configuration; customer preferences and quota usage are checked live at physical dispatch. RequestId replay does not select today's resources again.

Snapshot inspection is read-only:

| Endpoint under `/comm/v1` | Purpose |
| --- | --- |
| `GET /configuration-snapshots/getById` | Inspect captured configuration by snapshotId. |
| `GET /configuration-snapshots/list` | Page snapshots scoped to a definitionId. |
| `GET /configuration-snapshots/notifications` | Page notifications referencing a snapshotId. |

Current snapshots are retained indefinitely; automatic snapshot reclamation is not part of this implementation. Their lifecycle is separate from hot notification-history cleanup.

### Payload privacy

Context `SensitiveFields` declares display masking using FULL, KEEP_EDGES, EMAIL, NAME or constrained REGEX methods. Notification history stores a structurally preserved masked Payload. Extraction and outbound rendering use the necessary original business values, so display masking does not corrupt the recipient address or delivered content.

Masking is declaration-based, not automatic discovery of sensitive information. Undeclared fields remain visible. Previews expose diagnostics and matching counts so authors can identify gaps. Shared configuration snapshots contain reusable configuration, not customer payloads or credentials; delivery execution data is protected separately.

### Global transactions with TCC

![TCC sequence with separate commit and rollback branches](./image/comm_hub/tcc-sequence.png)

*Figure 15. Try prepares durable command state; Confirm enables processing; Cancel prevents materialization.*

1. **Try:** capture the accepted SnapshotId/time and persist the prepared command/request identity. No provider call occurs.
2. **Confirm:** mark the command READY and wake processing. The worker claims the command, materializes using retained configuration, then dispatches after local graph commit.
3. **Cancel:** record cancellation through the fence. Empty rollback leaves a tombstone to guard a late Try.
4. **Recover:** worker claims and fences reject stale completion. If graph materialization committed before a crash, reprocessing obtains the original idempotency result.

TCC prevents provider effects before business commit. Continued progress still requires functioning command processing, tenant context, storage and downstream connectivity.

### Message lifecycle

![Ten message states, main transitions and separate completion semantics](./image/comm_hub/message-lifecycle.png)

*Figure 16. The main transitions show claims, retries and timeout triggers. The dashed uncertainty callout represents an attribute overlay; other endings and state-specific guards are explained below.*

| State or attribute | Interpretation |
| --- | --- |
| PLANNED / WAITING / SCHEDULED | Ready for dispatch / waiting for fallback turn / waiting for due time. |
| SUBMITTED | A physical attempt has been claimed and handed to the driver. |
| ACCEPTED | Durable receiver acceptance; pending final evidence or completed acceptance-only tracking. |
| DELIVERED | Confirmed delivery evidence. |
| FAILED | Inspect retry due time, retry-disabled flag and uncertainty; the label alone does not prove finality. |
| SUPPRESSED | Condition/customer-policy outcome, including remaining candidates after role completion. |
| CANCELLED | Eligible unsent work cancelled by an operator or chain control. |
| EXPIRED | Future dispatch is no longer permitted: unsent work, pending failed retries, or quiet-hour holds that reach expiry. Earlier attempt history remains available. |
| `DeliveryUncertain` | Submission/delivery cannot be established. Holds automatic retry and fallback until verified. |
| `TrackingCompleted` | Completion evidence, including an ACCEPTANCE_ONLY handoff that remains ACCEPTED. |

The state enum marks DELIVERED, SUPPRESSED, CANCELLED and EXPIRED as terminal. A definite FAILED outcome with no pending work can also be operationally complete. Dead-letter alerting is a separate event, not an eleventh message state.

### Runtime data relationships

![Notification, snapshot, messages, attempts, events, interactions and attachments](./image/comm_hub/runtime-data-model.png)

*Figure 17. A logical data model highlights ownership and cardinality without tying the guide to a changing table count.*

Runtime data separates the request acknowledgement, notification aggregate, message projection, physical attempts and append-only events. List APIs return summary projections; use detail APIs for payload, rendered content and attempt/result detail. Message.ActiveAttemptId identifies the current attempt; callbacks from earlier generations cannot overwrite current state. Interactions have their own handling lifecycle and do not rewrite delivery evidence.

The tenant is selected through platform datasource routing. Configuration belongs to tenant-local storage; the diagram does not imply cross-tenant snapshot sharing or physical foreign-key constraints. Schema changes are managed by incremental Liquibase migrations.

---

## Operations Guide

### Investigate and intervene

![Operational lanes for retries, uncertain outcomes, manual actions, feedback and archiving](./image/comm_hub/operations-workflow.png)

*Figure 18. Choose the next action from evidence and current server eligibility, not from a status label alone.*

Start with RequestId or the business reference. Inspect notification details, message state, accepted snapshot, current attempt and timeline. Read `GET /comm/v1/messages/actionEligibility` before offering an operator action; the server rechecks eligibility when the action executes.

| Situation | Next action |
| --- | --- |
| SCHEDULED | Inspect due time, SendAt, accepted quiet-hour policy and sweep health. |
| SUPPRESSED | Inspect the condition, preference or frequency-cap reason. A retry is not a substitute for changing business intent. |
| Definite retryable failure | Inspect next retry time, accepted budget, expiry and any operator stop. |
| Uncertain delivery | Verify downstream evidence. Submit an authorized manual receipt with `VerificationReason` and `EvidenceReference`; leave unresolved cases on hold. |
| Eligible manual retry | `POST /messages/retry?messageId=...` under `/comm/v1`, with `{"Reason":"..."}`. If dispatch guards pass, one explicit action adds one physical attempt using original accepted data. |
| Expired or materially changed content | The business caller submits a new RequestId and current Payload, optionally linked by OriginalNotificationId. |
| Reply or opt-out | Submit, route and close the interaction through the interaction APIs. Explicit opt-out updates channel preference; delivery receipt state remains separate. Provider failure/bounce codes do not change customer consent. |

A manual retry may exceed the automatic budget, but does not restart an automatic retry cycle. Dispatch-time preferences, quiet hours and quota still apply; a denied action may create no attempt. Expiry, uncertainty, operator stops, completed delivery/acceptance-only handoff and fallback-turn ownership remain guards. In-flight work cannot be treated as safely cancelled.

### Retry and receipt policy ownership

| Setting | Owner | Meaning |
| --- | --- | --- |
| `MaxAttempts` | Definition; optional whole Rule override | Includes the first automatic attempt. 1 disables automatic retries. |
| `RetryInitialDelaySeconds`, `RetryBackoffMultiplier`, `RetryMaxDelaySeconds` | Definition; optional whole Rule override | Exponential delay capped at the configured maximum. |
| `Settings.ConnectTimeoutMs`, `Settings.ReadTimeoutMs` | Delivery Service / driver | HTTP connection and response timing. |
| `ReceiptMode`, `ReceiptTimeoutSeconds` | Delivery Service | Whether final evidence is expected and how long to wait. |

See the [retry timeline](#retry-timing-and-attempt-identity) for backoff and attempt identity, and the [stage-by-stage checks](#what-each-stage-actually-rechecks) for the precise runtime guards. Retry policy, receipt mode/deadline settings and explicit service Settings are captured at acceptance. Omitted HTTP timeout Settings use the runtime global defaults. Operator decisions and physical delivery evidence remain runtime facts.

### Recovery switches and intervals

These are source defaults, not proof of an environment's deployed configuration. Recovery requires the appropriate jobs to be enabled and healthy.

| Property | Default | Purpose |
| --- | --- | --- |
| `comm-hub.enabled` | true | Enable the module. |
| `comm-hub.sweep.enabled` | false | Enable message recovery sweeps. |
| `comm-hub.sweep.retry-interval-ms` | 60000 | Find due retries. |
| `comm-hub.sweep.scheduled-interval-ms` | 60000 | Release scheduled messages. |
| `comm-hub.sweep.expiry-interval-ms` | 120000 | Retire expired unsent work and pending failed retries. |
| `comm-hub.sweep.stale-interval-ms` | 300000 | Recover stale dispatch work. |
| `comm-hub.sweep.receipt-timeout-interval-ms` | 60000 | Detect overdue final receipts. |
| `comm-hub.sweep.alert-interval-ms` | 120000 | Recover missed dead-letter publications. |
| `comm-hub.command-worker.enabled` | true | Enable durable TCC command processing/recovery. |
| `comm-hub.command-worker.interval-ms` | 5000 | Scan ready/due commands. |
| `comm-hub.command-worker.stale-interval-ms` | 60000 | Recover stale command claims. |
| `comm-hub.preference.default-allow` | false | Default preference result when no applicable preference allows delivery. |
| `comm-hub.template.max-rendered-length` | 65536 | Rendered-content limit. |

Message sweeps use tenant-scoped execution and idle backoff. Interval values describe polling configuration, not delivery SLAs. Monitor overdue work and worker errors alongside provider outcomes.

### Alerts and cache

Dead-letter conditions publish the `commhub:alert:DeadLetter` CloudEvent through EventBridge. Subscribe a rule to route events to monitoring or ticketing. Classify definite final failure separately from uncertain submission or missing final receipts. Alert recovery is at-least-once; downstream consumers should tolerate duplicate event delivery.

Definition data uses the `[CommHub]` cache bucket. Configuration mutations invalidate after commit, and DataIO imports participate in invalidation. Direct database edits bypass the normal path; the platform cache-clear endpoint is `GET /public/cache/v1/clear/bucket?bucketCode=[CommHub]`. Cache invalidation affects future resolution, not accepted immutable snapshots.

### Configuration publication and history retention

Configuration resources participate in the DataIO `commhub` publication module with GroupId/resource-lock governance. The DataIO module name and the OpenAPI group `connecthub` serve different purposes. Runtime history and immutable snapshots are not configuration editing surfaces.

Batch V2 housekeeping processes eligible completed notification aggregates. Configure retention explicitly with the job's `days` parameter or BusinessConfig key `comm.hub.housekeeping.retention-days`; `-1` disables it. The job defaults to dry-run. Optional `archiveBeforeDelete` controls backup before deletion, with bounded pages and aggregate limits.

Active attempts, pending retries and uncertain outcomes must remain outside ordinary completed-history cleanup. Request idempotency and shared configuration snapshots have separate retention concerns; deleting hot history is not permission to reclaim every associated identifier or snapshot. Confirm the deployed retention and backup settings before scheduling housekeeping.

---

## Diagram sources and contract reference

All eighteen diagrams have editable SVG counterparts beside their PNG files in `image/comm_hub/`. The [diagram source index](./image/comm_hub/README.md) links each source and records the visual conventions.

This guide was aligned with the CommHub source contract on **2026-09-16**, through revision `25acbcd4c5`: platform JSON names, RecipientId, dynamic channels and receiver capabilities, dispatch policy rechecks, logical-message quota, expiry-aware retries and uncertain HTTP 5xx handling.

For API details, see the [Connect Hub API Reference](https://docs.insuremo.com/tech/pro/sdk/api/connecthub). The deployed `connecthub` OpenAPI describes the exact schemas and operations available in each environment.
