Skip to main content

What are Campaigns?

Campaigns are automated messaging workflows that send personalized messages to leads through Telegram. Each campaign processes leads individually (lead-by-lead), executing a flow of nodes that can send messages, wait for delays, evaluate conditions, and respond to user interactions.

Campaign Architecture

Core Components

Campaign Flow (campaign_flows table):
  • Defines the visual workflow (nodes and edges)
  • Stores campaign metadata (name, description, status, timezone)
  • Contains A/B test configuration and variations
  • Links to Telegram account for sending messages
Flow Execution (flow_executions table):
  • One execution per lead in the campaign
  • Tracks individual lead progress through the flow
  • Stores execution context (variables, node states)
  • Maintains status: pending, running, waiting, completed, responded, failed, paused, cancelled
Execution Logs (flow_execution_logs table):
  • Audit trail of every node execution
  • Records input/output data, status, timestamps
  • Used for statistics and debugging
Messages (messages table):
  • Stores all sent and received messages
  • Links to execution and campaign for tracking
  • Tracks delivery status and A/B test variation

Processing Model

Campaigns use a worker-based architecture:
  1. UnifiedWorker runs continuously, polling for pending executions
  2. Each execution is processed independently (lead-by-lead)
  3. Workers use Redis locks to prevent concurrent processing of the same execution
  4. Rate limiting is enforced per Telegram account to prevent FloodWaitError
  5. Response detection runs automatically, checking for replies every 60 seconds

Campaign Statuses

Draft

  • Campaign created but not launched
  • Can be edited freely
  • No executions created yet

Scheduled

  • Campaign set to start at a specific date/time
  • Timezone-aware: scheduled time is stored in UTC but displayed in campaign’s timezone
  • Automatically transitions to active when scheduled time is reached

Active

  • Campaign is running and processing executions
  • Workers pick up pending executions and process them
  • Cannot edit flow configuration (must pause first)
  • New leads can be added dynamically

Paused

  • Campaign temporarily stopped
  • All active executions are paused
  • Can be resumed (executions continue from where they stopped)
  • Flow configuration can be edited

Completed

  • All executions have finished (completed, responded, or failed)
  • Campaign automatically transitions to completed when no active executions remain
  • Can be reactivated by adding new leads (status changes back to active)

Cancelled

  • Campaign was stopped manually
  • All pending executions are cancelled
  • Cannot be resumed (must duplicate to restart)

Permissions

Organization Context

Campaigns respect organization isolation: Personal Mode (no organization):
  • Users can only see/edit campaigns with organization_id = NULL
  • Complete isolation from organization data
Organization Mode:
  • Campaigns belong to organization (organization_id set)
  • Access controlled by role and admin_view_enabled setting

Role-Based Permissions

Owner:
  • Can view all organization campaigns (if admin_view_enabled = true)
  • Can edit any organization campaign (regardless of status)
  • Can delete any organization campaign
  • Full control over campaign lifecycle
Admin:
  • Can view all organization campaigns (if admin_view_enabled = true)
  • Can edit any organization campaign (regardless of status)
  • Can delete any organization campaign
  • Cannot change organization owner
Member:
  • Can only view own campaigns (even if admin_view_enabled = true)
  • Can edit own campaigns (if status allows)
  • Can delete own campaigns
  • Cannot view/edit other members’ campaigns

Status-Based Edit Restrictions

Even with proper permissions, editing is restricted by status: Can Edit Flow Configuration:
  • draft - Always editable
  • paused - Editable
  • scheduled - Editable (before start time)
Cannot Edit Flow Configuration:
  • active - Must pause first (prevents mid-execution changes)
  • completed - Cannot edit (historical record)
  • cancelled - Cannot edit (historical record)
Note: Basic metadata (name, description, Telegram account) can be updated even when flow is locked.

Campaign Processing

Execution Flow

  1. Campaign Started:
    • Status changes to active
    • Executions created for each lead (status: pending)
    • next_execution_at set to now() for immediate processing
  2. Worker Picks Up Execution:
    • UnifiedWorker queries for executions where next_execution_at <= now()
    • Acquires Redis lock to prevent concurrent processing
    • Loads campaign flow, contact, and execution context
  3. Node Execution:
    • Processes current node based on type:
      • START: Initializes execution, moves to first node
      • SEND_MESSAGE: Sends message with rate limiting, tag replacement, spintax
      • WAIT: Calculates next execution time, sets status to waiting
      • CONDITION: Evaluates condition, branches to appropriate path
      • END: Marks execution as completed
  4. After Node Execution:
    • Updates execution context (variables, current_node_id)
    • Creates execution log entry (audit trail)
    • Calculates next node and next_execution_at
    • Releases Redis lock
  5. Execution Completion:
    • When execution reaches END node or fails
    • Status changes to completed, responded, or failed
    • Campaign checks if all executions are done → auto-completes campaign

