Skip to Content
Workflow Model

Workflow Model

The Complete Journey

Tenant registers (provides company website URL) ├─► Website crawler starts immediately (agent__tenant-web-crawler) │ Playwright BFS crawl — pages, images, documents stored in DB │ Homepage analysed for brand signals → colors + fonts written to brand_assets ├─► Connect Channels (LinkedIn, Facebook, WordPress, GBP, etc. via OAuth) Onboarding Wizard (/onboarding — 6 steps) Step 1: Connect Channels Step 2: Brand Voice (tone, personality, writing style) Step 3: Brand Assets (colors + fonts — pre-filled from website crawl if found) Step 4: Documents (upload brand docs) Step 5: Baseline (current traffic, ads, social metrics) Step 6: Launch → kicks off setup chain Onboarding Agent (setup chain) Researches client: website, competitors, industry, products Generates → Client Context File (skill injected into all agents) Strategy Agent Reads context file Generates → Marketing Strategy (goals + recommended deliverables + rationale) Human reviews and approves strategy Goals High-level outcomes the strategy aims to achieve e.g. "Increase organic website traffic", "Improve local search presence" Each goal has a metric and target Deliverables (per goal, recurring monthly) Fixed output commitments: 10 blogs, 12 social posts, 10 backlinks, etc. Each deliverable has a monthly period tracker Requests (BlogRequest / SocialMediaRequest) Raised by an agent (from the deliverable pipeline) OR by a DM team member ad-hoc Defines WHAT to create: topic, brief, target audience, target channel(s) Goes through approval before any content is created │ approved Content Entities (BlogPost / SocialPost) Created automatically when the request is approved The actual content that agents will write Activities Agent writes content → Human reviews → Approved → Published to Channel Completing all activities → deliverable unit fulfilled Fulfilling all deliverables → goals met

Concepts Defined

Client Context File

A Markdown skill document auto-generated by the Onboarding Agent. Contains:

  • Company overview, products, USPs
  • Target audience
  • Brand tone and voice
  • Competitors
  • Point of contact

This file is injected into every agent that executes activities for this tenant. It is the single source of truth for who the client is.

Created by: Onboarding Agent (initial generation) + human review/edit. Stored as: A skill file in MongoDB, assigned to all agent roles for this tenant. Updated by: Tenant can edit at any time in Settings → Skills.


Strategy

The marketing roadmap generated from the context file. Defines:

  • What goals to pursue
  • Which deliverable types will meet those goals
  • Monthly volumes for each deliverable
  • Platforms to focus on
  • Recommended timeline

Created by: Strategy Agent. Reviewed and approved by: Human (DM Portal approval step). Stored in: PostgreSQL strategies table.

A tenant has one active strategy at a time. Strategies are versioned — a new one can be created without deleting the old one.


Goals

High-level outcomes. Tied to a strategy. Measurable.

Examples:

  • Increase organic website traffic by 40% in 6 months
  • Rank in top 3 for 5 target keywords
  • Generate 50 qualified leads/month via content
  • Improve local search visibility (Google Business Profile)
  • Build domain authority from DA 20 to DA 40

Each goal has:

  • Name and description
  • Primary metric (sessions, rankings, leads, DA score)
  • Target value and target date
  • Status (on-track / at-risk / achieved)

Deliverables

Fixed, recurring monthly commitments that collectively fulfill the goals. The agency commits to delivering these every month.

Examples:

Deliverable TypeVolumeLinked Goal
Blog posts10/monthIncrease organic traffic
Social posts12/monthBrand awareness + engagement
Backlinks10/monthDomain authority improvement
Google Business Profile posts10/monthLocal search visibility
Website audit1/monthTechnical SEO health
Email newsletter2/monthLead nurture
Ad copy sets4/monthPaid acquisition
Performance report1/monthClient visibility

Each deliverable has:

  • Type and monthly target quantity
  • Linked goal(s)
  • Monthly period tracker (how many completed this month vs. target)
  • Status: on_track | at_risk | completed | missed

Channels

Channels are the external platforms a tenant connects via OAuth from their Dashboard. Each channel is a publishing destination — agents use the stored credentials to post content after human approval.

Examples:

  • LinkedIn Company Page
  • Facebook Business Page
  • Instagram Business Account
  • WordPress site (via REST API + application password)
  • Google Business Profile (GBP)
  • Google Search Console (read-only — for performance data)
  • X (Twitter)
  • TikTok Business Account
  • Mailchimp / Klaviyo (email)

