Skip to content

Architecture Overview

Heka is a single-process FastAPI server that acts as the bridge between WhatsApp Cloud API and an AI conversation agent. It persists all state in a local SQLite database (WAL mode) on a Fly.io volume.

Request flow

Meta Cloud API                    Heka Server                     External
─────────────                    ───────────                     ────────
                                 ┌──────────────────────┐
  Webhook POST ──────────────────► meta_webhook.py      │
  (inbound msg)                  │  ├─ parse payload     │
                                 │  ├─ save MessageRecord │
                                 │  ├─ check ownership   │──── human-owned? ──► skip agent
                                 │  ├─ run agent         │
                                 │  │   ├─ load context  │
                                 │  │   ├─ Claude API ───│──────────► Anthropic
                                 │  │   └─ extract data  │
                                 │  ├─ advance pipeline  │
                                 │  └─ send response ────│──────────► Meta Graph API
                                 │                       │
  Status callback ───────────────► meta_status.py        │
  (sent/delivered/read)          │  └─ save StatusRecord  │
                                 │                       │
                                 │  campaign/router.py   │
  Operator Console ──────────────► ├─ send-template     │──────────► Meta Graph API
  (REST API calls)               │  ├─ send-template/batch│
                                 │  ├─ campaign CRUD    │
                                 │  └─ funnel management│
                                 └──────────────────────┘

Key modules

Channels (heka.channels)

Abstraction over the WhatsApp Cloud API. The ProviderSender Protocol defines the send interface; MetaSender implements it against the Graph API; FakeMetaSender records calls for testing.

The sender supports: text messages, media messages, template messages (with TEXT, IMAGE, VIDEO, and LOCATION headers), and interactive buttons.

Campaigns (heka.campaigns)

Campaign lifecycle management. A campaign definition (campaigns table) has a pipeline of phases. Contacts move through phases based on conversation signals extracted by the AI agent. The CampaignAgent orchestrates the AI response loop per conversation.

Key subsystems:

  • Pipeline — state machine for contact phase transitions with gate validation
  • Extraction — Claude-powered structured data extraction from conversations
  • Inference — Claude API integration for generating agent responses
  • Scheduler — background drip message scheduling and idle timeout handling

Persistence (heka.persistence)

SQLite repository with schema auto-migration. Tables include:

Table Purpose
messages All inbound/outbound messages with wamid
message_statuses Delivery status callbacks (sent/delivered/read/failed)
contacts Contact profiles with wa_id
contact_events Append-only event log per contact
contact_profile Structured profile data (name, email, etc.)
campaigns Campaign definitions with pipeline_json
campaign_sends Campaign execution records
campaign_contact_phase Contact position in campaign funnel
ownership_events Human/agent ownership transitions
conversation_summaries AI-generated conversation summaries
segments Contact segmentation definitions
templates WhatsApp template metadata cache
tenant_context Tenant-specific agent configuration

Conversations (heka.conversations)

Message send/receive endpoints, conversation timeline, and ownership management. The ConversationGuard enforces ownership rules: when a contact is human-owned, the AI agent does not respond.

Handoff (heka.handoff)

Protocol for transferring conversations between AI agent and human operators. Supports accept, release, and automatic idle timeout (30 min with no operator activity).

Data flow invariants

  1. Every inbound message creates a MessageRecord before any processing
  2. Every outbound message creates a MessageRecord with wamid for delivery tracking
  3. Status webhooks correlate to messages via wamid — no orphan statuses
  4. Contact phase transitions are gated by the campaign pipeline state machine
  5. Human ownership blocks AI responses until explicitly released
  6. contact_events is append-only — updates and deletes trigger errors