Headlessly

Lead to Revenue

The complete pipeline — Lead capture, qualification, deal close, customer creation, subscription activation.

The lead-to-revenue workflow is the foundational cross-domain journey in headless.ly. It connects CRM and Billing into a single automated pipeline where a website visitor becomes a paying subscriber without manual handoffs.

Full Flow

import { Contact, Deal } from '@headlessly/crm'
import { Customer, Subscription } from '@headlessly/billing'

// 1. Capture lead
const contact = await Contact.create({
  name: 'Alice Chen',
  email: 'alice@acme.dev',
  stage: 'Lead',
  source: 'website',
})

// 2. Qualify
await Contact.qualify({ id: contact.$id })

// 3. Create deal
const deal = await Deal.create({
  name: 'Acme Enterprise',
  value: 48_000,
  contact: contact.$id,
  stage: 'Prospecting',
})

// 4. Close deal
await Deal.win({ id: deal.$id })

// 5. Auto-create customer and subscription via after hook
Deal.won((deal, $) => {
  const customer = $.Customer.create({
    name: deal.name,
    contact: deal.contact,
  })
  $.Subscription.create({
    customer: customer.$id,
    plan: 'plan_Nw8rTxJv',
  })
})

Step-by-Step Breakdown

Step 1: Capture the Lead

A Contact enters the system at the Lead stage. The source field tracks attribution.

import { Contact } from '@headlessly/crm'

const contact = await Contact.create({
  name: 'Alice Chen',
  email: 'alice@acme.dev',
  stage: 'Lead',
  source: 'website',
})
// contact.$id => 'contact_fX9bL5nRd'

Step 2: Qualify the Contact

Qualification transitions the contact from Lead to Qualified. This fires the full verb lifecycle: qualifying (before), qualify (execute), qualified (after).

await Contact.qualify({ id: 'contact_fX9bL5nRd' })
// contact.stage => 'Qualified'

Step 3: Create a Deal

A Deal ties the qualified contact to a revenue opportunity.

import { Deal } from '@headlessly/crm'

const deal = await Deal.create({
  name: 'Acme Enterprise',
  value: 48_000,
  contact: 'contact_fX9bL5nRd',
  stage: 'Prospecting',
})
// deal.$id => 'deal_k7TmPvQx'

Step 4: Win the Deal

Closing the deal triggers the won after-hook, which bridges CRM into Billing.

await Deal.win({ id: 'deal_k7TmPvQx' })
// deal.stage => 'Won'

Step 5: Create Customer and Subscription

The after-hook on Deal.won automatically provisions Billing entities. No human intervention required.

import { Customer, Subscription } from '@headlessly/billing'

Deal.won((deal, $) => {
  const customer = $.Customer.create({
    name: deal.name,
    contact: deal.contact,
  })
  $.Subscription.create({
    customer: customer.$id,
    plan: 'plan_Nw8rTxJv',
  })
})

Event Flow

Each step emits immutable events to the event log:

StepEventPayload
Lead capturecontact.created{ $id: 'contact_fX9bL5nRd', stage: 'Lead' }
Qualificationcontact.qualified{ $id: 'contact_fX9bL5nRd', stage: 'Qualified' }
Deal creationdeal.created{ $id: 'deal_k7TmPvQx', value: 48000 }
Deal closedeal.won{ $id: 'deal_k7TmPvQx', stage: 'Won' }
Customer createdcustomer.created{ $id: 'customer_mR3vYpLx' }
Subscription activatedsubscription.created{ plan: 'plan_Nw8rTxJv' }

MCP Example

An agent can execute this entire workflow through the do tool:

{
  "tool": "do",
  "input": "Create a contact named Alice Chen (alice@acme.dev), qualify them, create a $48k deal called Acme Enterprise, and close it as won"
}

The agent resolves this into the same SDK calls above, executing each step in sequence and letting the after-hooks handle the CRM-to-Billing bridge.

Automation Pattern

The power of this workflow is in the Deal.won after-hook. Register it once and every closed deal automatically provisions a customer and subscription:

// Register once at startup
Deal.won((deal, $) => {
  const customer = $.Customer.create({
    name: deal.name,
    contact: deal.contact,
  })
  $.Subscription.create({
    customer: customer.$id,
    plan: deal.plan ?? 'plan_Nw8rTxJv',
  })
})

This pattern eliminates the integration glue between CRM and Billing. One typed graph, one event system, zero middleware.

On this page