Connection flow:

  1. Tenant opens Dashboard → Channels (/channels)
  2. Clicks “Connect” for a platform → OAuth flow begins (redirect to platform)
  3. Platform redirects back with auth code → system exchanges for access + refresh tokens
  4. Channel record created with encrypted tokens + metadata (page ID, account name, etc.)
  5. Agent configs updated — agents can now post to this channel

Token management:

  • Access tokens stored encrypted at rest
  • Refresh tokens stored encrypted at rest
  • System automatically refreshes access tokens before expiry
  • If refresh fails → channel status set to expired → alert created for tenant admin

Channel types:

  • social — LinkedIn, Facebook, Instagram, X, TikTok (publish posts)
  • cms — WordPress, Webflow (publish blog posts)
  • analytics — Google Search Console, GA4 (read performance data)
  • local — Google Business Profile (publish GBP posts)
  • email — Mailchimp, Klaviyo (send email campaigns)
  • ads — Google Ads, Meta Ads (manage ad campaigns)
  • Website — tenant’s own website; Playwright BFS crawler stores pages + media + brand signals; auto-created at signup when website URL is provided

Requests (BlogRequest / SocialMediaRequest)

A Request is the brief or specification for a piece of content — raised before any content is created. Requests decouple the INTENT (what we want to make) from the EXECUTION (actually making it).

Who can raise a request:

  • 🤖 An agent (e.g. SEO Specialist raising blog requests as part of the deliverable pipeline)
  • 👤 A DM team member (ad-hoc request from the DM Portal)
  • 👤 A tenant admin (ad-hoc request from the Dashboard)

Request lifecycle:

draft → pending_approval → approved → in_progress → completed → rejected → (closed or revised)

On approval → content entity auto-created:

  • BlogRequest approved → BlogPost record created (status: draft)
  • SocialMediaRequest approved → SocialPost record(s) created (one per target channel)

Key properties of a request:

  • Title / topic brief
  • Target keyword(s) (blog only)
  • Target audience
  • Target channel(s) — which connected channel(s) the content will publish to
  • Notes / creative direction
  • Priority
  • Linked deliverable (if part of automated pipeline) or ad-hoc (if manual)

Content Entities (BlogPost / SocialPost)

Content entities are created automatically when a Request is approved. They represent the actual content that agents will write.

BlogPost:

  • Linked to the BlogRequest that created it
  • Has a lifecycle: draft → writing → review → approved → scheduled → published
  • Content stored in MongoDB (can be large — full article with formatting)
  • Published to a cms channel (WordPress, Webflow) using stored OAuth tokens

SocialPost:

  • One record per platform/channel (a request targeting LinkedIn + Facebook = 2 SocialPost records)
  • Each platform may have different content (different lengths, formats, hashtags)
  • Lifecycle: draft → writing → review → approved → scheduled → published
  • Published to the target social channel using stored OAuth tokens

Activities

The atomic unit of work. Everything that happens in the system is an activity.

An activity can be:

  • An agent task (research topics, write a blog post, build backlink list)
  • A human task (review and approve topics, final copy review)
  • An approval gate (HITL sign-off before content proceeds)

Key properties:

  • Assigned to a principal: either an AI agent or a human user
  • Has a clear input and expected output
  • Progresses through a status lifecycle
  • May have a parent activity (sub-tasks)
  • May be blocked by another activity (dependencies)

Activities are the core unit — the thing you create, assign, track, complete, and report on.


Activity Lifecycle

created → assigned → in_progress → awaiting_approval → approved → completed │ │ ▼ ▼ failed rejected → back to in_progress (retry)

Human activities follow the same lifecycle but the “execution” is the human completing the task in the DM Portal, not an agent call.


Example: Producing 10 Blog Posts per Month

This shows how a single deliverable (10 blog posts/month) maps to a pipeline with Requests, content entities, and channel publishing.

