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
- Add Custom Fields to track deal-specific data (e.g., contract type, renewal date)
- Set up Webhook Integration to get notified when deals change stage
- Build a dashboard using the TypeScript SDK