End-to-end workflow examples
// input: Calendly webhook data (name, email, company)
const booking = input[0];
// Step 1: Enrich the person
const contacts = await canvas.contacts.enrich({
people: [{ email: booking.email, name: booking.name }],
});
const contact = contacts[0];
// Step 2: Research their company
const companyInfo = await canvas.ai.research({
prompt: "What does this company do? What's their size and industry?",
data: { company: booking.company, email: booking.email },
});
// Step 3: Score and qualify
let score = 0;
if (contact.title?.match(/CEO|Founder|VP|Director|Head/i)) score += 30;
if (companyInfo.includes("SaaS") || companyInfo.includes("B2B")) score += 20;
if (contact.company_size > 50) score += 20;
const qualified = score >= 40;
// Step 4: Return with qualification
return [{
...contact,
company_research: companyInfo,
score,
qualified,
routing: qualified ? "ae-calendar" : "nurture-sequence",
}];
// Step 1: Find target companies
const companies = await canvas.companies.find({
industries: ["SaaS", "Fintech"],
employeeCountMin: 50,
employeeCountMax: 500,
countries: ["United States"],
limit: 50,
});
// Step 2: Find decision makers
const people = await canvas.people.search({
companies,
titles: ["CTO", "VP Engineering", "Head of Engineering"],
seniorities: ["c-suite", "vp", "head"],
maxPerCompany: 2,
});
// Step 3: Get contact info
const contacts = await canvas.contacts.enrich({ people });
// Step 4: Personalize each contact
for (const contact of contacts) {
contact.ice_breaker = await canvas.ai.research({
prompt: "Write a 1-sentence personalized opener referencing their company or role.",
data: contact,
});
}
return contacts;
// input: CSV of event attendees (name, email, company)
const attendees = input;
// Enrich all attendees
const enriched = await canvas.contacts.enrich({
people: attendees.map(a => ({ email: a.email, name: a.name })),
});
// Score and segment
for (const contact of enriched) {
let score = 0;
// Title scoring
if (contact.title?.match(/C-Level|VP|Director/i)) score += 40;
else if (contact.title?.match(/Manager|Lead/i)) score += 20;
// Company size scoring
if (contact.company_size > 200) score += 30;
else if (contact.company_size > 50) score += 15;
contact.score = score;
contact.segment = score >= 50 ? "hot" : score >= 25 ? "warm" : "nurture";
}
// Sort by score
enriched.sort((a, b) => b.score - a.score);
canvas.log(`Hot: ${enriched.filter(c => c.segment === "hot").length}`);
canvas.log(`Warm: ${enriched.filter(c => c.segment === "warm").length}`);
return enriched;
// input: list of target companies
for (const company of input) {
// Company overview
company.overview = await canvas.ai.research({
prompt: "2-sentence description: what they do and who they sell to.",
data: company,
});
// Recent news
company.news = await canvas.ai.research({
prompt: "Top 3 news items from the last 6 months. Include dates.",
data: company,
model: "sonar-deep-research",
});
// Potential pain points
company.pain_points = await canvas.ai.research({
prompt: "Based on their industry and size, what challenges do they likely face?",
data: company,
});
canvas.log(`Researched: ${company.name}`);
}
return input;
// input: LinkedIn post URLs to monitor
const postUrls = input.map(i => i.url);
// Get people who reacted
const reactions = await canvas.linkedin.reactions({
urls: postUrls,
maxPerPost: 100,
});
// Filter to relevant titles
const relevantPeople = reactions.filter(person =>
person.title?.match(/CEO|CTO|VP|Director|Head|Manager/i)
);
// Enrich with contact info
const contacts = await canvas.contacts.enrich({
people: relevantPeople,
});
// Add engagement context
for (const contact of contacts) {
contact.source = "linkedin-engagement";
contact.engaged_with = contact.post_url;
}
canvas.log(`Found ${contacts.length} engaged decision makers`);
return contacts;
// input: CRM export with potentially stale data
const records = input;
// Basic sanity check for emails (syntax only)
const candidates = records.filter(r => r.email && r.email.includes("@"));
// Enrich contacts to fill missing phone / titles
const enriched = await canvas.contacts.enrich({
people: candidates.map(r => ({
email: r.email,
name: r.name,
company: r.company,
})),
});
const enrichedByEmail = new Map(enriched.map(c => [c.email, c]));
const seen = new Set();
const cleaned = [];
for (const record of candidates) {
if (seen.has(record.email)) continue;
seen.add(record.email);
const contact = enrichedByEmail.get(record.email) || {};
const merged = { ...record, ...contact };
// Enrich missing company data
if (!merged.company_size || !merged.industry) {
const info = await canvas.ai.research({
prompt: "Return JSON: { employee_count: number, industry: string }",
data: { company: merged.company, website: merged.website },
});
try {
const parsed = JSON.parse(info);
merged.company_size = merged.company_size || parsed.employee_count;
merged.industry = merged.industry || parsed.industry;
} catch (e) {}
}
cleaned.push(merged);
}
canvas.log(`${cleaned.length} of ${records.length} records ready`);
return cleaned;
// Step 1: Find target companies
const companies = await canvas.companies.find({
industries: ["Healthcare IT", "Biotech"],
employeeCountMin: 100,
employeeCountMax: 1000,
countries: ["United States"],
limit: 25,
});
// Step 2: Find decision makers
const people = await canvas.people.search({
companies,
titles: ["VP Sales", "Head of Partnerships", "Chief Revenue Officer"],
seniorities: ["vp", "c-suite", "head"],
maxPerCompany: 2,
});
// Step 3: Enrich with contact info
const contacts = await canvas.contacts.enrich({ people });
// Step 4: Score and push to Salesforce
for (const contact of contacts) {
const score = await canvas.ai.research({
prompt: "Score 1-100 based on title seniority, company size, and industry fit for a sales automation product",
data: contact,
resultType: "Number",
});
// Push every enriched lead to Salesforce
await canvas.mcp.call("salesforce", "SALESFORCE_LEADS_CREATE", {
first_name: contact.first_name,
last_name: contact.last_name,
company: contact.company,
email: contact.email,
title: contact.title,
lead_source: "Canvas Outbound",
description: `Score: ${score}`,
});
// Alert the team on hot leads
if (score >= 80) {
await canvas.mcp.call("slack", "SLACK_SEND_MESSAGE", {
channel: "#sales-alerts",
text: `🔥 Hot lead: ${contact.full_name} (${contact.title} at ${contact.company}) — Score: ${score}`,
});
}
}
canvas.log(`Synced ${contacts.length} leads to Salesforce`);
return contacts;