Deliverable: 10 Blog Posts — April 2026 Channel: WordPress (acmecorp.com) — connected via OAuth ┌─────────────────────────────────────────────────────────────┐ │ Activity: Research 10 Blog Topics │ │ Assigned to: 🤖 SEO Specialist + Content Researcher │ │ Output: 10 BlogRequests created (status: pending_approval) │ │ Each has: topic, target keyword, search intent │ └───────────────────────────┬─────────────────────────────────┘ │ completes ┌─────────────────────────────────────────────────────────────┐ │ Activity: Review & Approve Blog Requests │ │ Assigned to: 👤 Human (DM Portal) │ │ Input: 10 BlogRequests │ │ Action: approve all / reject some / edit brief │ │ On approval of each: BlogPost entity auto-created (draft) │ └───────────────────────────┬─────────────────────────────────┘ │ approved (e.g. 10/10) ▼ (one sub-pipeline per approved BlogPost) ┌─────────────────────────────────────────────────────────────┐ │ Activity: Write Blog Post — "{Topic Title}" [×10] │ │ Assigned to: 🤖 Copywriter │ │ Input: BlogRequest brief + SEO brief + client context │ │ Output: BlogPost content updated (status: review) │ └───────────────────────────┬─────────────────────────────────┘ │ completes ┌─────────────────────────────────────────────────────────────┐ │ Activity: Review & Approve Blog Post — "{Topic Title}" [×10]│ │ Assigned to: 👤 Human (DM Portal) │ │ Action: approve / reject with notes / edit inline │ │ On approval: BlogPost status → approved │ └───────────────────────────┬─────────────────────────────────┘ │ approved ┌─────────────────────────────────────────────────────────────┐ │ Activity: Publish to WordPress — "{Topic Title}" [×10] │ │ Assigned to: 🤖 Copywriter (using WordPress channel tokens) │ │ Action: POST to WordPress REST API using stored OAuth token │ │ Output: BlogPost.published_url set; status → published │ └───────────────────────────┬─────────────────────────────────┘ │ published (10/10) ┌─────────────────────────────────────────────────────────────┐ │ Deliverable COMPLETED: 10 Blog Posts ✅ │ │ Goal progress updated: Organic Traffic goal +10 blogs │ └─────────────────────────────────────────────────────────────┘

Ad-hoc blog request (outside of deliverable pipeline):

A DM team member or tenant admin can raise a BlogRequest at any time from the Dashboard or DM Portal without it being part of the monthly deliverable cycle. These go through the same approval → write → review → publish pipeline but are not counted against any deliverable quota.


Deliverable: 10 Backlinks — April 2026 Goal: Improve Domain Authority Activity: Research 10 Backlink Opportunities → 🤖 SEO Specialist (Ahrefs/SEMrush tools) → Output: list of 10 target sites with contact details + angle Activity: Review Backlink Opportunities → 👤 Human approval (filter out irrelevant sites) → Output: approved target list Activity: Write Outreach Email for each target [×10] → 🤖 Copywriter → Output: personalised outreach email per site Activity: Review Outreach Emails [×10] → 👤 Human (DM Portal) → Action: approve / edit / reject Activity: Send Outreach [×10] → 👤 Human (manually send) OR 🤖 via email integration → Output: sent confirmation + tracking Deliverable: 10 Backlinks Outreached ✅

Example: 12 Social Posts per Month

Deliverable: 12 Social Posts — April 2026 (3×/week) Channels: LinkedIn Company Page + Instagram Business (both connected) Goal: Brand Awareness + Engagement ┌──────────────────────────────────────────────────────────────┐ │ Activity: Create Social Content Calendar │ │ Assigned to: 🤖 Social Media Manager │ │ Output: 12 SocialMediaRequests (status: pending_approval) │ │ Each has: topic, platform(s), format, brief, date │ └────────────────────────────┬─────────────────────────────────┘ │ completes ┌──────────────────────────────────────────────────────────────┐ │ Activity: Review & Approve Content Calendar │ │ Assigned to: 👤 Human (DM Portal) │ │ Input: 12 SocialMediaRequests │ │ Action: approve / edit / reject individual requests │ │ On approval of each: SocialPost records created │ │ → 1 SocialPost per channel (LinkedIn + Instagram = 2 each) │ └────────────────────────────┬─────────────────────────────────┘ │ approved ▼ (one sub-pipeline per approved SocialPost) ┌──────────────────────────────────────────────────────────────┐ │ Activity: Write Post Content — [×12 requests, ×2 channels] │ │ Assigned to: 🤖 Copywriter / Social Media Manager │ │ Input: SocialMediaRequest brief + channel format rules │ │ Output: Platform-specific copy per SocialPost │ │ LinkedIn: longer, professional tone (up to 3000 chars) │ │ Instagram: punchy, hashtags, emoji (up to 2200 chars) │ └────────────────────────────┬─────────────────────────────────┘ │ completes ┌──────────────────────────────────────────────────────────────┐ │ Activity: Review & Approve Posts [×24 SocialPosts] │ │ Assigned to: 👤 Human (DM Portal) │ │ Action: approve / edit / reject per post per platform │ │ On approval: SocialPost status → approved │ └────────────────────────────┬─────────────────────────────────┘ │ approved ┌──────────────────────────────────────────────────────────────┐ │ Activity: Publish to Channel — [×24 SocialPosts] │ │ Assigned to: 🤖 Social Media Manager (using channel tokens) │ │ LinkedIn: POST /v2/ugcPosts using stored LinkedIn token │ │ Instagram: POST via Graph API using stored Instagram token │ │ Output: SocialPost.published_url; status → published │ └────────────────────────────┬─────────────────────────────────┘ │ all 12 requests published Deliverable COMPLETED: 12 Social Posts ✅ Goal progress updated: Brand Awareness goal

