Projects

What I've actually built, not a skills list.

Why I built this

Anyone can list "n8n" and "Salesforce" on a resume. I wanted a page that shows the work instead of describing it, so I gave myself a real n8n instance, a real Salesforce org, and a fictional company to build for — then committed to one new automation a day. Nothing here is staged after the fact: every workflow below ran end-to-end against live data, and the screenshots are from the actual runs.

Turning Buying Signals Into Outreach

~3 min read

A rep drops a buying signal in Slack; Claude drafts two ready-to-send outbound sequences in seconds, then turns an approval into real Gmail drafts.

n8n Cloud · Slack API · Anthropic Claude (Sonnet 4.6) · Gmail API · n8n Data Tables

The problem

Reps see buying signals constantly — a funding round, an acquisition, a leadership change, a security incident at a target account — but turning a signal into a genuinely differentiated outbound sequence takes more time than most reps have in the moment. So the signal gets a LinkedIn like instead of a sequence, and the window passes.

The build

An AI agent lives in a Slack channel. A rep @mentions it with a plain-language signal — "Paramount just bought Warner Bros." — and Claude classifies the signal type, then drafts two complete, genuinely different 3-touch outbound email sequences: Angle A leads with risk/urgency framing, Angle B leads with a consultative/relationship framing. Both post back into the Slack thread within seconds, grounded in a real company context (below) so the copy references actual products and competitive positioning instead of generic filler.

From there it's an actual review loop, not a one-shot generation. The rep can reply in-thread with a plain instruction — "make angle B shorter" — and the agent edits just that angle using per-thread conversation memory, then reposts only what changed instead of re-flooding the thread with both angles again. When a rep reacts with ✅ on the angle they want, the workflow logs the approval, confirms it in-thread, and splits that angle's three touches into three separate Gmail drafts — each with its own real subject line and no leftover Slack instructions — ready to review and send. Every draft, edit, and approval is logged to an n8n Data Table for an audit trail, and a shared error-handling workflow alerts Slack and logs any node failure automatically.

Rep @mentions signal in Slack Claude classifies + drafts Angle A & B Posted to thread for review Reply to refine (optional) ✅ to approve Logged + 3 Gmail drafts created

Company context

Every draft is grounded in a fictional company I built specifically for this project — Fortavault, Inc., a data protection and security platform — so the agent has real products, industries, and competitors to reason about instead of writing in a vacuum.

Products

  • Fortavault Backup Cloud — cloud backup & disaster recovery
  • Fortavault Shield — ransomware detection, isolation, one-click rollback
  • Fortavault Access — zero-trust endpoint access control
  • Fortavault Insights — compliance & audit reporting (HIPAA, SOC 2, GLBA)

Industries served

Financial Services Healthcare Legal Manufacturing Higher Education Retail/E-commerce Insurance

Competitors & how Fortavault wins

  • Veyron Data (backup/DR) — faster to recover, simpler SaaS-data setup
  • Ashcombe Systems (ransomware detection point solution) — Fortavault pairs detection with backup for one-click recovery
  • Cyphertide (zero-trust access) — better suited for regulated industries needing audit-ready logs
  • Northgate Cloud Security (broad platform incumbent) — more focused roadmap, faster implementation
  • Ridgeline Compliance (compliance reporting point tool) — Fortavault Insights is natively integrated with backup/security data

Live run

The Signal-to-Sequence Translator workflow graph in n8n
The live workflow graph in n8n — dual-angle drafting, reply-to-refine gating, human-in-the-loop approval, and per-touch Gmail draft creation.
Angle A, risk/urgency framing, posted to the Slack thread Angle B, consultative/relationship framing, posted to the Slack thread
A real signal, drafted live: two distinct outbound angles posted back to the rep's Slack thread for review.
Three Gmail drafts created automatically from one approved angle
On approval, each touch becomes its own ready-to-send Gmail draft — real subject line, no leftover Slack instructions.

