Busflow Docs

Internal documentation portal

Skip to content

Channel Provisioning Protocol ​

Domain: Communications (Shared Core Domain) Trigger: Operator-initiated channel registration (post-provisioning) Output: Configured channel_accounts with 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 ​

mermaid
stateDiagram-v2
    [*] --> PENDING_VERIFICATION : registerChannel
    PENDING_VERIFICATION --> ACTIVE : Provider verification completes
    PENDING_VERIFICATION --> SUSPENDED : Verification failed / timed out (30d)
    ACTIVE --> SUSPENDED : Manager action or provider revocation
    ACTIVE --> REVOKED : Provider permanently bans account
    SUSPENDED --> ACTIVE : Re-verification or manager reactivation
    REVOKED --> [*] : Terminal β€” must register new account
FromToTriggerSide Effects
β€”PENDING_VERIFICATIONregisterChannel Hasura ActionCreates channel_accounts + operator_integrations (PENDING). Initiates provider verification.
PENDING_VERIFICATIONACTIVEProvider verification completesoperator_integrations.status β†’ CONNECTED. Pipeline can dispatch. change_event (CONFIG).
PENDING_VERIFICATIONSUSPENDEDVerification failed/timed outoperator_integrations.status β†’ FAILED + error_message.
ACTIVESUSPENDEDManager action or provider revocation detectedoperator_integrations.status β†’ DISCONNECTED. Pipeline skips channel β†’ fallback chain.
SUSPENDEDACTIVERe-verification or manager reactivatesoperator_integrations.status β†’ CONNECTED.
ACTIVEREVOKEDProvider permanently bans accountTerminal. operator_integrations.status β†’ DISCONNECTED. Must register new account.

Suspension detection:

  • WhatsApp: channel_health_check Hasura Cron Trigger (daily, 03:00 UTC) polls Meta WABA status.
  • Email: SES accountSendingPaused SNS 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.

mermaid
sequenceDiagram
    participant UI as Workspace UI
    participant Meta as Meta Embedded Signup
    participant API as NestJS registerChannel
    participant DB as PostgreSQL

    UI->>Meta: FB.login() (iframe)
    Meta-->>UI: waba_id, phone_number_id, short_lived_token
    UI->>API: registerChannel(WHATSAPP, credentials)
    API->>Meta: Exchange short-lived β†’ long-lived token
    API->>Meta: POST /{waba_id}/subscribed_apps (per-WABA webhook override)
    Meta->>API: GET /webhooks/meta/{tenant_id}?hub.verify_token=...
    API-->>Meta: hub.challenge (200 OK)
    API->>DB: INSERT channel_accounts (PENDING_VERIFICATION)
    API->>DB: INSERT operator_integrations (PENDING)
    API-->>UI: { channel_account_id, status }

Steps:

  1. Operator opens Workspace Settings β†’ Communications β†’ Add Channel β†’ WhatsApp.
  2. Meta Embedded Signup launches (FB.login() with whatsapp_business_management permissions).
  3. Operator completes flow β†’ Meta returns waba_id, phone_number_id, short-lived token.
  4. Frontend calls registerChannel with the credentials.
  5. Handler: exchange token β†’ generate webhook_verify_token β†’ subscribe to Meta webhooks via POST /<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:

  1. Operator opens Add Channel β†’ Email, enters domain (e.g., reisen-mueller.de).
  2. Frontend calls registerChannel with domain and sender email.
  3. Handler: create SES domain identity β†’ enable DKIM β†’ create Configuration Set β†’ create SNS topics + subscription β†’ return DNS records.
  4. Operator adds DNS records at domain registrar.
  5. ses_verification_check Cron Trigger (every 15 min) polls SES verification status.
  6. 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.
  • registerChannel for SMS creates the row with status = ACTIVE immediately.
  • provider_config stores platform-level SNS config.

Phase 2: operators can register their own Sender ID (1–3 week carrier approval).


Β§6 Hasura Action Contracts ​

registerChannel ​

PropertyTypeRequiredDescription
Input
channel_typeStringβœ…WHATSAPP, EMAIL, SMS
display_nameStringβœ…Operator-editable label
sender_identityStringβœ…Canonical sender identifier
whatsapp_configObject❌Required if WHATSAPP (§3)
email_configObject❌Required if EMAIL (§4)
Output
channel_account_idUUIDβœ…Created ChannelAccount ID
statusStringβœ…PENDING_VERIFICATION or ACTIVE
dns_recordsObject[]❌EMAIL only: DNS records to add
Errors
CHANNEL_ALREADY_EXISTSTenant has existing account for this channel type
PROVIDER_ERRORMeta/SES API call failed
VALIDATION_ERRORInvalid input

Authorization: MANAGER role only.

updateChannelConfig ​

Updates operator-editable fields. Does NOT allow changing channel_type or secrets (re-registration required).

PropertyTypeRequiredDescription
channel_account_idUUIDβœ…Target account
display_nameString❌Updated label
sender_identityString❌Updated sender (re-verification for Email)
reply_to_emailString❌Email only

Authorization: MANAGER role only.

testChannel ​

Sends a test message to verify end-to-end connectivity.

PropertyTypeRequiredDescription
channel_account_idUUIDβœ…Channel to test
test_recipientStringβœ…Phone (E.164) or email

Authorization: MANAGER or DISPATCHER.

suspendChannel / reactivateChannel ​

PropertyTypeRequiredDescription
channel_account_idUUIDβœ…Target account
reasonStringβœ…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):

  1. Handler calls POST /<WABA_ID>/subscribed_apps with tenant-specific override_callback_url and verify_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.
  2. Meta sends GET /api/webhooks/meta/{tenant_id}?hub.mode=subscribe&hub.verify_token={token}&hub.challenge={challenge}.
  3. Controller: extract tenant_id β†’ load channel_accounts β†’ compare webhook_verify_token β†’ respond with hub.challenge or HTTP 403.

Runtime signature verification (every delivery):

  1. Meta sends POST /api/webhooks/meta/{tenant_id} with X-Hub-Signature-256.
  2. Handler computes HMAC-SHA256(app_secret, body) β†’ compare.
  3. 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.

Eventchannel_accountsoperator_integrations
RegisteredINSERT PENDING_VERIFICATIONINSERT PENDING
Verification succeeds→ ACTIVE→ CONNECTED
Verification fails→ SUSPENDED→ FAILED + error_message
Manager suspends→ SUSPENDED→ DISCONNECTED
Manager reactivates→ ACTIVE→ CONNECTED
Provider revokes→ REVOKED→ DISCONNECTED
Manager deletesDELETE→ 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_typeDescriptionProvisioned By
META_WHATSAPPWhatsApp via Meta Cloud APIregisterChannel (operator)
AWS_SESEmail via Amazon SESregisterChannel (operator)
AWS_SNSSMS via Amazon SNSregisterChannel (platform-managed Phase 1)

Config shapes live in schema-backoffice.md Β§operator_integrations.

Internal documentation β€” Busflow