Logoazurai
accepting worknext window [oct 2026]

Jonathan Azur

I build automation pipelines and AI-powered workflows — the systems that handle the repeatable work, the overnight runs, and the decisions that used to need a person.

I do not take on projects where nobody on your side owns the credentials. Every handover includes an on-call window and the runbook that goes with it.

systems in production
11
lines of prod code
150,000+
automated tests
460+
email delivery rate
98.6%

Manifest of production systems

11 records / updated 2026-10-31

Every system below is live in a client account, owned by the client, and monitored. Trigger volumes are 30-day averages.

SYS-01STAGED
Compliance-first wallet backend
Payments / fintech
trigger: API + scheduled jobs
stack: TypeScript · Fastify · Postgres · Zod
SYS-02STAGED
Cashback & rewards app for members
Payments / fintech
trigger: user action
stack: Next.js 14 · Clerk · Vercel
SYS-03STAGED
Admin portal for organisations
Payments / fintech
trigger: user action
stack: Next.js · Clerk · PWA
SYS-04STAGED
Travel eSIM store
Payments / fintech
trigger: webhook + daily sweep
stack: Next.js · Postgres · Stripe · eSIM partner API
SYS-05LIVE
Guest data, synced every night
Marketing / CRM
trigger: daily
stack: Python · Vercel Cron · Hostaway API · Airtable
SYS-06LIVE
Hands-off email campaigns that can't double-send
Marketing / CRM
trigger: every 5 min
stack: Python · FastAPI · Resend · Postgres/Airtable
SYS-07LIVE
Email results that update themselves
Marketing / CRM
trigger: webhook
stack: Python · Svix · Postgres/Airtable
SYS-09LIVE
Internal CRM: lead to campaign in one place
Marketing / CRM
trigger: form submit
stack: Next.js · Supabase/Postgres · Clerk · S3
SYS-08LIVE
Lead lists that build themselves
Lead generation
trigger: weekly + daily
stack: Python · OpenStreetMap Overpass · SQLite · FastAPI
SYS-10LIVE
Assessment-to-report pipeline
AI pipelines
trigger: form submit
stack: Python · FastAPI · Claude API · PostgreSQL
SYS-11LIVE
AI content generation for campaigns
AI pipelines
trigger: on demand
stack: Python · OpenAI · FastAPI · Airtable

Run records

3 of 11 shown
run record / sys-06

Hands-off email campaigns that can't double-send

live
trigger

A scheduler fires every 5 min. It checks which campaigns are due, builds the send list, removes duplicates and unsubscribes, and dispatches in batches. No one presses send.

outcome
1,222

emails sent in the first 3 months: 98.6% delivered, bounced, zero double-sends.

flow
stack
PythonFastAPIResendPostgreSQLAirtable
constraint accepted

The suppression list is checked at send time, not at list-build time, so someone who unsubscribes between build and send is still protected. This adds one query per batch but removes the class of bug entirely.

failure modes
HANDLEDscheduler fires twice
Each batch carries a unique send key. The email provider rejects a duplicate key, so nobody is emailed twice regardless of how many times the job runs.
HANDLEDunsubscribe arrives between build and send
Suppressions are checked at send time, not at list-build time. A contact who opts out minutes before a batch goes out is removed before the batch fires.
HANDLEDsender domain not verified
The domain check runs before any send attempt. An unverified domain blocks the campaign entirely rather than letting it fail mid-way.
RETRYsend stalls mid-batch
After 30 minutes with no completion, the campaign is marked Failed and surfaced in the dashboard. It is never left half-sent and silent.
run record / sys-10

Assessment-to-report pipeline

live
trigger

A form submission carrying assessment responses fires a webhook. The pipeline parses the fields, builds the prompt, calls the model, validates the output against a strict schema, and delivers a formatted report — no human in the loop between submission and inbox.

outcome
< 90 sec

end-to-end, from form submission to formatted report in the client's inbox. Before: 2–3 hr manual of manual interpretation per submission.

flow
stack
PythonFastAPIClaude APIPostgreSQL
constraint accepted

The model output is validated against a strict schema before the report renders. A response that fails the schema triggers one retry with a narrower prompt; if the second attempt also fails, the submission is written to a dead-letter queue and flagged for manual review. No malformed report is ever sent.

failure modes
RETRYLLM output fails schema validation
The parser retries once with a tighter prompt. On a second failure the submission is flagged for manual review — no broken report is sent.
HANDLEDAPI rate limit hit mid-batch
Requests are queued with exponential backoff. The batch finishes late but complete — no submission is dropped.
HANDLEDform fields missing or malformed
Field validation runs before the prompt is built. An incomplete submission is rejected early with a structured error rather than producing a partial report.
FAILoutput schema fails on second attempt
The submission is written to a dead-letter queue and surfaced in the dashboard. A human reviews and re-triggers — the pipeline does not silently drop it.
run record / sys-04

Travel eSIM store

live
trigger