Rate Limiting

Rate limiting prevents Telegram FloodWaitError: Per-Account Limits:
  • Cooldown between messages: 30-60 seconds (with 20-40% jitter)
  • Daily message limits: Configurable per account profile
  • Hourly message limits: Configurable per account profile
Intelligent Cooldown:
  • Workers check rate limit before sending
  • If cooldown active, execution waits and retries later
  • next_execution_at adjusted to respect cooldown
Account Profiles:
  • conservative: Lower limits, safer for new accounts
  • moderate: Balanced limits
  • aggressive: Higher limits (use with caution)

Timezone Handling

Campaigns are timezone-aware: Scheduled Campaigns:
  • scheduled_start_at stored in UTC
  • timezone field stores campaign’s timezone (e.g., “America/Sao_Paulo”)
  • Worker converts UTC to campaign timezone for comparison
  • Ensures campaigns start at correct local time
Wait Nodes:
  • Duration-based waits are timezone-agnostic (relative)
  • Response-based waits use UTC timestamps

Response Detection

Automatic response detection stops sending when lead replies: Detection Process:
  1. UnifiedWorker checks for responses every 60 seconds
  2. Queries Telegram API for messages after last sent message timestamp
  3. If response found:
    • Execution status → responded
    • response_detected_at timestamp recorded
    • response_text stored (first 500 chars)
    • Last sent message marked as delivered (response = proof of delivery)
    • No further messages sent to this lead
Response Timeout:
  • Configurable per campaign (response_timeout_hours)
  • If no response after timeout, execution continues
  • Default: No timeout (waits indefinitely)
Pause on Response:
  • Campaign setting: pause_on_response
  • If enabled, execution stops immediately when response detected
  • If disabled, execution completes current node before stopping

Flow Builder

Node Types

START Node:
  • Entry point of campaign
  • Defines start type: immediate or scheduled
  • For scheduled: requires scheduled_start_at and timezone
SEND_MESSAGE Node:
  • Sends text message (with optional media)
  • Processes spintax and dynamic tags
  • Enforces rate limiting
  • Creates message record and execution log
WAIT Node:
  • Duration-based: waits specified time (days/hours/minutes)
  • Response-based: waits for lead response with timeout
  • Sets next_execution_at for future processing
CONDITION Node:
  • Evaluates condition based on execution context
  • Branches to different paths based on result (yes/no)
  • Uses edges with condition field to route flow
END Node:
  • Marks execution as completed
  • Campaign checks if all executions done → auto-completes

Flow Configuration

Nodes Array:
  • Ordered list of all nodes in flow
  • Each node has: id, type, data, position
Edges Array:
  • Defines connections between nodes
  • Sequential edges: connect nodes in order
  • Conditional edges: connect condition nodes to branches (condition: "yes" or "no")
Processing Logic:
  • Nodes processed sequentially by array order
  • Edges used only for condition branching
  • If no edges, nodes processed in array order

Dynamic Tags

Tag Replacement

Tags are replaced with actual values before sending: Basic Tags:
  • {firstName} → Contact’s first name
  • {lastName} → Contact’s last name
  • {username} → Telegram username (without @)
  • {phoneNumber} → Phone number
  • {groupName} → Group name (if contact from group)
Custom Field Tags:
  • {cf-fieldName} → Custom field value
  • Example: {cf-companyName} → “Acme Corp”
Fallback Values:
  • Format: {tag|fallback}
  • If tag value missing, uses fallback
  • Example: {firstName|Guest} → “John” or “Guest”
Processing Order:
  1. Spintax processed first (variations resolved)
  2. Tags replaced second (with fallbacks)
  3. Final message sent

Spintax

Spintax allows message variations: Syntax:
  • {option1|option2|option3}
  • Example: {Hi|Hello|Hey} {firstName}
