Solo engineer · Feb 2026 – present
Tender
I built Tender solo for a multinational fire-safety client: a multi-tenant platform that ingests UK public-sector tender notices, makes them searchable, and scores fit for each company. 841,433 notices ingested.
- Next.js 16 (App Router)
- React 19
- TypeScript
- PostgreSQL
- Python
- Bruin
- Postgres full-text search
The problem
If you are an SME bidding for UK public-sector work, the opportunities are spread across separate government portals (Find a Tender and Contracts Finder), each with its own format and quirks. Finding the right notices is slow, and once you have them you still have to judge which ones are worth the effort of a bid. The client, a multinational fire-safety company, wanted that whole job collapsed into one place: aggregate everything, make it searchable, and track each opportunity from new through to won or lost.
What I built
I built the product end to end, solo: the data pipeline, the SQL, the Next.js application, auth, multi-tenancy and the AI features.
- A Python and Bruin pipeline that ingests open OCDS tender data into PostgreSQL on a raw, staging and canonical (medallion) pattern. It paginates the government APIs 24 months back, handles rate limits with backoff, sanitises malformed JSON and bulk-upserts. 841,433 tender notices ingested.
- Weighted full-text search over the canonical table, backed by GIN indexes, plus filtering by CPV code, value and status, so a company can narrow hundreds of thousands of notices to the handful that matter.
- AI fit scoring against each company’s profile: a go/no-go assessment with the key requirements and risks, plus drafted buyer-outreach emails. Responses stream to the client over server-sent events and completed summaries are cached per tender to avoid repeat model calls.
- A Kanban pipeline and lightweight CRM so each opportunity carries its bid status, with a Leaflet map for location.
The hard part
Tender value is not a single field. A notice might state it on the tender, or only on its lots, or its contracts, or its awards, and the same opportunity can appear several times as new releases arrive. I resolved value through a fallback chain (tender, then summed lots, then contracts, then awards), deduplicated to the latest release per OCID, and normalised status from inconsistent tags. Getting that flattening right is what makes search and fit scoring trustworthy rather than noisy.
The platform is multi-tenant with schema-per-tenant isolation: provisioning a customer creates its own Postgres schema with its own migrations, and every request sets the search path to that tenant after resolving it from the session. Tenants never share rows.
Status
A working client product with 841,433 tender notices ingested, built solo. The repository is private; a live demo link is coming soon.