All projects

Talent Platform & AI Content Pipeline.

Inheriting a live site, and making it write its own content.

A developer-hiring platform needed more pages that rank and leads the sales team could trace. As its only active developer since April 2026, I added an AI content pipeline, a lead funnel with full attribution and 80 programmatic landing pages.

Role
Sole developer · Softaims
Team
Solo (took over from an earlier team)
Time
Since Apr 2026
Stack
Next.js · Strapi · OpenAI · PostgreSQL
Access
Private: company platform
  • 130 / 131
    commits as sole developer
  • 80
    programmatic landing pages
  • 7,500
    line app given sync, without a rewrite

Key decisions.

  1. Case studies generate on request, never on save

    I made case-study generation a run someone starts on purpose, instead of generating on save, where one edit could trigger an accidental spend. Research uses the Responses API with web search, writing and review are two cheaper Batch API cycles, and editors approve the result in the CMS.

    AI as a batch jobsimplified · example values
    Editorsaved · no AIcallResearchweb searchWritebatch cycle 1ValidateZod schemaReviewbatch cycle 2CMS draftneedsapproval
    1. Save: Saving a case study never calls the AI. I removed generation on save, so one edit can't trigger a spend.
    2. Research: A run is started on purpose from the CMS. Research comes first, on the Responses API with live web search.
    3. Write: Writing goes through the cheaper Batch API: the first batch cycle.
    4. Validate: The written draft is checked against a Zod schema before it moves on.
    5. Review: Then an independent review runs as a second batch cycle.
    6. Approve: It lands in the CMS as a draft. An editor approves it before anything goes live.
    7. Re-run: Running it again is safe: a second run is refused while one is active, a published case study is skipped, and a failed batch is retried at its own stage without redoing research.

    Save. Saving a case study never calls the AI. I removed generation on save, so one edit can't trigger a spend.

  2. Pages from data, not by hand

    I generate the service and location pages from structured data, with schema markup and automated link checks, instead of building them by hand, so 80 landing pages stay consistent as the list grows.

    Pages from datasimplified · example values
    Structureddataservices,placesservices8cities42areas30Templates+ schemamarkupLandingpages80, generatedLinkcheckerautomated
    1. Data: Services and locations live as structured data, not as hand-built pages.
    2. Generate: Templates turn every row into a page with structured data: 8 pillar pages, 42 city pages and 30 neighbourhood pages, with ratings markup on the city and service pages.
    3. Check: An automated checker verifies the links between technologies and services.
    4. Grow: Adding a city is one new data file of local copy. Its page, schema, sitemap entry and links follow from the templates.

    Data. Services and locations live as structured data, not as hand-built pages.

  3. Offline sync around the app, not a rewrite

    I added accounts and sync through a local-first IndexedDB layer and an upload queue, instead of rewriting a 7,500-line app. The newest edit wins, deletions are flagged, and nothing talks to the server while idle.

    Sync around the app, not a rewritesimplified · example values
    Browser app7.5k lines,as isIndexedDBlocal firstUploadqueuehook layerServeraccount syncOtherdevicesame accountsave
    1. Edit: Every change is saved locally first, through a hook layer around the existing app. No rewrite.
    2. Queue: The change is queued for upload.
    3. Sync: When the user has an account and is online, the queue uploads it.
    4. Newest wins: Another device sends an older edit to the same item. The newest edit wins.
    5. Delete: Deleting flags the item instead of erasing it.
    6. Idle: And while the app sits idle, nothing talks to the server.

    Edit. Every change is saved locally first, through a hook layer around the existing app. No rewrite.

Background

The platform helps companies hire vetted developers: it ranks for searches like “hire React developers in [city]”, turns that traffic into leads, and books sales calls. Other developers built it from 2023; from April 2026 I took over the site and the Strapi CMS behind it as the only active developer on both.

My job was to make it grow without breaking what already worked: more pages that rank, content that doesn't depend on someone writing it by hand, and a lead funnel that tells the sales team where every lead came from.

How it was built

The AI content pipeline

Case studies are generated in three idempotent stages: research with the OpenAI Responses API and live web search, then writing and an independent review as two Batch API cycles, with a polling service in between. The written draft is checked against a Zod schema before it's reviewed; a case study can target a city or state, and lands in Strapi as a draft for an editor to approve, where a stricter schema checks it again. I removed case-study generation on save, so saving a case study can't trigger an accidental spend, made the pipeline recover drafts from orphaned entries, and wrote a “doctor” script that explains why a case study isn't live yet and clears the cache. The same batch pattern also powers job templates and podcasts.

Programmatic SEO

I built a services hub with 8 data-driven pillar pages, plus 42 city and 30 neighbourhood landing pages for mobile app development: 80 in all, each with structured data, and aggregate ratings on the city and service pages. Around them: split sitemaps, a “local developers” strip ranked city → state → country, and an automated checker for links between technologies and services. Hero images are self-hosted WebP with blur placeholders.

Lead capture

I reworked the funnel around what the sales team needs: a work-email gate with an opt-in for personal addresses, alerts when a form is started but not finished, the existing UTM tracking extended with entry-page and full-journey attribution, phone validation with a country guess from location, and a password-protected admin view of every lead's journey.

Offline sync, without a rewrite

The site's free productivity tool was a 7,500-line browser app. I added optional accounts and sync across devices around it rather than rewriting it: data is saved locally first, an IndexedDB hook layer queues changes for upload, the newest edit wins, deletions are flagged instead of erased, and nothing talks to the server while the app is idle. The sync API is rate-limited and size-capped, and a device with a wrong clock can't lock a record.

My role

From April 2026 I've been the only active developer on both the site and its Strapi CMS (130 of the site's 131 commits in that period), covering features, content models, CMS plugins, tests and deployments.

Highlights
  • Sole developer of an established Next.js platform and its Strapi CMS
  • Three-stage AI case-study pipeline: web-search research → batch writing → batch review
  • Idempotent, Zod-validated AI output, approved by editors in the CMS
  • 8 service pillar pages plus 72 city and neighbourhood landing pages, all with structured data
  • Lead attribution from entry page to submission, with alerts for partly completed forms
  • Offline-first sync across devices for a 7,500-line browser app, without a rewrite
  • CMS lifecycle hooks that sync content to the site with retries and backoff
  • 15 test files covering the pipeline, attribution, journeys and sync
Full stack

Frontend

Next.js · React · Tailwind CSS

Backend & data

Prisma · PostgreSQL · Strapi 5 · Zod · Better Auth · Dexie / IndexedDB

AI

OpenAI Responses API · OpenAI Batch API

Infra

AWS EC2 · AWS RDS · PM2 · GitHub Actions

Want to talk about this build?

I'm open to remote full-time roles and freelance work, and happy to walk through the decisions behind it.