Channel Provisioning Protocol β
Domain: Communications (Shared Core Domain) Trigger: Operator-initiated channel registration (post-provisioning) Output: Configured
channel_accountswith active CPaaS provider connections Sources: communications.md, ADR-003L3 DoD: β Schema | β API | β Edge States β Ready to Code
Β§1 Overview β
Channel provisioning is an explicit post-provisioning operator action β ProvisionTenant (ADR-003) seeds notification_templates but does NOT create channel_accounts. Channel registration requires operator action (Meta Business Verification, SES domain verification) that the system cannot automate at signup.
The Workspace UI shows a "Complete your communications setup" onboarding checklist. Seeded notification_templates reference channels that do not yet exist β the notification pipeline defers template activation until the matching ChannelAccount reaches ACTIVE.
Β§2 ChannelAccount Lifecycle State Machine β
| From | To | Trigger | Side Effects |
|---|---|---|---|
| β | PENDING_VERIFICATION | registerChannel Hasura Action | Creates channel_accounts + operator_integrations (PENDING). Initiates provider verification. |
PENDING_VERIFICATION | ACTIVE | Provider verification completes | operator_integrations.status β CONNECTED. Pipeline can dispatch. change_event (CONFIG). |
PENDING_VERIFICATION | SUSPENDED | Verification failed/timed out | operator_integrations.status β FAILED + error_message. |
ACTIVE | SUSPENDED | Manager action or provider revocation detected | operator_integrations.status β DISCONNECTED. Pipeline skips channel β fallback chain. |
SUSPENDED | ACTIVE | Re-verification or manager reactivates | operator_integrations.status β CONNECTED. |
ACTIVE | REVOKED | Provider permanently bans account | Terminal. operator_integrations.status β DISCONNECTED. Must register new account. |
Suspension detection:
- WhatsApp:
channel_health_checkHasura Cron Trigger (daily, 03:00 UTC) polls Meta WABA status. - Email: SES
accountSendingPausedSNS notification β auto-suspend.
Β§3 WhatsApp Registration Flow (Meta Embedded Signup) β
Phase 1 uses Meta's Embedded Signup β a JavaScript SDK handling Facebook Login, business verification, and WABA creation in an iframe.
Steps:
- Operator opens Workspace Settings β Communications β Add Channel β WhatsApp.
- Meta Embedded Signup launches (
FB.login()withwhatsapp_business_managementpermissions). - Operator completes flow β Meta returns
waba_id,phone_number_id, short-lived token. - Frontend calls
registerChannelwith the credentials. - Handler: exchange token β generate
webhook_verify_tokenβ subscribe to Meta webhooks viaPOST /<WABA_ID>/subscribed_apps(WABA-level override, not app-level/{app_id}/subscriptions) β encrypt secrets β INSERT both tables β poll Meta Business Verification status.
NOTE
Busflow Meta App: All tenants share one Busflow-owned Meta App (BSP model). app_id and app_secret are platform-level constants stored in provider_config for worker convenience.
WARNING
Blocking prerequisite: The Busflow Meta App must pass Meta App Review for whatsapp_business_management and whatsapp_business_messaging before any tenant can provision WhatsApp.
Β§4 Email Registration Flow (SES Domain Verification) β
Operator adds DNS records (DKIM, SPF/DMARC) to prove domain ownership.
Steps:
- Operator opens Add Channel β Email, enters domain (e.g.,
reisen-mueller.de). - Frontend calls
registerChannelwith domain and sender email. - Handler: create SES domain identity β enable DKIM β create Configuration Set β create SNS topics + subscription β return DNS records.
- Operator adds DNS records at domain registrar.
ses_verification_checkCron Trigger (every 15 min) polls SES verification status.- On success β
ACTIVE. On 30-day timeout βSUSPENDED.
DNS Record Display UI: Panel with copy-to-clipboard buttons, per-record status indicators (β³/β /β), and "Verify Now" button.
NOTE
SES Sandbox: Busflow's AWS account must promote out of SES Sandbox before any tenant sends production emails.
Β§5 SMS Registration (Platform-Managed) β
Phase 1: Platform-managed, not operator-provisioned. DACH Sender ID registration requires per-country carrier pre-registration β unsuitable for self-service.
- Busflow registers a platform-wide Sender ID ("Busflow") with DACH carriers via SNS.
- All tenants share this Sender ID.
registerChannelfor SMS creates the row withstatus = ACTIVEimmediately.provider_configstores platform-level SNS config.
Phase 2: operators can register their own Sender ID (1β3 week carrier approval).
Β§6 Hasura Action Contracts β
registerChannel β
| Property | Type | Required | Description |
|---|---|---|---|
| Input | |||
channel_type | String | β | WHATSAPP, EMAIL, SMS |
display_name | String | β | Operator-editable label |
sender_identity | String | β | Canonical sender identifier |
whatsapp_config | Object | β | Required if WHATSAPP (Β§3) |
email_config | Object | β | Required if EMAIL (Β§4) |
| Output | |||
channel_account_id | UUID | β | Created ChannelAccount ID |
status | String | β | PENDING_VERIFICATION or ACTIVE |
dns_records | Object[] | β | EMAIL only: DNS records to add |
| Errors | |||
CHANNEL_ALREADY_EXISTS | Tenant has existing account for this channel type | ||
PROVIDER_ERROR | Meta/SES API call failed | ||
VALIDATION_ERROR | Invalid input |
Authorization: MANAGER role only.
updateChannelConfig β
Updates operator-editable fields. Does NOT allow changing channel_type or secrets (re-registration required).
| Property | Type | Required | Description |
|---|---|---|---|
channel_account_id | UUID | β | Target account |
display_name | String | β | Updated label |
sender_identity | String | β | Updated sender (re-verification for Email) |
reply_to_email | String | β | Email only |
Authorization: MANAGER role only.
testChannel β
Sends a test message to verify end-to-end connectivity.
| Property | Type | Required | Description |
|---|---|---|---|
channel_account_id | UUID | β | Channel to test |
test_recipient | String | β | Phone (E.164) or email |
Authorization: MANAGER or DISPATCHER.
suspendChannel / reactivateChannel β
| Property | Type | Required | Description |
|---|---|---|---|
channel_account_id | UUID | β | Target account |
reason | String | β | Logged in change_events |
Side effects (suspend): Pipeline skips channel. Queued messages β FAILED with failed_reason = 'channel_suspended'. Fallback chain activates.
Authorization: MANAGER role only.
Β§7 Meta Webhook Setup & Verification β
NOTE
WABA-level override: This section covers webhook setup via POST /<WABA_ID>/subscribed_apps. The per-tenant callback URL (/api/webhooks/meta/{tenant_id}) receives both inbound messages and delivery status callbacks. See notification-pipeline-protocol.md Β§8 for the delivery webhook handler spec.
During registration (automatic):
- Handler calls
POST /<WABA_ID>/subscribed_appswith tenant-specificoverride_callback_urlandverify_token. This is a WABA-level override β it does NOT overwrite the app-level subscription. Each tenant's WABA independently routes to its own callback URL. - Meta sends
GET /api/webhooks/meta/{tenant_id}?hub.mode=subscribe&hub.verify_token={token}&hub.challenge={challenge}. - Controller: extract
tenant_idβ loadchannel_accountsβ comparewebhook_verify_tokenβ respond withhub.challengeor HTTP 403.
Runtime signature verification (every delivery):
- Meta sends
POST /api/webhooks/meta/{tenant_id}withX-Hub-Signature-256. - Handler computes
HMAC-SHA256(app_secret, body)β compare. - Match β process. Mismatch β HTTP 401, log security alert.
IMPORTANT
Shared Meta App Secret: All tenants share one app_secret. Tenant isolation relies on WABA-level scoping (each WABA receives webhooks for its own phone number only).
Β§8 operator_integrations β channel_accounts Sync Contract β
Two tables, two schemas, one business concept. Writes happen atomically within a single transaction.
| Event | channel_accounts | operator_integrations |
|---|---|---|
| Registered | INSERT PENDING_VERIFICATION | INSERT PENDING |
| Verification succeeds | β ACTIVE | β CONNECTED |
| Verification fails | β SUSPENDED | β FAILED + error_message |
| Manager suspends | β SUSPENDED | β DISCONNECTED |
| Manager reactivates | β ACTIVE | β CONNECTED |
| Provider revokes | β REVOKED | β DISCONNECTED |
| Manager deletes | DELETE | β DISCONNECTED |
NOTE
Cross-schema write justification: Controlled exception to "contexts communicate via events." Channel provisioning is an administrative configuration action (infrequent, operator-initiated) that must be atomic. Async sync would create consistency gaps in the Workspace UI.
Β§9 operator_integrations β CPaaS Types β
integration_type | Description | Provisioned By |
|---|---|---|
META_WHATSAPP | WhatsApp via Meta Cloud API | registerChannel (operator) |
AWS_SES | Email via Amazon SES | registerChannel (operator) |
AWS_SNS | SMS via Amazon SNS | registerChannel (platform-managed Phase 1) |
Config shapes live in schema-backoffice.md Β§operator_integrations.