Impact (measured from live runs)

~15 sec
signal → two drafted angles
3
ready-to-send Gmail drafts per approval
0
manual research or copy-paste steps

Bringing Dead Deals Back to Life

~3 min read

A daily scan flags old lost deals worth a second shot, drafts the re-engagement pitch, and waits for a rep's Slack approval before reopening anything.

n8n Cloud · Salesforce (OAuth2) · Anthropic Claude (Sonnet 4.6) · Slack API (interactive approvals) · n8n Data Tables

The problem

Closed Lost opportunities pile up in Salesforce and get forgotten — even the ones that were lost for reasons that don't hold up six months later. A budget freeze thaws. A champion who left gets replaced by someone inheriting the exact same problem. Nobody has time to manually comb through the Closed Lost list and guess which dead deals are actually worth a second look, so they just stay dead.

The build

A daily scheduled scan queries Salesforce for every Closed Lost opportunity. Claude reviews each one against its stated loss reason and how much time has passed, deciding whether it's worth re-engaging — deliberately instructed to be selective, since most closed-lost deals shouldn't be resurrected. For anything flagged worth resurrecting, Claude drafts a short re-engagement angle grounded in real Fortavault products and competitive positioning, then the workflow sends an interactive Slack approval message for that one deal — reopen it, or skip it.

On approval, the opportunity is reopened in Salesforce (moved back to Qualification, close date pushed out 90 days), a high-priority follow-up task is created with the drafted angle as the task description, and every decision — approved, declined, or skipped — is logged to an n8n Data Table for a full audit trail. When a run flags more than one deal at once, each gets its own independent Slack approval cycle in sequence rather than being bundled into a single wait — a real bug I hit and fixed mid-build, after the first multi-deal run silently dropped the second approval.

Daily Salesforce scan Claude assesses each Closed Lost opp Flagged deals looped one at a time Slack approval per deal Reopened + follow-up task created Logged to Data Table

Assessor system prompt

Same Fortavault context as above, with a different job: be skeptical by default, and only surface deals with a genuine, time-sensitive reason to revisit.

You are a deal-resurrection assessor for Fortavault, Inc., a fictional data protection and security platform (cloud backup, ransomware recovery, zero-trust access, and compliance reporting). Products: Fortavault Backup Cloud, Fortavault Shield (ransomware detection + one-click rollback), Fortavault Access (zero-trust), Fortavault Insights (compliance reporting). Key competitors: Veyron Data, Ashcombe Systems, Cyphertide, Northgate Cloud Security, Ridgeline Compliance. You review Closed Lost opportunities and decide whether it is worth re-engaging the account now. Be selective - most closed-lost deals should NOT be resurrected. Only flag ones where the stated loss reason plausibly could have changed given how much time has passed (e.g. a budget freeze may have thawed, a point-in-time staffing gap may be resolved), or where the deal size clearly justifies a second look. If worth resurrecting, draft a short reengagementAngle: 2-3 sentences a rep could paste into an email or LinkedIn message, referencing the original context and a specific reason to revisit now, grounded in real Fortavault products and competitive positioning where relevant. Respond with a JSON object with exactly three fields: worthResurrecting (boolean), rationale (a 1-2 sentence explanation for the rep, private - not sent externally), and reengagementAngle (the drafted opener text, or an empty string if worthResurrecting is false).

Live run

The Dead Deal Necromancer workflow graph in n8n
The live workflow graph in n8n — Salesforce scan, selective AI assessment, one-at-a-time Slack approval loop, and Salesforce/Data Table logging on every branch.
Two dead-deal resurrection approval messages posted to Slack
Two real flagged deals from a live run — each with the loss reason, Claude's rationale, and a drafted re-engagement opener, waiting on a rep's approve/skip.

Impact (measured from live runs)