Example: 10 GBP Posts per Month

Deliverable: 10 Google Business Profile Posts — April 2026 Channel: Google Business Profile (acmecorp) — connected via GBP OAuth Goal: Improve Local Search Visibility Activity: Create 10 GBP Post Requests → 🤖 Social Media Manager → Output: 10 SocialMediaRequests (type: gbp_post, status: pending_approval) Activity: Review GBP Post Requests → 👤 Human (DM Portal) → On approval: SocialPost records created (channel: GBP) Activity: Write each GBP Post [×10] → 🤖 Copywriter (short format, local intent, 1500 char limit) → Output: SocialPost content updated (status: review) Activity: Review GBP Posts [×10] → 👤 Human final approval → On approval: SocialPost status → approved Activity: Publish to GBP [×10] → 🤖 Social Media Manager → POST via Google Business Profile API using stored OAuth token → SocialPost.published_url set; status → published Deliverable: 10 GBP Posts Published ✅

Channels Scoring

When a channel is first connected — and monthly thereafter — the Data Analyst calculates a Channel Health Score (0–100) for each connected channel. The score reflects how well the channel is set up and performing.

Score components vary by platform:

PlatformScore dimensions
LinkedIn PageProfile completeness, post frequency, engagement rate, follower growth, content variety
InstagramProfile completeness, post frequency, engagement rate, story usage, hashtag quality
Facebook PageCompleteness, response rate, post frequency, review rating
Google Business ProfileProfile completeness (hours, photos, description), review count + avg rating, post frequency
WordPressTechnical SEO health, content freshness, avg word count, internal linking

Score lifecycle:

  1. Channel connected → initial score calculated immediately
  2. Low-scoring areas → suggested improvement activities auto-created (e.g. “Upload LinkedIn cover image”, “Respond to 3 unanswered Google reviews”)
  3. Monthly re-score → score delta shown in Dashboard
  4. Improving the score is itself a deliverable goal (“Improve LinkedIn score from 68 to 80 in 3 months”)

Score history stored in channel_scores table per month for trend tracking.


Mini CRM — Lead Tracking

The system includes a lightweight CRM to track leads collected from all channels and campaigns. Growing the number of qualified leads is a first-class goal type that the strategy can target.

Lead sources

Leads flow in from:

  • Website forms — via webhook (tenant embeds a script or webhook URL in their contact form)
  • Facebook Lead Ads — via Meta Webhooks (auto-captured when a lead form is submitted)
  • Google Ads Lead Form Extensions — via Google Ads API or webhook
  • Manual entry — DM team or tenant manually adds a lead
  • Imported — CSV upload

Lead lifecycle

Lead captured (from any source) ▼ (auto-enrichment activity — optional) 🤖 Enrichment: look up company info, LinkedIn profile, etc. Lead visible in CRM (status: new) ▼ Human or agent qualifies the lead ├── Qualified → lead.status = qualified │ │ │ ▼ │ (optional) Agent drafts follow-up email │ Human reviews + approves → sent via email channel └── Disqualified → lead.status = disqualified, reason noted

Relationship to goals

“Generate 50 qualified leads/month” is a strategy goal. The Data Analyst updates goals.current_value monthly by counting leads WHERE status = 'qualified' AND created_on >= period_start.


Paid campaigns (Google Ads, Meta Ads) follow a different pattern from content creation. Instead of creating new content, the agent syncs existing campaign data, classifies it, proposes optimizations, and pushes approved changes back.

