Nonso Ezeoma Business Data Analyst · Automation
Automation portfolio

AI automations I built, ran, and debugged end‑to‑end.

I turn slow, manual work into pipelines that run on their own. Below are the automations I designed, wired up, and fixed when they broke — from a Node.js job-search robot to a no-code lead-routing workflow. Each one reads real data, makes a decision with an LLM, and acts.

4
automations built end-to-end
3
LLMs wired in — Claude, Llama, Gemini
6+
APIs integrated (Google, Slack, job boards)
100%
runs unattended on a schedule
Filter

The problem

Job hunting means checking dozens of company career pages every day and hand-copying every application into a tracker. It is slow, and good openings slip through before you see them.

What I built

A Node.js service that does the whole loop unattended: pull openings, judge fit with an LLM, tailor the CV, record the result, and email me a short digest — twice a day via Windows Task Scheduler.

How it works

01 · FETCH
Pull openings
Greenhouse, Lever, Ashby, Workable APIs
02 · FILTER
Cut the noise
Keyword + location rules, free of cost
03 · SCAN
Read inbox
Gmail API, last 14 days of leads
04 · SCORE
Judge fit
Claude API — strong / medium / weak + reason
05 · TAILOR
Draft CV
Reworks my real CV to the role (docx)
06 · DELIVER
Log + notify
Writes Google Sheet, emails a digest

Results

~400 jobs / run
fetched and screened automatically across 4 job-board platforms
manual daily search
2× a day, unattended
scheduled runs, morning and evening
generic CV
role-tailored draft
generated only for strong matches, from my real CV

An engineering problem I fixed

Debugging · silent failure

Every job scored “weak,” identically. I forced the raw API response to print and found the real cause: an invalid model name returning a 404 that a catch-block was swallowing into a default score. Fixed the model string, added a startup access-check, and made errors surface loudly instead of defaulting silently. Along the way I also solved an OAuth redirect-port mismatch, markdown-fenced JSON breaking JSON.parse, an SSL certificate block, and a 7-day auth-token expiry (by publishing the OAuth app).

The problem

When leads arrive by form, a person has to read each one and decide if the sender is a serious buyer. At ten a day that is fine; at two hundred, good customers wait days and go elsewhere. Revenue that was already earned leaks away — and nobody did anything wrong.

What I built

An n8n workflow where an LLM reads each lead and returns a locked JSON verdict — score, reason, and a ready-to-send reply email — then a routing step sends only the hot ones to the channel the sales team already watches.

How it works

01 · IN
New lead
name, company, message
02 · SCORE
AI reads it
Llama 3 → hot/warm/cold, reason, email
03 · PARSE
Unpack answer
split score from reason (JSON)
04 · ROUTE
Traffic cop
Switch — hot / warm / cold, three ways
05 · ALERT
Slack ping
posts name, company, reason, email

The AI's locked answer

The model is forced to reply in one fixed JSON shape, so every step after it can trust the data:

{ "score": "hot | warm | cold", "reason": "one short sentence why", "email_draft": "a friendly 3-sentence reply" }

Seen running

The lead-qualification workflow open in the n8n editor, seven connected nodes with green success ticks
The live workflow in n8n — trigger, field setup, the Groq LLM chain, the Switch router and the Slack node, each with a green tick from a successful run.
A hot lead posted automatically into the Slack #leads channel
The result in the team's Slack #leads channel — a hot lead posted automatically with name, practice, the AI's reason and the email.

Results

hours or days
seconds
time for a hot lead to reach sales
whatever there was time for
100% reviewed
every lead read, none missed
hours a week
≈ none
manual screening time removed
different people guessing
one fixed rule
every lead judged consistently

An engineering problem I fixed

Debugging · invisible bug

The Slack alert kept arriving with the reason blank. After rebuilding the message three times, I found it: one field had been saved with an invisible trailing space in its name — "name " instead of "name" — so the workflow couldn't find the value. I corrected how the fields were read, then hardened the flow with a locked response format and retry-on-failure so one bad message can't break the run.

Also in the full portfolio

AI Agentic Operations n8n
Building, running and fixing AI content agents in a live process, with a human-in-the-loop review step and API integrations — a full “operate the agents” project.
n8nAI agentshuman-in-the-loopAPI integration
Business Decision Dashboard Analytics
Customer-enablement piece: a clear, usable decision tool with documentation, turning an operational workflow into something a non-technical stakeholder can actually run.
DashboardSQLPower BIDocs