Business Communication Configuration Guide
Overviewβ
The Business Communication Configuration Matrix controls how each business sends communications across all supported channels. Every combination of (business Γ communication type Γ channel) has its own configuration row that governs:
- Whether that channel is enabled for that type
- Whether messages are sent automatically or require manual dispatch
- Template overrides
- Time-window restrictions (e.g. business hours only)
- Rate limits and minimum send intervals
- Custom JSON conditions (e.g. minimum transaction amount)
- Admin notes
Key Conceptsβ
Configuration Matrixβ
The matrix is a 3-dimensional grid:
| Dimension | Values |
|---|---|
| Business | UUID |
| Communication Type | invoice, sale, payment_confirmation, payment_link, collection_notice, low_stock_alert, general_notification, invitation, user_invitation, daily_report, emergency_alert, order_confirmation, refund_confirmation, reminder, materialConsumption, creditNote, debitNote, felCancellation, cashRegisterSession, quote |
| Channel | email, sms, whatsapp, messenger, pdf |
Configuration Fieldsβ
| Field | Type | Description |
|---|---|---|
isEnabled | boolean | Whether this channel is active for this type |
isAutomatic | boolean | Auto-send on trigger vs. manual dispatch |
templateId | UUID? | Template override (uses default when null) |
sendDelayMinutes | int? | Minutes to wait before sending (0β1440) |
sendTimeWindowStart | string? | Window start in HH:mm or HH:mm:ss format (validated by regex) |
sendTimeWindowEnd | string? | Window end in HH:mm or HH:mm:ss format (validated by regex) |
timezone | string? | IANA timezone string β stored for reference; server-side timezone enforcement is not yet implemented |
maxSendsPerRecipientPerDay | int? | Daily rate limit per recipient (1β100) β not yet enforced (requires communication history table) |
minIntervalMinutes | int? | Minimum minutes between sends to same recipient β not yet enforced |
conditions | jsonb? | Custom rule object (see Conditions section) |
notes | string? | Admin notes |
Authentication & User Identityβ
All mutation endpoints (POST, PUT, DELETE) derive createdBy, updatedBy, and deletedBy from the authenticated Firebase user via the @UserId() decorator. These fields are not accepted as client input β they are set server-side from the JWT token.
canSend() Fail-Open Strategyβ
The canSend() method is designed fail-open: if no configuration is found, or if an error occurs during evaluation, it returns allowed: true. This ensures that missing configuration never silently blocks a communication.
Rate Limiting & Interval Checks β Not Yet Activeβ
The maxSendsPerRecipientPerDay and minIntervalMinutes fields are stored and returned in the API, but the canSend() enforcement for these two rules requires querying a communication-history table that is not yet implemented. The fields currently have no runtime effect. Tracked as a TODO in the service.
Time Window Enforcementβ
sendTimeWindowStart / sendTimeWindowEnd are enforced in canSend() using server UTC time. The timezone field is stored but not yet applied to the comparison. Overnight windows are supported (e.g. 22:00β06:00).
Custom Conditionsβ
conditions is a free-form JSON object. Currently supported keys:
| Key | Type | Behavior |
|---|---|---|
min_amount | number | Block send if context.amount < min_amount |
max_amount | number | Block send if context.amount > max_amount |
Unknown keys are ignored. Fail-open on parse errors.
Default Initializationβ
Calling POST /business-communication-config/business/:businessId/initialize upserts defaults for all types Γ channels:
| Channel | Default isEnabled | Default isAutomatic |
|---|---|---|
email | true | true for invoice, payment_confirmation, invitation, user_invitation; false for all others |
sms | false | false |
whatsapp | false | false |
messenger | false | false |
pdf | false | false |
Default time window: 08:00β20:00 UTC. Default rate limit: 5/day, 60 min interval.
The endpoint is idempotent β safe to call multiple times.
API Endpointsβ
Base path: /business-communication-config
| Method | Path | Description |
|---|---|---|
GET | /business/:businessId | List all configs for a business |
GET | /business/:businessId/type/:type | Get enabled channels for a type β { channels: string[] } |
GET | /business/:businessId/type/:type/channel/:channel | Get specific config (404 if not found) |
GET | /business/:businessId/type/:type/channel/:channel/can-send | Validate send permission |
POST | / | Create a single config |
PUT | /:id | Partial update a config (immutable: businessId, communicationType, channel) |
DELETE | /:id | Delete a config |
POST | /business/:businessId/initialize | Initialize defaults (idempotent) |
POST | /bulk-update | Upsert multiple configs at once |
All endpoints require Authorization: Bearer <firebase-id-token>. User identity for audit trail is derived from the token automatically.
can-send query paramsβ
| Param | Required | Description |
|---|---|---|
recipientId | No | Enables rate-limit and interval checks (currently stubbed) |
amount | No | Used by min_amount / max_amount conditions |
Update constraintsβ
The PUT /:id endpoint does not allow changing businessId, communicationType, or channel β these are immutable identity fields. To change them, delete and re-create the configuration.
Example Requestsβ
Create a configβ
POST /business-communication-config
Authorization: Bearer <firebase-id-token>
{
"businessId": "550e8400-e29b-41d4-a716-446655440000",
"communicationType": "invoice",
"channel": "email",
"isEnabled": true,
"isAutomatic": true,
"sendTimeWindowStart": "08:00",
"sendTimeWindowEnd": "20:00",
"timezone": "America/Guatemala",
"maxSendsPerRecipientPerDay": 3,
"minIntervalMinutes": 60,
"notes": "Auto-send invoices via email during business hours"
}
Bulk update (typical UI save)β
POST /business-communication-config/bulk-update
Authorization: Bearer <firebase-id-token>
{
"businessId": "550e8400-e29b-41d4-a716-446655440000",
"configs": [
{ "communicationType": "invoice", "channel": "email", "isEnabled": true, "isAutomatic": true },
{ "communicationType": "invoice", "channel": "whatsapp", "isEnabled": true, "isAutomatic": false },
{ "communicationType": "payment_confirmation", "channel": "sms", "isEnabled": false, "isAutomatic": false }
]
}
Check can-send with amount conditionβ
GET /business-communication-config/business/{id}/type/invoice/channel/email/can-send?amount=150
Response:
{ "allowed": true, "reason": "All configuration checks passed" }
Audit Trailβ
Every createConfig, updateConfig, deleteConfig, and bulkUpdate operation is recorded in business_communication_config_audit with:
configId,businessId,communicationType,channelaction:created|updated|deletedpreviousValue/newValue: full config snapshotschangedBy: user UUID (derived from authenticated Firebase token)reason: free-text change reason (supported inupdateConfigvia thereasonDTO field)
Architecture Notesβ
The module follows strict Hexagonal Architecture:
domain/ β IBusinessCommunicationConfigRepository (port), BusinessCommunicationConfig types, CanSendResult/SendTimeWindow value types
application/ β BusinessCommunicationConfigService (use cases)
infrastructure/ β BusinessCommunicationConfigRepository (Kysely adapter)
interfaces/ β BusinessCommunicationConfigController (HTTP), request DTOs, response DTOs
The service is exported from the module so other modules (e.g. the communications dispatcher) can inject it directly to call canSend(), shouldAutoSend(), getSendDelay(), and getTemplateOverride().
Known Limitations & TODOsβ
- Rate limiting β
maxSendsPerRecipientPerDaystored but not enforced. Requires acommunicationhistory table query. - Min interval β same as above.
- Timezone-aware time windows β
timezonefield stored but server compares against local server time. Needsluxonordate-fns-tzintegration.