Channel connected (Google Ads OAuth) ▼ (on connect + periodic sync) ┌─────────────────────────────────────────────────────────────┐ │ Activity: Sync Google Ads Campaigns │ │ Assigned to: 🤖 Paid Ads Manager │ │ Action: Pull campaigns, ad groups, keywords, search terms │ │ Output: `ad_campaigns`, `ad_groups`, `ad_keywords`, │ │ `search_term_reports` records upserted in DB │ └───────────────────────────┬─────────────────────────────────┘ │ completes ┌─────────────────────────────────────────────────────────────┐ │ Activity: Classify Search Terms │ │ Assigned to: 🤖 Paid Ads Manager │ │ Input: Raw search terms from sync │ │ Output: Each term classified as: │ │ - keyword (add as exact/phrase match) │ │ - negative_keyword (exclude) │ │ - watch (monitor, no action yet) │ │ Records: `search_term_classifications` created │ └───────────────────────────┬─────────────────────────────────┘ │ completes ┌─────────────────────────────────────────────────────────────┐ │ Activity: Review Search Term Classifications │ │ Assigned to: 👤 Human (DM Portal / Dashboard) │ │ Input: classified terms with recommended action │ │ Action: approve / override classification / skip │ │ On approval: push to Google Ads API │ │ - keyword → add to ad group as exact/phrase match │ │ - negative → add to campaign negative keyword list │ └───────────────────────────┴─────────────────────────────────┘ Separately (monthly performance cycle): ┌─────────────────────────────────────────────────────────────┐ │ Activity: Generate Optimization Recommendations │ │ Assigned to: 🤖 Paid Ads Manager │ │ Input: Campaign performance data (CTR, CPC, Conv rate, ROAS)│ │ Output: `ad_optimization_recommendations` records created │ │ e.g. "Increase bid for X by 15% — reason: CTR 8.2%" │ └───────────────────────────┬─────────────────────────────────┘ │ completes ┌─────────────────────────────────────────────────────────────┐ │ Activity: Review & Approve Optimizations │ │ Assigned to: 👤 Human (DM Portal / Dashboard) │ │ Action: approve / reject / snooze per recommendation │ │ On approval: push changes to Google Ads API │ │ (bid adjustments, budget changes, pause/enable keywords) │ └─────────────────────────────────────────────────────────────┘

Meta Ads Flow

Same pattern as Google Ads:

  1. Sync — pull campaigns, ad sets, ads, audience data from Meta Ads API
  2. Analyse — Paid Ads Manager analyses performance (CPC, CPM, CTR, ROAS, frequency)
  3. Recommend — create ad_optimization_recommendations (pause underperforming ads, adjust budgets, refresh creatives, update audiences)
  4. Review — human approves/rejects each recommendation
  5. Push — approved changes applied via Meta Marketing API

Key Distinction from Content Workflows

Content (Blog/Social)Paid Campaigns
Agent CREATES new contentAgent ANALYSES existing data
Request → Entity → Write → Review → PublishSync → Classify/Recommend → Review → Push
BlogRequestBlogPostSearchTermReportClassificationRecommendation
Approval gates on creative decisionsApproval gates on account changes (money involved)

Two Types of Activity Templates

The system has two distinct template concepts:

  1. Pipeline Templates — multi-step blueprints for producing a deliverable (e.g., 10 blog posts = research → approve → write × 10 → review × 10)
  2. Recurring Task Templates — standalone predefined tasks that recur on a schedule, not tied to a deliverable (e.g., “Update Google My Business listing”, “Send monthly report to client”)

Recurring Task Templates (Predefined Tasks)

Some activities are not part of a deliverable pipeline but are still recurring operational tasks that every tenant needs. These are defined as Recurring Task Templates.

How they work

Global Recurring Task Templates (managed in Manage App) │ On tenant creation: copy global templates → tenant-specific templates Tenant Recurring Task Templates (tenant can customise) │ Based on periodicity + trigger: auto-create activities Activities (created on schedule or on trigger)

Template properties