Consistency:
  • Same lead always gets same variation (deterministic)
  • Uses hash of contact_id as seed
  • Ensures consistent experience per lead
Nested Spintax:
  • Supports nested variations: {Hi|Hello} {there|here}
  • Processed recursively until all resolved
Distinction from Tags:
  • Spintax: 3+ options → random selection
  • Tag with fallback: 2 options → data replacement
  • Processor distinguishes automatically

A/B Testing

Configuration

Variations:
  • Multiple flow configurations (A, B, C, etc.)
  • Each variation has independent nodes/edges
  • Variations can be enabled/disabled independently
Selection:
  • Deterministic: Same lead always gets same variation
  • Uses hash of contact_id for consistency
  • Weighted distribution: Configurable weights per variation
Optimization:
  • Automatic optimization based on performance metrics
  • Compares response rates between variations
  • Adjusts weights to favor better-performing variations
  • Threshold: ab_test_optimization_threshold (default: 0.1 = 10%)
Statistics:
  • Tracked per variation: messages sent, responses, response rate
  • Updated every 5 minutes by worker
  • Available via /campaign-flows/{id}/ab-statistics endpoint

Statistics

Real-Time Metrics

Execution Metrics:
  • total_contacts: Total leads in campaign
  • contacts_started: Executions that began processing
  • contacts_completed: Executions that finished successfully
  • contacts_failed: Executions that failed
  • contacts_active: Currently processing executions
  • contacts_responded: Leads who replied
Message Metrics:
  • messages_sent: Total messages sent
  • messages_failed: Failed send attempts
  • messages_delivered: Confirmed deliveries
  • messages_not_sent: Pending sends
Rates:
  • completion_rate: (completed / total) × 100
  • response_rate: (responded / delivered) × 100
  • delivery_rate: (delivered / sent) × 100
Calculation:
  • Statistics computed by database function (get_campaign_statistics)
  • Cached for performance (refreshed every 5 minutes)
  • Available via /campaign-flows/{id}/statistics endpoint

Audit Logs

Execution Logs

Every node execution creates a log entry: Log Fields:
  • node_type: Type of node executed
  • status: completed, failed, skipped
  • executed_at: Timestamp of execution
  • input_data: Input context (contact info, message template)
  • output_data: Output result (sent message, contact info)
  • error_message: Error details if failed
Log Types:
  • sendMessage: Message sent to lead
  • wait: Wait period started
  • condition: Condition evaluated
  • delivery: Delivery status checked
  • response_detection: Response check performed
Access:
  • Available via /campaign-flows/{id}/logs endpoint
  • Filtered by execution for lead-specific view
  • Used for debugging and compliance

When Campaigns Stop

Automatic Completion

Campaign automatically stops when:
  1. All executions reach terminal status (completed, responded, failed, cancelled)
  2. No pending/running/waiting executions remain
  3. Status changes to completed

Manual Stopping

Pause:
  • Temporarily stops processing
  • Executions paused (can be resumed)
  • Status: paused
Stop:
  • Immediately cancels all active executions
  • Status: cancelled
  • Cannot be resumed (must duplicate)
Disable:
  • Toggles enabled flag to false
  • Cancels all active executions immediately
  • Prevents new launches
  • Different from pause (cannot launch when disabled)

Response Detection

If pause_on_response = true:
  • Execution stops immediately when response detected
  • Status: responded
  • No further messages sent

Fallback Mechanisms

Message Sending Fallback

If primary chat_id fails:
  1. Tries telegram_user_id (if available)
  2. Tries phone_number (if available)
  3. Tries username (if available)
  4. If all fail, execution marked as failed

Tag Fallback

If tag value missing:
  • Uses fallback value if specified: {tag|fallback}
  • Otherwise, tag left as-is (prevents broken messages)

Rate Limit Fallback

If rate limit exceeded:
  • Execution waits for cooldown period
  • next_execution_at adjusted
  • Retries automatically on next worker cycle

Best Practices

  1. Start Small: Test with small audience before full launch
  2. Monitor Rate Limits: Watch account health and rate limit usage
  3. Use Fallbacks: Always provide fallback values for tags
  4. Test Flow: Review flow visually before launching
  5. Respect Timezones: Set correct timezone for scheduled campaigns
  6. Monitor Responses: Check response rates and adjust messaging
  7. Use A/B Testing: Test variations to optimize performance
  8. Review Logs: Check audit logs for debugging and compliance