5
Closed Lost opps assessed per run
2 of 5
correctly flagged worth resurrecting
$110K
pipeline reopened from approved deals

Support Triage That Knows Who's Asking

~3 min read

An inbound support email gets read by Claude, matched to the right Salesforce account, and either escalated, routed, or quietly queued depending on who sent it and how urgent it actually is.

n8n Cloud · Gmail API · Salesforce (OAuth2) · Anthropic Claude (Sonnet 4.6) · Slack API · n8n Data Tables

The problem

Most support triage treats every inbound ticket the same way: same queue, same SLA clock, same Slack noise, regardless of whether it's a trivial question from a small account or a fleet-wide outage at an enterprise customer mid-renewal. That flattening cuts both ways — teams either over-alert on things that don't matter, or bury a genuinely urgent ticket from a top account in the same pile as a "how do I export this" question.

The build

There's no live customer support inbox connected for this sprint, so the trigger is a scoped Gmail watch that only fires on test emails carrying a demo subject tag — a stand-in for a real support alias. Everything after that runs live: a real Salesforce lookup, a real Claude call, a real Case, a real Slack message. When a matching email lands, a Code node parses out the sender's stated email and the message body, then Salesforce is queried for a matching Contact. If one exists, the linked Account's AnnualRevenue determines a customer tier — Enterprise, Mid-Market, or SMB — computed from real seeded Salesforce data, not a hardcoded label.

Claude reads the email and classifies it on two axes: category (Security Incident, Technical Issue, Billing/Account, or Feature Request/General) and priority (Urgent/High/Normal/Low), grounded in Fortavault's product line so it can tell a real outage report from a feature ask. A routing step then combines category, tier, and Claude's priority into the routing decision: a Security Incident always escalates immediately regardless of tier; an Enterprise account with an urgent issue gets a 1-hour SLA and an @here Slack ping; a low-priority ticket from a small account gets a 2-business-day SLA and no Slack ping at all — it's logged and queued, not dropped. Every ticket gets a real Salesforce Case created either way, and every decision is logged to a Data Table for an audit trail of what got escalated, what got queued, and why.

Support email received Claude classifies category & priority Salesforce tier lookup Routing rules set SLA + escalation Salesforce Case created Slack alert or silent log

Routing logic

Category, tier, and priority don't just stack — they interact:

Security Incident → always High priority, always @here in Slack, 15-minute SLA, regardless of customer tier. Enterprise + Urgent/High → High priority, @here in Slack, 1-hour SLA. Enterprise + Normal/Low → Medium priority, Slack-notified (no @here), 4-hour SLA. Mid-Market + Urgent/High → High priority, Slack-notified, 4-hour SLA. Mid-Market + Normal/Low → Medium priority, Slack-notified, 1-business-day SLA. SMB (or no CRM match) + Urgent/High → High priority, Slack-notified, 4-hour SLA. SMB (or no CRM match) + Normal/Low → Low priority, no Slack ping, 2-business-day SLA — queued and logged, not dropped. A Salesforce Case is created on every path. The only thing that changes is the SLA, the priority written to the Case, and whether Slack gets pinged.

Live run

The Support Triage Concierge workflow graph in n8n
The live workflow graph in n8n — Gmail trigger, Salesforce tier lookup, Claude classification, combined routing logic, Case creation, and the Slack/silent-queue branch.
An @here Slack alert for an urgent Enterprise-tier support case
A real run: an Enterprise account reporting two nights of failed backups with no recovery point — classified Urgent, routed to a 1-hour SLA, and posted with an @here ping.
The Salesforce Case created from the triaged support email
The real Salesforce Case created from that same run, with the AI's category, priority, and rationale written into the description.

Impact (measured from live runs)

2 of 2
real tickets routed to the correct tier and SLA
1 hr vs 2 days
SLA spread between Enterprise/urgent and SMB/low routing
0
tickets silently dropped — every ticket gets a Salesforce Case