• Skip to main content
  • Skip to navigation
  • Skip to search
    Petunia™
    FeaturesPricingIntegrationsAboutContact
    Log inStart free trialSign up
    Loading
    Petunia™

    Reimagining customer communication for the modern business.

    Product

    • Features
    • Pricing
    • Integrations
    • Roadmap
    • What's New

    Resources

    • Help Center
    • Documentation
    • Guides
    • API Reference
    • Community
    • Support

    Company

    • About Us
    • Careers
    • Blog
    • Press
    • Contact

    © 2026 Gray Group International LLC. All rights reserved.·
    Made by gardenpatch 🌱

    Privacy PolicyTerms of ServiceCookie Policy

    Petunia™ is a trademark of Gray Group International LLC. The Petunia name, brand, product design, and content are proprietary. Unauthorized use, imitation, or copying is prohibited.

    Documentation

    SEQUENCE_LIMITATIONS

    docs/SEQUENCE_LIMITATIONS.md
    Docs homeGuidesSupport
    Quick links
    Start here
    How the docs are organized.
    Environment setup
    Configure env + run locally.
    Unified Inbox
    Inbox concepts & behavior.
    Voice AI setup
    Providers, Twilio, testing.
    Pricing model
    Source-of-truth pricing.
    Operations runbook
    How to operate safely.

    Petunia Sequence System: Current Capabilities

    Last Updated: December 2025 Status: Production Test Coverage: Verified by /tests/lib/autoresponder/sequence-execution.test.ts

    This document provides a comprehensive overview of what Petunia's sequence and automation system can and cannot do. Understanding these capabilities helps you design effective follow-up campaigns within current constraints.

    Architecture Overview

    The sequence system consists of:

    • Production System (/lib/autoresponder/productionSystem.ts) - Core touchpoint and sequence management
    • Sequences API (/app/api/autoresponder/sequences/route.ts) - CRUD operations for sequences
    • Rules Engine (/lib/automation/rules-engine.ts) - Business automation triggers

    Important: The current implementation is CRUD-focused - sequences are stored and managed but execution relies on external scheduling mechanisms.


    What Sequences CAN Do

    Message Sending

    • Send emails at scheduled times after sequence enrollment
    • Send SMS messages at scheduled times after sequence enrollment
    • Add delays between steps (minutes, hours, days)
    • Use merge fields for basic personalization (name, company name, etc.)

    Lifecycle Management

    • Activate/deactivate sequences without losing enrolled contacts
    • Pause sequences for individual contacts
    • Resume sequences for paused contacts
    • Remove contacts from sequences

    Enrollment

    • Manually enroll contacts via UI or API
    • Auto-enroll new leads based on source (e.g., all Yelp leads)
    • Bulk enroll multiple contacts at once

    Tracking

    • Track delivery status of messages
    • Track opens for emails (with tracking pixel)
    • Track link clicks within messages
    • View sequence analytics (enrolled, completed, responded)

    What Sequences CANNOT Do (Yet)

    No Branching Logic

    Cannot: "If lead is hot, send email A; if cold, send email B"

    Current Limitation:
    Sequences are LINEAR - every enrolled contact receives the same steps in the same order.
    
    Workaround:
    Create separate sequences for each scenario:
    - "Hot Leads - Aggressive Follow-up"
    - "Cold Leads - Nurture Campaign"
    Manually assign contacts to appropriate sequence.
    

    No Conditional Execution

    Cannot: "Skip this step if previous email was opened" Cannot: "Only send if lead score > 50" Cannot: "Exit sequence if contact books appointment"

    Current Limitation:
    Steps execute regardless of previous interactions or external conditions.
    
    Workaround:
    - Monitor sequence analytics manually
    - Remove contacts who shouldn't continue
    - Use autoresponder for immediate response handling
    

    No Dynamic Content

    Cannot: Pull real-time data into message templates Cannot: Show different content based on contact attributes Cannot: Include dynamic pricing or availability

    Current Limitation:
    Message content is static at send time (only merge fields work).
    
    Workaround:
    - Use standard merge fields ({{firstName}}, {{companyName}})
    - Create multiple sequences with different static content
    - Update sequences regularly with current information
    

    No A/B Testing

    Cannot: Test different message variants automatically Cannot: Send winner to remaining contacts Cannot: Track variant performance automatically

    Current Limitation:
    No built-in A/B testing or experimentation framework.
    
    Workaround:
    - Create duplicate sequences with different content
    - Manually split contacts between sequences
    - Compare analytics manually
    

    No External Triggers

    Cannot: Start sequence when webhook fires Cannot: Trigger on calendar events Cannot: Start based on website behavior Cannot: Trigger from third-party app events

    Current Limitation:
    Sequences must be started via UI, API call, or auto-enroll rules.
    
    Workaround:
    - Use API to enroll contacts programmatically
    - Build automation with external tools (Zapier, Make) that call our API
    

    No Time-Zone Awareness

    Cannot: Send at 9am in contact's local timezone Cannot: Respect contact's timezone for scheduling

    Current Limitation:
    All times are based on portal timezone, not contact timezone.
    
    Workaround:
    - Create timezone-specific sequences for major regions
    - Schedule for times that work across timezones (e.g., 11am EST = 8am PST)
    

    No Reply Detection Branching

    Cannot: Automatically change path based on reply content Cannot: Route to different steps based on sentiment

    Current Limitation:
    Sequences pause on ANY reply, but don't analyze reply content.
    
    Workaround:
    - Sequence pauses when contact replies (good for human follow-up)
    - Use autoresponder for immediate reply handling
    - Manually review replies and take action
    

    Common Use Cases & Solutions

    Use Case: Follow up with new leads

    Solution: Create a 5-step email sequence with increasing urgency

    Step 1: Day 0 - Welcome email
    Step 2: Day 2 - Value proposition
    Step 3: Day 5 - Case study / social proof
    Step 4: Day 8 - Direct call-to-action
    Step 5: Day 14 - Final follow-up
    

    Use Case: Different follow-up for different sources

    Solution: Create source-specific sequences and use auto-enroll rules

    Sequence: "Yelp Lead Nurture"
    - Auto-enroll: source = "yelp"
    - Messaging: Reference Yelp specifically
    
    Sequence: "Google Lead Nurture"
    - Auto-enroll: source = "google"
    - Messaging: Reference Google specifically
    

    Use Case: Re-engage cold leads

    Solution: Create a re-engagement sequence, manually enroll cold leads

    Sequence: "Re-engagement Campaign"
    - Step 1: "We haven't heard from you..."
    - Step 2: Special offer or incentive
    - Step 3: Final "last chance" message
    
    Manually filter leads by:
    - No activity in 30+ days
    - Not currently in any sequence
    - Bulk enroll into re-engagement
    

    Performance Considerations

    Sending Limits

    • Email: Subject to email provider limits (typically 500-1000/day for new senders)
    • SMS: Subject to carrier limits and opt-out rules
    • Recommendation: Ramp up sending volume gradually

    Step Delays

    • Minimum delay: 1 minute between steps
    • Maximum contacts per step: No hard limit, but large batches are queued
    • Delivery time: May vary by 1-5 minutes from scheduled time
    • Delay precision: Tests verify delays are stored accurately in milliseconds

    Database Impact

    • Large sequences (1000+ contacts) may have slight delay in analytics updates
    • Bulk enrollment processes in batches to avoid performance issues

    Non-Persistent Scheduling

    • Current implementation: Uses setTimeout for delays (not persistent across restarts)
    • Implication: Server restart may affect pending scheduled messages
    • Workaround: Critical sequences should use shorter delays or external scheduling

    Technical Implementation Notes

    Sequence Data Structure

    interface Sequence {
      id: string;
      name: string;
      description?: string;
      steps: SequenceStep[];
      isActive: boolean;
      touchpoints?: Touchpoint[];
    }
    
    interface SequenceStep {
      id: string;
      type: 'email' | 'sms' | 'touchpoint';
      content: unknown;
      delay?: number; // milliseconds from previous step
      condition?: unknown; // Stored but NOT evaluated
    }
    

    Touchpoint Data Structure

    interface Touchpoint {
      id: string;
      name: string;
      channel: 'sms' | 'email' | 'voice' | 'yelp' | 'facebook' | 'google' | 'whatsapp' | 'webchat';
      conditions: TouchpointCondition[];
      message: string;
      isActive: boolean;
      triggerDelay?: number;
      triggerType?: 'event' | 'time';
      scheduledTime?: string; // Portal timezone only
    }
    

    Test-Verified Behaviors

    The following behaviors are verified by automated tests:

    1. Business Isolation: Sequences are isolated per business - businessId is required for all operations
    2. Step Order Preservation: Steps maintain their order as defined
    3. Delay Accuracy: Step delays are stored and retrieved accurately
    4. Activation Toggle: Sequences can be activated/deactivated without data loss
    5. Touchpoint References: Sequences can reference touchpoints by ID

    Autoresponder Integration

    The autoresponder system (/lib/services/autoResponderService.ts) handles:

    • Channel detection: Determines which platform the message came from
    • Trigger evaluation: Checks business hours, channel enablement
    • Response generation: AI or template-based responses

    Test coverage: /tests/lib/autoresponder/trigger-evaluation.test.ts verifies:

    • Channel enablement checks (per-channel toggle)
    • Business hours validation (timezone-aware)
    • Response type selection (AI vs template)
    • Trigger priority ordering

    Roadmap

    These features are planned for future releases:

    FeatureTargetStatus
    Conditional branchingQ1 2026Planning
    Lead score triggersQ1 2026Planning
    A/B testingQ2 2026Backlog
    Webhook triggersQ2 2026Backlog
    Time-zone awarenessQ2 2026Backlog
    Dynamic contentQ3 2026Backlog

    FAQ

    Q: Can I stop a sequence for all contacts at once? A: Yes, deactivating a sequence pauses it for all contacts. Reactivating resumes where they left off.

    Q: What happens if a contact is in multiple sequences? A: They receive messages from all sequences. Be careful about over-messaging.

    Q: Can I edit a sequence while contacts are enrolled? A: Yes, but changes only affect future step executions, not already-sent messages.

    Q: How do I know if a sequence is working? A: Check sequence analytics for delivery rates, open rates, and reply rates.


    API Usage Examples

    Creating a Sequence via API

    // POST /api/autoresponder/sequences
    const response = await fetch('/api/autoresponder/sequences', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        businessId: 'your-business-id',
        name: 'Welcome Sequence',
        description: 'Automated follow-up for new leads',
        steps: [
          { id: 'step-1', type: 'email', content: { subject: 'Welcome!', body: 'Hello {{firstName}}!' }, delay: 0 },
          { id: 'step-2', type: 'sms', content: { message: 'Following up on my email...' }, delay: 86400000 }, // 1 day
        ],
        isActive: true,
      }),
    });
    

    Calculating Cumulative Timing

    // When displaying sequence timeline, calculate cumulative delays
    function calculateSequenceTimeline(steps: SequenceStep[]): StepTiming[] {
      let cumulativeTime = 0;
      return steps.map(step => {
        cumulativeTime += step.delay || 0;
        return {
          stepId: step.id,
          executeAtMs: cumulativeTime,
          executeAtHuman: formatDuration(cumulativeTime),
        };
      });
    }
    
    // Example output:
    // Step 1: Immediately (0ms)
    // Step 2: After 1 day (86400000ms)
    // Step 3: After 3 days total (259200000ms)
    

    Checking Trigger Eligibility

    // Evaluate if a message should trigger autoresponder
    function shouldTriggerAutoresponder(settings: AutoresponderSettings, message: IncomingMessage): boolean {
      // 1. Check global enablement
      if (!settings.enabled) return false;
    
      // 2. Check channel enablement
      if (!settings.channels[message.channel]) return false;
    
      // 3. Check business hours (if enabled)
      if (settings.businessHours.enabled) {
        if (!isWithinBusinessHours(settings, message.timestamp)) return false;
      }
    
      return true;
    }
    

    Workaround Patterns

    Pattern 1: Simulating Conditional Branching

    Since sequences don't support branching, use multiple sequences with manual assignment:

    Setup:
    1. Create "Hot Leads - Fast Track" sequence (aggressive, 3-day cycle)
    2. Create "Cold Leads - Nurture" sequence (gentle, 14-day cycle)
    3. Create lead scoring criteria
    
    Workflow:
    1. New lead arrives
    2. Score lead based on criteria (engagement, source, etc.)
    3. Assign to appropriate sequence via API
    4. Monitor and re-assign if engagement changes
    

    Pattern 2: Simulating Exit Conditions

    Since sequences don't auto-exit on events, use monitoring + API:

    Setup:
    1. Create sequence with all steps
    2. Set up webhook for "appointment booked" event
    
    Workflow:
    1. Contact enrolled in sequence
    2. Contact books appointment → webhook fires
    3. Webhook handler calls API to remove contact from sequence
    4. Contact receives no more sequence messages
    

    Pattern 3: Time-Zone-Aware Sending

    Since all times are portal timezone, use time slots that work across zones:

    Strategy: "Safe Windows"
    - 11:00 AM EST = 8:00 AM PST = 4:00 PM GMT
    - Covers US business hours reasonably
    - Add regional sequences for international audiences
    
    Alternative: API-Based Scheduling
    - Store contact timezone in metadata
    - Use external scheduler (e.g., Cloud Functions)
    - Call sequence API at appropriate local time
    

    Pattern 4: Handling Server Restarts

    Since delays use setTimeout, scheduled messages may be lost on restart:

    Mitigation Strategies:
    1. Use shorter delays (hours vs days) for critical messages
    2. Implement a "catch-up" cron job that checks for overdue steps
    3. Store scheduled execution times in database
    4. On startup, re-schedule any pending messages
    
    Example Catch-Up Query:
    SELECT * FROM sequence_enrollments
    WHERE next_step_at < NOW()
    AND status = 'active';
    

    Support

    For questions about sequences or to request features:

    • Email: support@petunia.ai
    • In-app: Use the help widget
    • Documentation: This file + /features/12-autoresponder-sequences.md

    Related Test Files

    • /tests/lib/autoresponder/sequence-execution.test.ts - Sequence CRUD, timing, validation
    • /tests/lib/autoresponder/trigger-evaluation.test.ts - Trigger evaluation, channel checks
    • /tests/lib/automation/rules-engine.test.ts - Business automation rules
    • /tests/integration/flows/07-autoresponder-trigger-execute.test.ts - Integration tests
    On this page
    Architecture OverviewWhat Sequences CAN DoMessage SendingLifecycle ManagementEnrollmentTrackingWhat Sequences CANNOT Do (Yet)No Branching LogicNo Conditional ExecutionNo Dynamic ContentNo A/B TestingNo External TriggersNo Time-Zone AwarenessNo Reply Detection BranchingCommon Use Cases & SolutionsUse Case: Follow up with new leadsUse Case: Different follow-up for different sourcesUse Case: Re-engage cold leadsPerformance ConsiderationsSending LimitsStep DelaysDatabase ImpactNon-Persistent SchedulingTechnical Implementation NotesSequence Data StructureTouchpoint Data StructureTest-Verified BehaviorsAutoresponder IntegrationRoadmapFAQAPI Usage ExamplesCreating a Sequence via API