interface RecurringTaskTemplate { id: string; tenantId: string | null; // null = global template name: string; // e.g. "Update Google My Business listing" description: string; assigneeType: 'agent' | 'human'; agentRole?: AgentRole; humanRole?: 'reviewer' | 'admin'; periodicity: 'one_time' | 'daily' | 'weekly' | 'monthly' | 'quarterly'; trigger: 'on_onboarding' | 'on_strategy_approval' | 'cron' | 'manual'; cronExpression?: string; // if trigger = 'cron' promptTemplate: string; // task description with {{variables}} for substitution variables: TemplateVariable[]; // filled in per-tenant (e.g. GBP URL) isActive: boolean; sourceTemplateId: string | null; // global template this was copied from } interface TemplateVariable { key: string; // e.g. "gbp_url" label: string; // e.g. "Google Business Profile URL" type: 'string' | 'url' | 'number' | 'select'; required: boolean; value?: string; // filled in at tenant level }

Examples

Template NameTriggerPeriodicityAgent/HumanVariables
Update “always open” in Google My Businesson_onboardingone_time👤 Humangbp_url
Request Google reviews from recent customersmonthlymonthly👤 Humangbp_url, customer_contact_method
Verify NAP consistency across directoriesquarterlyquarterly🤖 SEO Specialistbusiness_name, address
Add new product/service to GBPmanual👤 Humangbp_url
Archive completed campaign assetsmonthlymonthly👤 Human
Update copyright year on websiteyearlyyearly👤 Humanwebsite_url

Tenant variable substitution

When a recurring task template is copied to a tenant, tenant-specific variables are filled in:

// Global template prompt: // "Go to {{gbp_url}} and set the listing to 'Always Open'" // After tenant fills in variables: // "Go to https://business.google.com/u/0/dashboard/l/ABC123 and set the listing to 'Always Open'"

Variables that are not yet filled in are flagged on the tenant’s Setup Checklist.


Pipeline Activity Templates

Rather than creating activity pipelines from scratch for each deliverable, the system uses activity templates — pre-defined pipelines per deliverable type.

When a deliverable period starts (e.g. April blog posts due), the system automatically spawns the activity pipeline from the template.

interface ActivityTemplate { deliverableType: DeliverableType; steps: ActivityTemplateStep[]; } interface ActivityTemplateStep { name: string; assigneeType: 'agent' | 'human'; agentRole?: AgentRole; // if agent humanRole?: 'reviewer' | 'approver' | 'admin'; // if human inputFrom?: string; // step name whose output feeds this step isApproval: boolean; repeatPerUnit?: boolean; // true = one activity per blog post, not one for all blockedBy?: string[]; // step names that must complete first }

Template: Blog Post Pipeline

const blogPostTemplate: ActivityTemplate = { deliverableType: 'blog_posts', steps: [ { name: 'Research topics', assigneeType: 'agent', agentRole: 'seo-specialist', isApproval: false, }, { name: 'Approve topic list', assigneeType: 'human', humanRole: 'reviewer', inputFrom: 'Research topics', isApproval: true, blockedBy: ['Research topics'], }, { name: 'Write blog post', assigneeType: 'agent', agentRole: 'copywriter', inputFrom: 'Approve topic list', isApproval: false, repeatPerUnit: true, // one per approved topic blockedBy: ['Approve topic list'], }, { name: 'Review and approve blog post', assigneeType: 'human', humanRole: 'reviewer', inputFrom: 'Write blog post', isApproval: true, repeatPerUnit: true, blockedBy: ['Write blog post'], }, ], };

Monthly Cadence

At the start of each month, the system automatically:

  1. Creates a DeliverablePeriod record for each active deliverable (e.g. “April 2026 Blog Posts”)
  2. Spawns the first activity in each deliverable’s pipeline
  3. Notifies the assigned human or agent

Agents work on their assigned activities. Humans review and approve in the DM Portal. As activities complete, downstream activities are unblocked and spawned automatically.

Progress tracking:

  • deliverable_periods.completed_count increments as each unit is finished
  • Goal progress_value is updated as deliverables complete
  • Dashboard shows: “Blog Posts: 6/10 completed this month”

How Activities Relate to the Control Plane

When an activity is assigned to an agent:

  1. Control plane creates an activities record
  2. Enqueues the activity to the correct BullMQ queue (tenant:agentType)
  3. Agent adapter dispatches the task to the agent runtime
  4. Agent executes, phones home with output
  5. Output stored in MongoDB (activity output)
  6. Activity status → awaiting_approval if the next step is a human review, else → completed
  7. Next activity in the pipeline is spawned

When an activity is assigned to a human:

  1. Control plane creates an activities record with assignee_type: 'human'
  2. DM Portal shows it in the assignee’s queue
  3. Human completes the activity (approves, edits, rejects)
  4. Activity status → completed or rejected
  5. Next activity spawned (or previous re-triggered on rejection)

© 2026 Leadmetrics — Internal use only