Fundable Post Studio
Writes a founder's LinkedIn posts from live funding data, every number computed in code.
Next.js 16React 19TypeScriptTailwind CSS 4OpenRouter (DeepSeek V4)Fundable REST APIServer-Sent Eventshtml-to-imageVercel
Problem
Fundable's co-founder publishes data-led LinkedIn posts (sector lists, city spotlights, investor leaderboards, valuation step-ups) that used to mean hand-querying the database, doing the arithmetic, drafting in his voice, and building a square graphic for each one. The posts go out under a real name from a company whose product is data credibility, so a wrong or inflated figure is the worst thing the tool could produce. Reframing a true number is fair game; inflating one is not, so the build amplifies framing and never figures.
What I built
A Next.js 16 chat app where a small planner model turns a plain-English ask into a structured query (format, sector, city, rounds, window), which is resolved against Fundable's REST API through a curated alias map, fetched, ranked, and handed to a writer model that streams the post over Server-Sent Events. Hook candidates (round cadence, top-of-list gap, stage anomaly, list total, extreme stat, investor concentration) are derived from the returned rows in TypeScript before the writer runs, so the model chooses a verified stat instead of attempting arithmetic. Every draft then passes a regex claim detector for cross-slice comparisons and percentages absent from the data, plus a structure check on the 12-word hook and 25-word fold line; a violation triggers one corrective retry, and anything that survives is shown as a visible warning rather than hidden. Nine post formats include deal velocity and valuation step-ups computed from batched deal-history calls, and an investor cheque leaderboard read straight off the investors endpoint. A 1200x1200 card renders in three layouts and exports to PNG via html-to-image, with company logos proxied through the app's own origin so the canvas is never tainted.
Highlights
- Hooks are computed, not improvised: six deterministic candidate generators (cadence, top gap, stage anomaly, list total, extreme stat, investor concentration) are weighted by how much they make a reader double-take, and the top four go to the writer pre-verified. The model cannot do arithmetic, and every candidate is derived from rows already in the payload, so the claim checker passes it by construction.
- Code guards, not prompt guards: a claim detector flags 'than any other sector'-style comparisons and any percentage not in the returned rows; a structure checker rejects hooks over 12 words or with no digit in them; one corrective retry is accepted only if it made nothing worse, and survivors surface as a 'check before posting' warning.
- Fails loudly on silent API behaviour: the data API drops unrecognised filters instead of erroring, so a curated alias map of verified permalinks resolves sectors and cities, and the app refuses to write rather than let 'Boston startups' quietly become 'startups anywhere'.
- Data sanity bounds in code: a $1M valuation floor and 100x cap on step-ups (a junk $15 base valuation would otherwise print a 98-million-x markup), an employee-count cap so late-stage rounds tagged as seed stay out of early-stage lists, and follow-on velocity measured only between named stages.
- Em dashes are resolved once over the finished text rather than per stream chunk, because the replacement rule needs context on both sides of the character and a chunk boundary can split it.
Numbers
- Nine post formats, picked by the planner from the prompt
- Alias map holds 101 location aliases and 23 industry aliases (124 entries), each mapped to a verified permalink
- Three card layouts, each exporting the same 1200x1200 PNG
- The 'hot right now' rail probes eight sectors (last 30 days vs the 30 before) and is cached six hours
- Step-up guard: base valuation floored at $1M, multiple capped at 100x
- Six hook candidate generators, top four passed to the writer; hook limit 12 words, fold line minimum 25 words
- Early-stage employee cap at 250 headcount; deal history batched 50 companies per call
Screenshots

