nodestash

Building a Sales Pipeline

End-to-end guide to creating a pipeline, adding stages, and tracking deals

This guide walks you through setting up a complete sales pipeline — from creating the pipeline and stages to tracking deals as they progress.

1. Create a Pipeline

Start by defining your sales process as a pipeline with stages:

curl -X POST https://api.nodestash.io/v1/pipelines \
  -H "Authorization: Bearer $NODESTASH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "B2B Sales",
    "description": "Standard B2B sales process",
    "stages": [
      { "name": "Lead", "position": 0, "type": "open" },
      { "name": "Qualified", "position": 1, "type": "open" },
      { "name": "Proposal Sent", "position": 2, "type": "open" },
      { "name": "Negotiation", "position": 3, "type": "open" },
      { "name": "Closed Won", "position": 4, "type": "won" },
      { "name": "Closed Lost", "position": 5, "type": "lost" }
    ]
  }'
const pipeline = await client.pipelines.create({
  name: 'B2B Sales',
  description: 'Standard B2B sales process',
  stages: [
    { name: 'Lead', position: 0, type: 'open' },
    { name: 'Qualified', position: 1, type: 'open' },
    { name: 'Proposal Sent', position: 2, type: 'open' },
    { name: 'Negotiation', position: 3, type: 'open' },
    { name: 'Closed Won', position: 4, type: 'won' },
    { name: 'Closed Lost', position: 5, type: 'lost' },
  ],
})

// Store stage IDs for later use
const stages = Object.fromEntries(
  pipeline.stages.map((s) => [s.name, s.id])
)

Every pipeline needs at least one won and one lost stage. These stages trigger automatic closed_at timestamps on deals.

2. Create a Company and Contact

Set up the account you're selling to:

# Create the company
curl -X POST https://api.nodestash.io/v1/companies \
  -H "Authorization: Bearer $NODESTASH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "TechCorp",
    "domain": "techcorp.io",
    "industry": "Technology",
    "size": "201-500"
  }'

# Create the contact (use company_id from above)
curl -X POST https://api.nodestash.io/v1/contacts \
  -H "Authorization: Bearer $NODESTASH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "sarah@techcorp.io",
    "first_name": "Sarah",
    "last_name": "Chen",
    "title": "Head of Engineering",
    "company_id": "co_abc123"
  }'
const company = await client.companies.create({
  name: 'TechCorp',
  domain: 'techcorp.io',
  industry: 'Technology',
  size: '201-500',
})

const contact = await client.contacts.create({
  email: 'sarah@techcorp.io',
  first_name: 'Sarah',
  last_name: 'Chen',
  title: 'Head of Engineering',
  company_id: company.id,
})

3. Create a Deal

Add a deal to your pipeline, linking it to the contact and company:

curl -X POST https://api.nodestash.io/v1/deals \
  -H "Authorization: Bearer $NODESTASH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "TechCorp Annual License",
    "value": 75000,
    "currency": "EUR",
    "pipeline_id": "pp_abc123",
    "stage_id": "ps_lead_id",
    "contact_id": "ct_abc123",
    "company_id": "co_abc123",
    "expected_close_date": "2026-06-30",
    "tags": ["enterprise", "annual"]
  }'
const deal = await client.deals.create({
  title: 'TechCorp Annual License',
  value: 75000,
  currency: 'EUR',
  pipeline_id: pipeline.id,
  stage_id: stages['Lead'],
  contact_id: contact.id,
  company_id: company.id,
  expected_close_date: '2026-06-30',
  tags: ['enterprise', 'annual'],
})

4. Move the Deal Through Stages

As the deal progresses, update its stage:

# Move to Qualified
curl -X PATCH https://api.nodestash.io/v1/deals/dl_abc123 \
  -H "Authorization: Bearer $NODESTASH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "stage_id": "ps_qualified_id" }'

# Move to Proposal Sent
curl -X PATCH https://api.nodestash.io/v1/deals/dl_abc123 \
  -H "Authorization: Bearer $NODESTASH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "stage_id": "ps_proposal_id" }'
// Move to Qualified
await client.deals.update(deal.id, {
  stage_id: stages['Qualified'],
})

// Move to Proposal Sent
await client.deals.update(deal.id, {
  stage_id: stages['Proposal Sent'],
})

5. Log Activities Along the Way

Track every interaction:

# Log a discovery call
curl -X POST https://api.nodestash.io/v1/activities \
  -H "Authorization: Bearer $NODESTASH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "call",
    "subject": "Discovery call with Sarah",
    "body": "Discussed their current tooling and pain points. Team of 50 engineers.",
    "contact_id": "ct_abc123",
    "deal_id": "dl_abc123",
    "is_done": true,
    "metadata": { "duration_seconds": 2400 }
  }'

# Create a follow-up task
curl -X POST https://api.nodestash.io/v1/activities \
  -H "Authorization: Bearer $NODESTASH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "task",
    "subject": "Send proposal to Sarah",
    "deal_id": "dl_abc123",
    "contact_id": "ct_abc123",
    "due_date": "2026-03-10T09:00:00Z"
  }'
// Log a discovery call
await client.activities.create({
  type: 'call',
  subject: 'Discovery call with Sarah',
  body: 'Discussed their current tooling and pain points. Team of 50 engineers.',
  contact_id: contact.id,
  deal_id: deal.id,
  is_done: true,
  metadata: { duration_seconds: 2400 },
})

// Create a follow-up task
await client.activities.create({
  type: 'task',
  subject: 'Send proposal to Sarah',
  deal_id: deal.id,
  contact_id: contact.id,
  due_date: '2026-03-10T09:00:00Z',
})

6. Close the Deal

When the deal is won, move it to the "Closed Won" stage:

curl -X PATCH https://api.nodestash.io/v1/deals/dl_abc123 \
  -H "Authorization: Bearer $NODESTASH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "stage_id": "ps_won_id" }'
await client.deals.update(deal.id, {
  stage_id: stages['Closed Won'],
})

// closed_at is automatically set!
const closedDeal = await client.deals.get(deal.id)
console.log(`Deal closed at: ${closedDeal.closed_at}`)

7. View Your Pipeline

Get an overview of all deals in your pipeline:

curl "https://api.nodestash.io/v1/deals?pipeline_id=pp_abc123&sort=-value" \
  -H "Authorization: Bearer $NODESTASH_API_KEY"
let totalValue = 0

for await (const deal of client.deals.listAll({
  pipeline_id: pipeline.id,
  sort: '-value',
})) {
  console.log(`${deal.title}: ${deal.value} ${deal.currency} (${deal.stage_id})`)
  totalValue += Number(deal.value ?? 0)
}

console.log(`Total pipeline value: ${totalValue}`)

Next Steps

On this page