Maestro

Pipelines

A pipeline is a named bucket of contacts inside Maestro. Every contact and every activity belongs to exactly one pipeline. Pipelines are how you keep different audiences separate, run A/B tests of a score, and eventually track work that isn’t outreach.

What a Pipeline is (and isn’t)

A Pipeline is purpose-built for agent-driven work. It owns the contact and activity data the agents create — every action every agent took, timestamped and joinable back to the run that produced it. That’s what makes attribution clean: when you ask “did Maestro work?”, the answer comes from the Pipeline’s own record, not from a system someone else also writes to.

Maestro doesn’t replace your CRM. It complements it. Export to CSV any time, forward stage changes to a webhook, and pipe data into HubSpot or Salesforce in 20 minutes with Zapier or n8n if you want one-way sync. The canonical record of what the agents did lives in Maestro; the rest of your stack does what it’s already good at.

Workspace → pipelines → contacts

workspace (one per install in v1)
  └── pipelines (many)
        └── contacts (many, unique by email *within a pipeline*)
              └── activities (many, every agent action timestamped)

A workspace can have many pipelines. The same person — same email address — can exist in two pipelines without conflict. That matters because real ICPs overlap: a “VP Engineering at series-A SaaS” lead might also belong in your “developer-tools-specifically” test pipeline. Forcing global email uniqueness would either drop one of them or merge them in a way that destroys the per-pipeline experiment.

Companies, by contrast, are workspace-scoped (de-duped by domain). The same company can be a target across pipelines without creating duplicate company records.

Pipeline kinds

Every pipeline has a kind that tells Maestro what shape its data takes:

KindStatusHolds
outreachShipping in v1Contacts + companies + activities. The B2B SaaS Outbound score writes here.
contentReservedWill hold blog drafts and editorial state when the content score ships.
seoReservedWill hold tracked keywords and rank deltas when the SEO score ships.

The content and seo kinds exist on the schema so the column doesn’t need a future migration, but no agents or UIs handle them in v1. Stick to outreach until those scores ship.

When to create a new pipeline

The two strong signals are:

  • Different ICPs. “Cold leads · SaaS” and “Cold leads · Healthcare” should be separate pipelines. They probably have different opener templates, different stage definitions over time, and different success metrics.
  • A/B testing a score. Run pipeline A with the original opener, pipeline B with the variant. Same contacts in both pipelines is fine. Compare reply rates side by side.

The weak signals — don’t split pipelines for these — are:

  • Different time periods. Stages already track this; date-filter the same pipeline.
  • Different agents. A score can use multiple agents writing to the same pipeline (cold-leads + reply-triage do this).
  • Cosmetic separation. Use labels or tags within a pipeline (when those ship) rather than a whole new pipeline.

Stages

Stages are defined per score, not per pipeline. The v1 hero score (B2B SaaS Outbound) defines this stage progression for outreach pipelines:

new → enriching → ready → contacted → replied → triaged → booked

Plus terminal states disqualified and unsubscribed.

When a second hero score ships (say, a content score), it’ll define its own stage set for content pipelines. The Pipeline UI reads the stage list from the score the pipeline is bound to.

Activities

Every agent action against a contact is logged as an activitycreated, enriched, contacted, replied, triaged, booked, note, bounced, unsubscribed, excluded. Activities are joinable to the run + step that produced them, so the contact detail timeline can show “this email was sent at 14:02 by run r_a8f3e1c, step 4 of cold-leads.”

Manual activities (notes, status changes you make in the UI) work the same way but with null run_id and step_id.

Real-time

Pipeline views stream live. Insert or update a contact or activity in any pipeline and the UI updates within ~1 second without a refresh. The mechanism is Postgres LISTEN/NOTIFY → SSE → TanStack Query cache invalidation. See the SSE hub in apps/api/src/sse.ts if you want the gritty details.

Each pipeline has its own SSE channel, so if you have multiple pipelines open in different tabs they each only receive their own events.

Export

Two mechanisms in v1:

  • CSV export — contacts and activities, filterable by stage and date range.
  • Webhooks — “contact reached stage X” fires a configured URL with the contact payload. Wire it up to Zapier or n8n in 20 minutes for one-way sync into HubSpot, Salesforce, or whatever you already use.

Export is deliberately first-class. Your data is yours; moving it should be easy.

What pipelines are not

  • They are not smart-folder views over a single contact list. Each pipeline is a real bucket — a contact in pipeline A is a different row than the same email in pipeline B.
  • They are not a workflow engine. The score is the workflow; the pipeline is the data sink.
  • They are not a project-management tool. There are no tasks, no notes-as-objects, no calendar entries (in v1). Activities exist, but they record agent actions — they aren’t your to-do list.
  • Scores — the agent compositions that write to pipelines.
  • Skills and tools — what those agents use under the hood.
  • Secrets — how the credentials those skills need are stored.