Marketing Automation App Development
Build custom app solutions with Scrums.com's expert development team. With an NPS (Net Promoter Score) of 82, Scrums.com crafts cost-effective, custom applications that drive results.
Marketing automation platforms are workflow engines with a marketing user interface on top. The engineering complexity is not in the email send; delivery infrastructure is a commodity. It is in the workflow execution layer: state machines that track where each contact is in a journey, evaluate branching conditions in real time, handle re-entry and exit logic correctly, and do this reliably across millions of simultaneous enrolments without missing a trigger or duplicating a send. Scrums.com builds dedicated engineering teams for MarTech companies and SaaS platforms building marketing automation engines from scratch or rebuilding a legacy automation layer that cannot scale.
Workflow Engine Architecture
The workflow engine is the core of a marketing automation platform. It must represent arbitrary directed acyclic graphs of triggers, conditions, and actions; execute steps in order with correct branching logic; persist state durably so that workflows survive process restarts; and handle delays (wait for 3 days, wait until Monday 9am in the contact's timezone) without holding an active thread.
Workflow definitions are stored as configuration data (JSON or YAML DAG definitions), not compiled code. A workflow record specifies: entry trigger (form submitted, tag added, score threshold crossed), steps (send email, wait N days, evaluate condition, add to list, notify sales), and exit conditions (goal achieved, manual removal, disqualification trigger). When a contact enters a workflow, a workflow_enrolment record is created. The current step position is stored on the enrolment record; the workflow definition itself is never mutated.
Step execution uses a durable workflow orchestrator (Temporal is the reference implementation) or a purpose-built state machine backed by a workflow_step_queue. Wait steps create a scheduled job (Redis sorted set keyed by enrolment_id, scored by the wake time in Unix timestamp) rather than blocking a thread. The scheduler polls for due enrolments and resumes them. This architecture supports millions of concurrent enrolments without proportional thread count.
Branching conditions are evaluated at runtime against the contact's current attribute values, not at enrolment time. This means that a condition branch checking whether a contact is a paid subscriber reflects their current subscription status at the moment they reach that step. A configuration option to snapshot contact attributes at enrolment time is available for workflows where point-in-time evaluation is required.
Lead Scoring and Contact Lifecycle Management
Lead scoring translates diverse contact signals into a single number that sales teams use to prioritise outreach. Getting the scoring model wrong wastes sales capacity on low-intent contacts or under-prioritises high-intent contacts who disengage while waiting for follow-up.
Scoring is implemented as a rules engine: each rule defines a signal (email opened, page visited, form submitted, job title contains "Director", company size above 200), a score delta (positive for intent signals, negative for disqualification signals), and an optional decay function (email open scores decay 50% over 30 days to prevent old activity from inflating current intent). Rules are stored as configuration data. The composite score is a projection over the contact's event history evaluated against the active ruleset at query time, not a pre-aggregated field. This means that changing the scoring model retroactively re-scores all contacts against the new rules without data migration.
Contact lifecycle stages (subscriber, lead, MQL, SQL, opportunity, customer, churned) are stored as an append-only lifecycle_events log, not as a status field. Lifecycle progression rules are configured in the workflow engine: when a contact's score crosses the MQL threshold, a lifecycle_event is emitted, which triggers a notification workflow for the assigned sales rep. Regression (SQL back to MQL after a deal is lost) creates a new lifecycle event rather than overwriting the previous stage, preserving the full history.
Suppression logic is applied at the delivery layer, not the workflow layer. A contact who has unsubscribed continues to progress through the workflow state machine but all delivery actions are intercepted by the suppression check. This design keeps the workflow history complete for analytics while enforcing communication compliance. The suppression check is atomic with the send action, not a pre-flight check that could be bypassed by a race condition.
Marketing automation platforms like these are built and delivered by dedicated engineering teams through our mobile app development service.
Trigger Architecture and Real-Time Event Processing
Triggers are the entry points into marketing automation workflows. They must fire reliably, fire exactly once (or at-least-once with idempotent processing), and fire with low latency for time-sensitive workflows (a welcome email triggered by a sign-up event must arrive within seconds, not minutes).
Events that trigger workflows are emitted as messages to a Kafka topic (or equivalent: SQS, Google Pub/Sub). Each event carries a contact_id, event type, timestamp, and a deduplication_key (typically contact_id + event_type + event_timestamp truncated to minute). A Kafka consumer group processes events and evaluates which workflows have a matching entry trigger. The deduplication_key is checked against a processed_triggers table before processing; duplicate events (from retry logic or double-firing pixels) are discarded. This ensures at-least-once delivery from the event bus combined with idempotent processing produces exactly-once workflow enrolments.
Re-entry logic is a critical edge case. A workflow configured with allow_re_entry: false must check whether the contact has an active or completed enrolment before creating a new one. The check and enrolment creation are performed inside a serialisable-isolation transaction to prevent race conditions where two simultaneous events for the same contact both pass the re-entry check and create duplicate enrolments.
Web behaviour tracking feeds triggers via a first-party tracking endpoint: a JavaScript snippet posts page views and custom events to a collection endpoint, which emits them to the trigger event bus. The tracking endpoint respects the contact's consent categories: contacts who have not consented to marketing tracking emit events that are stored but not processed for workflow triggering.
Automation Analytics and Performance Measurement
Marketing automation analytics has two distinct use cases that should not be served by the same data layer. Operational analytics (is this workflow running correctly? are emails delivering?) must be low-latency and always current. Strategic analytics (which workflows drive the most pipeline? what is the time-to-MQL for contacts who enter this journey?) can tolerate a few hours of lag and benefit from warehouse-scale computation.
Operational metrics are maintained as materialised counters per workflow step: enrolments, completions, exits (goal-achieved vs timed-out vs manually removed), email send/open/click rates. These counters are updated by the workflow execution layer via event-driven counter increments (Redis or ClickHouse for real-time aggregation). The workflow builder UI reads these counters directly for in-product reporting.
Strategic metrics are computed as dbt models in the data warehouse: time-in-stage distributions (how long does it take contacts to move from MQL to SQL for contacts who entered via workflow X vs workflow Y?), workflow-to-revenue attribution (which journey touch points appear most often in the paths of contacts who converted?), and cohort analysis. These models run nightly and are exposed via a BI layer.
A/B testing on workflows (send the welcome email immediately vs wait 1 hour) uses deterministic hash variant assignment at enrolment time (contact_id + experiment_id modulo variant_count), SPRT sequential testing for early stopping, and holdout groups defined at workflow configuration time. Test results are materialised in the warehouse alongside other strategic metrics.
Ready to build a marketing automation platform that scales? Start a conversation with Scrums.com or explore our dedicated team model.
Frequently Asked Questions
How do you handle timezone-aware wait steps at scale: for example, "send this email at 9am in the contact's local timezone"?
Each contact record stores a timezone attribute (IANA timezone identifier, e.g. "America/New_York"). When a workflow step schedules a "send at 9am local time" action, the scheduler converts 9am in the contact's timezone to a UTC timestamp and stores it as the wake time in the sorted set. Contacts without a stored timezone fall back to a configurable default. Timezone updates do not retroactively adjust already-scheduled wake times; they apply to steps that have not yet been scheduled. The wake time calculation uses a timezone library that correctly handles DST transitions: a contact in New York scheduled for 9am during a DST change receives the send at 9am local time, not 8am or 10am UTC offset.
What prevents a high-volume trigger event from overwhelming the workflow engine?
The trigger event bus (Kafka) decouples event production from workflow processing. The workflow engine consumer group has a configurable concurrency cap per workflow, so a viral event that triggers 500,000 enrolments within a minute does not spin up 500,000 concurrent execution threads; it queues them and processes them at a rate the execution infrastructure can handle. Priority queues separate time-sensitive triggers (sign-up events, purchase confirmations) from bulk triggers (marketing list imports) so that sign-up welcome emails are processed in near-real-time even during a bulk import. The pending_enrolments queue depth is a monitored SLA metric with an alert threshold.
How do you correctly handle a contact who is simultaneously enrolled in multiple workflows that could send conflicting communications?
Contact communication frequency capping is implemented as a shared state per contact: a contact_send_log table records every outbound communication attempt (channel, timestamp, workflow_id). Before any delivery action executes, the workflow engine checks the send log against the contact's configured frequency cap (maximum 1 email per day, maximum 3 emails per week). If the cap is reached, the delivery action is skipped (not re-queued) and a send_suppressed_frequency_cap event is recorded. Workflow designers can configure per-workflow priority levels so that transactional or high-priority workflows override the frequency cap for their send actions.
Don't Just Take Our Word for It
Hear from some of our amazing customers who are building with Scrums.com Teams.
Find Related App Types
Remote Work app
E-Commerce Platform App
Credit Management App
Stock market app
Courier Delivery App
Payment Processing app
Good Reads From Our Blog
Stay up-to-date with the latest trends, best practices, and insightful discussions in the world of mobile app development. Explore our blog for articles on everything from platform updates to development strategies.
Essential Guides
Gain a deeper understanding of crucial topics in mobile app development, including platform strategies, user experience best practices, and effective development workflows with expertly crafted guides.













.png)