A customer completes checkout and a Stripe webhook fires. A polling fallback and a daily sweep catch any notification the webhook missed.

outcome
357

automated tests — 262 unit · 95 integration unit, against a real database — prove the full purchase flow before anything goes live.

flow
stack
Next.jsPostgreSQLStripeeSIM partner API
constraint accepted

When provider credentials are absent, the store runs on a built-in simulator — the full purchase flow works and every test passes before a single dollar is at risk. The simulator is not a mock; it is a separate environment the partner provides.

failure modes
HANDLEDprovider credentials missing
The store falls back to the simulator automatically. No money moves and no order breaks — the flow continues as normal for testing.
HANDLEDpayment notification arrives with bad signature
The signature is verified before any order change. An invalid payload is rejected immediately; nothing is updated.
RETRYfulfilment notification missed
A polling job checks every 5 minutes and a daily sweep covers anything still open after 24 hours. Orders do not stay stuck.
HANDLEDcustomer cancels unused eSIM
Unused eSIMs can be cancelled and refunded through the same purchase flow. The refund goes back via Stripe; the eSIM is marked inactive with the partner.

Claude Code, as actually run

3 builds / session excerpt 2026-09

I build AI-powered features with the same discipline I apply to automation — scope defined before the session opens, every output reviewed, production access mine. Two real builds and one session showing how it is actually driven.

shipped with claude code
SYS-10

Assessment-to-report pipeline

replaced
Manual interpretation of each assessment submission — 2 to 3 hours per report, done by a person reading responses and drafting output by hand.
built
An end-to-end pipeline: form webhook → field parser → prompt builder → LLM call → schema validator → formatted report delivered by email. The retry logic and dead-letter queue handle the cases where the model output doesn't meet the schema.
now
Reports are in the client's inbox in under 90 seconds. The manual step is gone; the human review step is reserved for the small percentage that fail schema validation twice.
stack
Claude CodePythonFastAPIClaude APIPostgreSQL

Company site with admin CMS and SEO tracking

replaced
A developer in the loop for every content change, and no active SEO monitoring — ranking drops and audit issues went unnoticed until traffic had already slipped.
built
The site itself, plus an admin dashboard for adding and editing content directly, and an SEO module that pulls ranking data and audit results into the same view as site analytics.
now
Pages and copy changes go live without a developer touching them, and rankings and audit issues are read from one dashboard instead of going unwatched between reports.
stack
Claude CodeNext.jsAdmin CMSSEO platform APIAnalytics
SYS-07

Marketing campaign analytics dashboard

replaced
Campaign numbers pulled out of several ad and analytics platforms by hand, costing hours before any reporting or decision could start.
built
A dashboard that pulls campaign and analytics data into one view on a schedule, replacing the manual export step with a pipeline that stays current on its own.
now
Performance is visible without assembling it first, so the reporting step no longer sits between the data and the decision.
stack
Claude CodeAnalytics APIsAutomation pipelineDashboard UI
one session, abbreviated

Building the LLM response parser and its validation layer for the assessment-to-report pipeline. Scope was one module and its tests — the API layer, prompt templates, and anything touching live data stayed with me.

claude code / repo: assessment-interpreterbranch: feat/llm-response-parser
$ claude
prompt
Add an LLM response parser for the assessment pipeline.
Parse the model output against the report schema in src/schema/report.py.
Add a validator that retries once with a narrower prompt if the schema check fails.
Write unit tests for the happy path and both failure modes.
Do not touch the API layer or the prompt templates.
plan
  1. 01Read src/schema/report.py to understand the output contract
  2. 02Draft src/parser/llm_response.py — parse, validate, retry hook
  3. 03Add src/parser/validators.py — schema check and typed error classes
  4. 04Write tests/test_llm_response.py — happy path, schema fail, retry path
  5. 05Run pytest and mypy; surface any type errors for review
files touched
src/parser/llm_response.py+82-0
src/parser/validators.py+34new
tests/test_llm_response.py+67new
commands
pytest tests/test_llm_response.py14 passed (3.1s)
mypy src/parser/ok
diff summary
3 files, +183 −0
left to me
Prompt template changes — model behaviour is mine to tune, not the agent's.
The retry prompt is hand-written; the agent drafted the hook that calls it.
Production API key rotation and any change to live assessment data.
scoping

One session, one module or one route. The prompt names the file and the blast radius. Anything vaguer produces work I have to read twice.

parallel tasks

Independent packages get separate sessions rather than subagents inside one. Two agents editing the same module costs more review time than it saves.

review gates

Typecheck and module tests must pass before I read the diff, and I read every line of any migration and every new dependency.

not delegated

Production credentials, migration timing, deletes, and anything that sends a message to a real customer. The agent proposes those; I run them.

Contact

azurjonathan@gmail.com

Replies inside one business day, Asia/Manila. Send the trigger, the systems involved, and what currently breaks — that is enough for me to tell you whether it is worth building.

I take two builds at a time. If both slots are full I will say so in the reply rather than start late.

also

jonathan azur / automation · AI delivery