What's new on Employbl.
The shipping log, newest first — built in public, one entry per change. For what the product is, start with the homepage; for what agents can do with our data, see the MCP docs.
August 2026 (2026-08-24c) — The last two pages that didn't fit a phone
- The company collections and locations pages put two 320px filter boxes side by side. On a 390px phone that made the page 511px wide, so — before this week's
initial-scale=1fix — Safari zoomed the whole page out to fit it. The Company Stage and HQ Location filters now stack on a phone and sit side by side from tablet up. That one change covers every page built on the company table: the collections pages, the per-location pages, and the peninsula list. /for-companieswas 580px wide because of a one-line install command. The MCP install command can't wrap, and it was setting the minimum width for its whole section — dragging the page out to 190px past the screen. The command now scrolls sideways in its own box, the way it was always meant to, and the section fits.- Both are confirmed fixed by measurement, not by eye —
scripts/check-mobile-overflow.tsreports the page exactly as wide as the screen at 320, 375, 390 and 430 pixels. Desktop is unchanged.
August 2026 (2026-08-24b) — Company profiles fit on a phone again
- Public company profiles were rendering at about half size on iPhone. A single open-role row with a multi-city location ("San Francisco, CA | New York City, NY | Seattle, WA | London, United Kingdom") was pinned against shrinking, so it pushed the page to roughly twice the width of the screen. Safari's response to a page wider than the phone is to zoom the whole thing out to fit — so every section, not just that row, came out at ~50%. Anthropic's profile was the one that showed it, because its job board is the one carrying five cities in a single location line.
- Role rows now stack on phones. The job title sits above the location instead of beside it, and a long location line is trimmed with an ellipsis — hover or long-press still gives you the full list. Desktop is unchanged. Both the signed-out preview and the signed-in full list are fixed.
- The page is pinned at 1:1. Pages now declare
initial-scale=1, so a future overflow shows up as a short sideways scroll on one section instead of silently shrinking every page on the site. Pinch-zoom still works.
August 2026 (2026-08-24) — Salary and seniority filters that actually filter
- The salary and experience-level filters on
/jobshad nothing to filter on. Both have been in the interface, in the API and in the MCP server for a long time, but almost no listing carried the data — 1,076 of 104,448 open roles had a salary and 1,163 had an experience level. We now read both out of the job description itself, so the filters return real results instead of almost none. - The experience-level filter used labels that could never match anything. It offered "Entry", "Mid-level", "Management" and "Executive" while everything else in the product — the API, the MCP server, the recommendation engine — worked in terms of Intern, Junior, Mid-level, Senior, Staff and Principal. Filtering for "Junior" never matched the 485 junior roles that were sitting right there. The two vocabularies are now one.
- Some jobs advertised a salary of "$20". Where a role was paid hourly, the hourly rate had been stored as though it were an annual salary — so a $20/hour shift-lead role displayed as $20 a year, and was filtered out of any search asking for real money. Those are corrected, and hourly pay is now converted to an annual figure before it is shown or filtered on.
- We would rather show nothing than show you a wrong number. Job descriptions are full of dollar figures that are not pay — funding rounds, revenue targets, budgets, 401(k) match caps, and the on-target earnings sales roles quote, which are base plus commission rather than a salary. Where we cannot tell that a figure is someone's pay, we leave the field empty instead of guessing.
- So the salary filter covers some roles, not all of them, and it is worth knowing which. About one open role in eight now carries a salary. Nearly half mention pay somewhere in the description, but most of those don't print the number — they say "the range for this role is posted below" and put it in their applicant tracking system's own pay widget. Reading those directly is what we're doing next. As a sanity check on all of this: coverage in states with pay-transparency laws — California, New York, Colorado, Washington — is roughly double what it is in Texas and Florida, which is exactly the gap those laws should produce.
- Retired job listings are no longer kept forever. Roles that closed more than 90 days ago are removed along with the data hanging off them, which makes the whole site's job data meaningfully faster to search.
August 2026 (2026-08-23d) — Signing out now actually signs you out
- "Sign out" didn't always end your session. Signing in with Google left a second session behind that sign-out started closing but never waited for. Because that session can hand you a fresh login on its own, the next page load could put you straight back in as if you'd never left.
- Your session key stayed valid after you signed out. Sign-out only removed the key from your browser; the key itself kept working for its full 90-day life. It's now revoked on our side the moment you sign out, so a copy that outlives your browser — a second tab, a shared computer — is dead.
- One "Sign out" button didn't go anywhere. In the mobile menu on marketing pages it signed you out and left you sitting on the same page, still looking signed in. Every sign-out control now takes you back to the homepage, and the page is reloaded from scratch so nothing from your session is left on screen.
- A dead link on the verify-your-email screen. "Signed up with the wrong email? Sign out and register again" led to a page that doesn't exist. It signs you out now.
August 2026 (2026-08-23c) — 6,100 more open roles, and New York jobs that finally show up on a map
- Job-feed coverage went 13.5% → 15.2%. 518 companies gained a job board and 6,137 job listings became visible — Cursor's 113 roles, Kong's 79, Equip Health's 66, Blockstream's 54, Thumbtack's 48. All of them previously read as "we don't track this company's jobs."
- Seven in ten of those boards came from the company's own careers page, not from guessing. Asking a company where its jobs are turns out to be both cheaper and more reliable than trying tokens against every applicant tracking system, so that's the question we ask first now.
- New York jobs were invisible to location search. Our city lookup stores the city as "New York City"; applicant tracking systems write "New York". The names never matched, so 6,851 New York listings had no coordinates and never appeared in a location filter or a "jobs near me" search. Fixed, along with the same problem for "SF", "Bay Area", "St. Paul" and postings that write their city as "Hybrid - San Francisco". Listings with a location on the map went from 86,288 to 152,213.
- We now read Workable job boards. 167 companies host their jobs there and we could see the boards but couldn't open them. Their roles will appear as those boards sync.
- A job board belonging to a company in London briefly appeared on a New York company's profile. Three different companies are called Kernel and two of them share a web address shape, so the check that's meant to prove a board belongs to you couldn't tell them apart. Removed within the hour. The fix is to read the jobs themselves — a board advertising six London roles does not belong to a New York food company — which caught it where no amount of name matching could.
August 2026 (2026-08-23b) — Job listings that are actually open, and descriptions that stop disappearing
- We were showing roles that no longer exist. A company's job board could never go from "12 open roles" to zero — when an employer closed every role, the sync couldn't tell "this board is empty" from "this request failed", so it kept the old listings forever. We found 1,511 listings across 170 boards whose apply links were already dead: Plaid, Tractian, WHOOP, Saronic and Mistral among them. Every one of those was a 404 you'd have hit after clicking through. They're gone now, and a board that empties out will empty out here too.
- Job descriptions had been quietly going missing. When an applicant tracking system was slow to answer, the sync wrote an empty description over the one we already had. Listings missing a description are down from 679 to 240, and Greenhouse — our largest source at 66,000 listings — went from 148 missing to zero.
- Some companies' jobs had silently stopped updating entirely. A single duplicated row could jam a company's whole feed, and Zoox's 239 roles and Roblox's had been frozen that way. Also fixed: job postings with very long URLs were being dropped instead of saved, and boards belonging to companies we'd removed were still being fetched.
- Job-feed coverage went 9.2% → 13.5%, and the nightly sync now finishes in a fraction of the time — the Y Combinator pass alone went from ~87 minutes to 6.5. Faster syncs mean listings that reflect today rather than last night.
- The duplicate company records are gone. 1,127 duplicate or defunct company rows were removed along with the stale data hanging off them, so search results and counts stop double-counting the same company. Watchlists and claimed profiles were moved to the surviving record, never deleted.
August 2026 (2026-08-23) — Company locations that are actually where the company is
- Most company pins were city centroids, not addresses. 18,637 of our 29,899 geocoded company locations sat on a point shared with at least ten other companies — roughly 5,000 in San Francisco stacked on one spot, 3,100 in New York, 1,200 in Los Angeles. 6,917 of them had a full street address sitting in the same database row, unused. Those are now geocoded from the address: 17,094 locations are address-precise, up from 10,991, and another 4,677 resolve to a zip.
- The centroids weren't even in the right neighbourhood. The point every "New York" company shared was in Park Slope, Brooklyn. San Francisco's was out near Golden Gate Park.
- Search by zip, or by distance from anywhere. "Which Series A companies are in 94133?" and "seed fintech within 5 miles of Oakland" are now single queries, on the website's data layer, the MCP server and the REST API. Location filters combine with funding stage, sector, tech stack and headcount, and they match satellite offices rather than only headquarters.
- Companies with more than one office finally show more than one office. Every location record we held was flagged as a headquarters — there were literally zero branch offices in the database, though the maps had been built to display them. Branch offices are now derived from the cities where a company advertises on-site roles, and the company profile lists every office with its own address instead of picking one at random and labelling it "Headquarters."
- We removed thousands of wrong zip codes. Our data provider stamps a placeholder zip on companies whose real one it doesn't know: 3,130 San Francisco companies were all marked 94115. Where we could check against a real street address, those companies averaged more than a mile from that zip. 7,434 placeholder zips were cleared rather than left to give confident, wrong answers.
- Map and radius search got faster. A missing database index meant every "companies near here" query scanned the whole table; the same query now runs in a sixth of the time.
August 2026 (2026-08-22) — 3,400 more open roles, from data we already had
- Job-feed coverage went 12.0% → 12.6%. 180 companies gained a job board and 3,430 job listings became visible — Cohere's 150, Brillio's 121, Flagship Pioneering's 89, Hudson River Trading's 69, Spring Health's 65.
- No new data was bought and no pages were fetched. The ATS links were already in company records we'd paid for months ago; nothing had ever read them. Zero network calls, zero vendor credits.
- Being sure it's your board matters more than finding more boards. A company record can pick up stray links belonging to a similarly-covered company, so a link is only trusted when it lines up with the company's own domain, name, or the owner the ATS reports. That blocked three boards belonging to entirely different companies. One slipped through — two companies are named Pinwheel, and the wrong one's roles showed for a few hours before we caught and removed them.
- Every company checked before today gets checked again. The check got better, so the old "we looked and found nothing" answers were retired rather than left to stand.
- Then we started working through the companies nobody had ever looked at. 1,500 done, 222 more job boards found, and the search got roughly twice as efficient along the way — asking a company's careers page before guessing at their ATS, and not chasing six URL variations that almost never pay off. Coverage is 13.3%, up from 9.2% two days ago, with 101,244 open roles now visible. 22,029 companies still to check.
August 2026 (2026-08-21c) — 5,794 more open roles you can actually see
- Job-feed coverage went 9.2% → 11.4%. We checked every US company that raised in the last six months without a job feed (1,342 of them), then every company whose domain matches a Y Combinator listing (1,580 more). 683 gained a board.
- 5,794 job listings are now visible on Employbl that weren't this morning. Perplexity's 110 roles, Athelas's 102, Hadrian's 138, Whatnot's 131, Etched's 131, Lambda's 78.
- Y Combinator was by far the biggest unlock — 500 of the 689 new boards. Seed-stage YC companies frequently post only to Work at a Startup, so they were structurally invisible to us. Rippling added another 20.
- Nothing gets attached unless we're confident it's yours. Three separate near-misses were caught and blocked during the run, each of which would have shown another company's jobs on the wrong profile — including a 190-role board belonging to a same-named company. When two companies appear to claim one board, we record it for a human rather than guess.
August 2026 (2026-08-21b) — Finding where companies actually post their jobs
- We only had a job feed for 9% of companies. This goes after the other 91%. Employbl pulls listings from a company's applicant tracking system, and for ~27,700 companies we'd simply never found theirs. New discovery runs cheapest-first: what we already have cached, then a direct check against the ATS APIs, then the company's own careers page — all free — and only falls back to paid page-rendering as a last resort.
- Two new job sources: Y Combinator and Rippling. YC's Work at a Startup boards are now matched by exact domain against YC's public directory (6,192 companies cached), so seed-stage YC companies that only post there are no longer invisible. Rippling ATS boards are ingested too.
- New companies get their job board automatically. The weekly funding importer creates company records; it now kicks off board discovery for each one, so a company that just raised doesn't sit at "no job feed yet" until someone runs a script by hand.
- We won't attach a board unless we're sure it's yours. Guessing a company's ATS token is easy to get wrong — a check against the board's registered owner stopped us attaching a defense contractor's cleared-engineering roles to a same-named pre-seed startup. When two companies appear to claim the same board, we record it for a human instead of guessing.
- Open-role counts update immediately. Attaching a board used to leave the count at zero until the following Monday, so a company with 138 open roles briefly read as having none.
August 2026 (2026-08-21) — We stopped telling you a company isn't hiring
- "Not currently hiring" is gone, because it usually wasn't true. Employbl syncs job listings from a company's applicant tracking system — and we only have a feed for about 9% of the 31,635 companies in the index. For the rest we'd never found their job board, but the site and the MCP tools both reported that as "not currently hiring." Of the 38 companies that raised in this week's funding import, 35 had no synced feed — including Whatnot, which was hiring for 132 roles at the time we said it wasn't.
- Three states instead of two. A company now reads as having open roles, having a synced job board that's currently empty, or not having a job feed on Employbl yet. Only the middle one is a statement about the company; the last one is a statement about our coverage, and it now says so.
- Recommendations stopped hiding most of the market.
get_recommended_companiesdefaulted to only returning companies with synced listings, so personalized recommendations could never surface a company whose board we hadn't found. That filter is now off by default. - "Sort by most hiring" actually sorts by most hiring. It had been falling through to the default browse order, which leads with companies that have a website screenshot.
- Clearer filter labels. The map and funding-rounds "Hiring now" checkboxes now read "Has job listings", matching company search — they filter on what we've ingested, not on who's hiring.
August 2026 (2026-08-20) — Company members get one world, and it works
- Company always wins. An account linked to a claimed company is a company account, period — no job-seeker dashboard, no "Search Jobs", no candidate upsells, regardless of how the account was originally created. Every home/logo/Dashboard link routes company members to their company dashboard.
- The sidebar stays put. Companies, Funding Rounds, and the Map now keep the app sidebar for signed-in users — clicking around the product no longer drops you back into marketing chrome. Marketing pages (homepage, pricing, blog, docs) keep the marketing nav, with Dashboard in the corner.
- Fewer links, all working. The "Your Company" sidebar group is now three links that reliably work — Dashboard, Edit Profile, Public Page — and Edit Profile no longer bounces existing claim holders into the claim funnel.
- Fixed the crash on brand-new company profiles. A company created minutes ago has no jobs/locations/tags yet; the profile page assumed those lists existed and crashed. Now covered by a regression test.
- Company dashboard analytics can no longer take the page down. Every external analytics call (PostHog page views, HogQL demand/AI-visibility, the MCP-mentions scan) has a hard 4–5s budget enforced at the source — including in the database itself — and fails to zeros instead of timing out the page.
August 2026 (2026-08-19d) — Company pages that resolve, and dashboards that don't time out
- Company URLs now work in any casing.
/companies/octavehqand/companies/OctaveHQresolve to the same profile — the stored slug is canonical and other spellings redirect to it. Previously the public profile and the company dashboard used two different slug-matching rules, so a URL could 404 on one and load on the other. - A hiccup can no longer freeze a profile into a 404. Company profiles are cached until the next deploy; a transient backend error during that first render used to be cached as "page not found" for everyone until we shipped again. Transient failures now retry on the next visit — only a genuine "this company doesn't exist" is cached.
- Company dashboards stopped timing out. The analytics calls behind the dashboard (page views, AI-visibility) now run in parallel with hard time limits instead of serially with none — a slow analytics day degrades those numbers to zero instead of 504-ing the whole page. Database indexes were added for the company-slug and claim lookups every dashboard visit makes.
August 2026 (2026-08-19c) — One nav for the whole product
- Public pages stopped shape-shifting when you sign in. Marketing pages (homepage, blog, pricing, public company profiles) now always show the marketing top nav — signed in, the right corner becomes a Dashboard link and your avatar menu instead of "Sign in / Start free trial". The app sidebar appears only on actual app pages, so browsing the public site no longer wraps everything in the app shell.
- Company members get a company-first sidebar. If you're a member of a claimed company, the app sidebar leads with a "Your Company" section — Dashboard, Edit Profile, Candidates, Billing, and your public page. Members without a job-seeker profile don't see "Search Jobs", the job tracker, or the candidate-premium upsell at all (their billing is the company plan); people who are both keep both worlds in one nav.
- The company dashboard joined the app. The rep dashboard and profile editor now live inside the same shell as everything else — same sidebar, same sign-out, real navigation back to the rest of Employbl — instead of a disconnected standalone page. Mobile gets matching bottom-nav tabs.
- No more dead ends for company accounts. A company-only member who lands on the job-seeker dashboard is taken to their company dashboard — previously an expired candidate trial could bounce them into the candidate subscription page they had no reason to see.
August 2026 (2026-08-19b) — Google sign-in counts as verification, and the company funnel tells the truth
- No more "verify your email" after signing in with Google. Google OAuth proves you own the mailbox, so it now counts as email verification everywhere — including for company reps, whose accounts previously slipped through without the verified flag and hit the wall on their dashboard. Returning email/password users who sign in with Google get verified the same way, and their 7-day trial starts if it never had.
- Company sign-in no longer quietly turns reps into job seekers. Signing in from the company login with a work email we don't have a company for used to silently create a candidate account and drop you on the job-seeker dashboard. It now routes you to "Add your company" (two minutes, domain-verified); personal-email sign-ins get pointed at the right explanation instead.
- The claim page now says what's actually wrong. If you're signed in with an email that belongs to a different company, it links you to your own dashboard. If your company isn't on Employbl yet, it says so and offers to add it — instead of telling everyone to "switch Google accounts."
August 2026 (2026-08-19) — One sign-in, recognized everywhere
- Signing in with Google no longer leaves you half signed in. Employbl ran two parallel sign-in systems — the email/password session and the Google (company) session — and each half of the site only checked one of them. Sign in with a company Google account and the homepage still showed "Sign in" and "Start free trial", admin pages said "Not an admin", and the site generally treated you as a stranger. Google sign-ins now carry the same session the rest of the site reads, existing signed-in sessions heal themselves on the next page load (no re-login needed), and signing out signs you out of both.
- The company dashboard stopped asking existing claimants to claim again. If you'd already claimed your company but were signed in with email/password, the dashboard bounced you into the "claim your company" funnel you'd already completed. It now sends you to the one-click Google verification for your dashboard instead. Edits still require the domain-verified Google session — that gate is the point — but the path to it is now honest.
August 2026 (2026-08-18) — The whole site reads like a newspaper now
- Every public page speaks the same editorial language. The news-desk redesign that started on the homepage now covers the entire site: the blog reads like a front page (lead story, Most Read rail, hairline story list), company profiles open like printed briefs with a stats strip,
/companiesand/jobsgot proper mastheads, and funding rounds render as a wire desk — dateline rows, amounts in monospace. - One accent, used sparingly. Orange marks the single primary action on each page plus live-data signals; press blue handles links and everything secondary; hairline rules replace card shadows throughout. Collections, city hubs, tech-stack and VC-firm pages, the map chrome, pricing, and the sign-in/up pages all follow suit.
- The footer became a masthead. Site-wide footer rebuilt as a newspaper-style colophon — wordmark, mission line, and the full link directory on the dark press-blue base.
- Signed-in surfaces match. The app sidebar, dashboard, tracker, and profile chrome share the same palette, so crossing from the marketing site into the product no longer feels like changing websites.
August 2026 (2026-08-18) — Recommendations stay in your country, and feedback got a vocabulary
- No more jobs from the wrong side of the planet. Recommended jobs are now filtered to your country (jobs with no location data still pass, so sparse listings aren't punished). Before this, location was only a ranking bonus — of 74,000 active listings, about 27,000 anchored solely abroad could reach a US feed. Built after a California candidate kept getting recommendations based in India. The main job search is unchanged; this gates recommendations only.
- Say why with one tap. Thumbs-down now offers multiple-choice reasons — wrong location, not remote, wrong seniority, compensation, just not interested — plus the optional write-in. Dismissing a job asks (optionally) why, and thumbs-up takes a reason too, so good matches teach the feed as much as bad ones. Picking "wrong location" or "not remote" offers the matching hide-all rule on the spot.
- The full "For You" page can finally talk back. Feedback controls existed only on the dashboard widget; the
/jobsFor You tab now has the same thumbs, reasons, and hide-rules on every row. - Passing on a company twice quiets the whole company. Two or more passes on one employer's roles mildly demote the rest of their listings in your feed.
- MCP callers get the same vocabulary.
rate_jobanddismiss_jobaccept a structuredreason_category(and dismiss takes a free-text reason now). Fixedupdate_preferenceserasing hide-rules on unrelated changes. Server v0.10.0.
August 2026 (2026-08-06) — Teach "Jobs For You" what you don't want
- Every recommended job now takes feedback. Thumbs-up a job and more like it rank higher; thumbs-down or dismiss it and it's gone — from the web feed and from anything an AI assistant pulls via MCP. Thumbs-down takes an optional one-line "why" so the reason is on record, not guessed.
- Hide a whole category in one move. Seeing roles in a city you'd never take, or non-remote jobs when you only want remote? Dismiss one and Employbl offers to hide all jobs in that location — or every non-remote role — for good. The rule sticks across the feed and MCP. (Built after a candidate kept seeing jobs in India she couldn't remove.)
- "Why this matches you" on each job. Every match now shows the reason it surfaced — a skill in the title, a company you watch, your city, the funding stage you asked for — so the ranking isn't a black box. No AI cost: it's the same signals the recommender already scores.
- See more without leaving the page. A "Show more matches" control reveals more of your feed in place.
- All of it works from AI assistants too. New MCP tools
rate_job,dismiss_job, andhide_jobs_matchingmean "stop showing me jobs in India" or "thumbs down that one — too senior" is a single instruction to Claude or any MCP client. Server v0.9.0.
August 2026 (2026-08-05) — A new way of describing what Employbl is
- Employbl is a live dataset of tech companies — and the site now says so. The homepage leads with "A live dataset of tech companies you don't have to build yourself." The product is the maintained record: funding, tech stack, hiring, and team, kept current across 30,000+ companies and 75,000+ ATS-verified roles. The web app, AI chat, MCP server, and REST API are four ways to read the same records, and there's now a section on the homepage that says exactly that.
- The page argues it end to end. New vitals strip under the hero (index size plus the 30-minute ATS sync cadence, because "live" is a claim that needs evidence). "Why we exist" is now the honest version: this information is public but scattered across thousands of ATS boards, filings, and career pages, and it goes stale in weeks — so everyone rebuilds it badly. The four pillars became the four things that make a dataset worth relying on: coverage, freshness, provenance, portability.
- For companies, the pitch changed. It was written as a hiring tool. It isn't one. Companies claim a profile to control what AI assistants say about them when someone asks — answer-engine optimization — and to point the same dataset at their own market for target lists and buying signals.
/for-companiesnow leads with those two jobs; the candidate-demand features are still there for when you're hiring, just no longer the headline. - Pricing reframed as one dataset with two ways to use it: $25/mo for deciding where to work, $99/mo for getting found and finding customers.
- Retired
/for-recruitersand/for-researchers. Both sold a recruiting-led version of the company story that is no longer the offer, and both routed into the same claim funnel. They now redirect to/for-companies.
August 2026 (2026-08-04) — The company side of the site, rewritten
/for-companiesrebuilt around what Company Premium actually does. The page now leads with what AI assistants and candidates find about you, then walks through the three things Employbl does for a company — visibility (own the profile AI reads, one-click generation, AI-referral analytics), demand (view counts, interested candidates, double-opt-in intros), and intelligence (the full MCP toolkit for the team). Four concrete workflows show the plain-English questions a recruiter or founder actually asks, and a new "what you'll learn from Employbl" section points at the free intelligence — funding velocity, tech-stack shifts, hiring patterns by stage and city.- The 30-day free Company Premium trial is finally on the marketing pages. Every first claim has started 30 days of Premium — no card — since July 30, but the site only ever quoted "$99/mo". It's now on the company page, the pricing page, and the homepage, alongside a three-step getting-started path: claim free, trial starts itself, teammates self-join by work domain.
/pricingrebuilt as two equal columns — one for the person deciding where to work, one for the company being decided about — on the same editorial design as the rest of the site. The company plan now lists what it includes instead of a one-line MCP mention.- "For companies" is in the top navigation. It was previously reachable only from the footer.
- Homepage copy tightened. The hero deck now opens with what Employbl actually is ("market intelligence for your job search — like Crunchbase, but for deciding where to work") before listing what it does. The MCP tools on the homepage describe what they get you rather than every filter they accept, the AI assistant section says plainly why it beats asking a general chatbot (it reads today's listings and rounds, not training data), and repeated phrasings across section headlines were varied so the page reads as one argument.
- Copy corrections: the homepage advertised "7 MCP tools" (there are 24), and 19 files across the site still claimed 90,000+ job listings — the verified figure after the duplicate-listing sweep is 75,000+. Every dataset number on the site now derives from one shared source, so pricing, docs, search, chat, the MCP tool descriptions, and the marketing pages can't drift apart again.
August 2026 (2026-08-01) — Website-enriched profiles + a PR-wire funding lane
- Newly-added companies now fill themselves in from their own websites. A new enrichment pass reads each company's site (about, team, careers, pricing pages) and backfills what was missing — description, founders, headquarters, social links, logo, and headcount — without ever overwriting a fact we already had. ~180 recently-added companies enriched in the first pass; thin single-page sites are flagged and skipped gracefully rather than guessed at.
- Funding rounds now flow straight from the press wires. Beyond the newsletter fast lane, a new discovery lane reads the PR Newswire venture-capital feed directly, extracts each round, and stages it — gating out fund closes, acquisitions, debt facilities, grants, and non-US companies before anything is verified and imported. First rounds in: Bidbus (Series A, $15M), Ratio Therapeutics (Series C, $70M), and Eileen (Pre-Seed). All live in
recent_funding_rounds, the REST API, and site search immediately. - Under the hood: every funding source — newsletters, PR wires, FinSMEs/TechCrunch, web search — now feeds one deduped staging queue, so the same round is never researched or imported twice.
July 2026 (2026-07-31) — Funding data is current again
- This week's funding rounds are live the week they were announced. A new fast lane imports rounds from funding newsletters — each one verified against a primary source (the company's own announcement or press release) before it lands. First batch: 16 US rounds from the week of July 31, from Seed through Series D (including K2 Space's $500M Series D and Antora Energy's $550M Series C). Funding data previously lagged ~6 weeks behind announcements.
- The
recent_funding_roundsMCP tool, REST API, and site search all serve the new rounds immediately. - 8 newly-funded companies got profiles: FluxCo, Klaimee, Meshy, Inner Logic, DataBahn, Discern, CAIS, and Paper (paper.design). Full enrichment (team, tech stack, headcount) follows in the next data pass. (PR #469)
July 2026 (2026-07-31) — Companies: your profile, your links
- Careers page link. Claimed companies can add their careers page URL — it renders under "Hiring at {Company}" on the public profile (web editor or the
update_company_profileMCP tool, server v0.8.3). - Funding announcement links. Companies can attach the public announcement (press release or blog post) to each funding round — the round links out from the profile. Round names, amounts, and dates remain independently maintained by Employbl.
- Editable headquarters address. Claimed companies can correct their HQ street address; the profile map pin re-geocodes automatically.
- Edits go live instantly. Saving from the company editor now refreshes the public page immediately — no waiting on the next site deploy. Customer chips on company profiles also link to the customers' own Employbl profiles (with logos), and founders link to LinkedIn where we have it.
July 2026 (2026-07-31) — Cleaner funding data + fresher company profiles
- Duplicate funding rounds are gone sitewide. A 2026-07-09 dedup soft-deleted ~69K duplicate rounds, but the API serving company pages never filtered them out — so many companies showed the same round twice. The duplicates are now fully removed, and investor names that only existed on the deleted copies were merged into the surviving rounds first (PR #461).
- Investor lists show every named investor. The Investors section previously only showed investors that have an Employbl collection page (e.g. Y Combinator, a16z). It now also lists every investor named on any funding round.
- "Updated" date reflects profile freshness. The date in the company-page header now uses the most recent of the company record and the premium profile's publish/update time — a freshly published profile no longer shows a months-old date.
- Behind the scenes: profile generation now waits up to 90s for slow company websites instead of silently falling back to a thinner data source, and empty profile sections no longer render bare headers.
July 2026 (2026-07-31) — Companies: own what AI says about you
- AI-visibility analytics. Company dashboards now show how AI assistants send candidates your way: profile visits referred by ChatGPT, Perplexity, Gemini, and Claude, plus your company's appearances in AI answers served through Employbl's MCP. The weekly profile report includes the same numbers. Your published profile is the text AI assistants read about you — keeping it sharp is answer-engine optimization.
- Claimed profiles get featured placement. A new homepage strip spotlights companies that claimed their profile (clearly labeled — the funding and headcount facts stay independently maintained), and the newsletter's company-spotlight slot now prefers claimed profiles.
July 2026 (2026-07-31) — Companies: meet the candidates who found you
- Intro requests are live. The company dashboard's Interested Candidates panel now has a Request Intro button: candidates who saved or applied to your roles get a private email and choose whether to connect. If they accept, both sides get each other's contact — if they decline (or do nothing), the company learns nothing at all. Double-opt-in by design; limited to 5 requests per company per week so every request stays warm. (PR #454)
- Your trial, in numbers. Near the end of a company premium trial, the wrap-up email now mirrors what actually happened — profile views, interested candidates, intros, your team's MCP usage — instead of a generic pitch.
- A principle, made permanent: companies only ever see candidates who chose them. No resume database, no talent search — the only candidates a company can meet are the ones who engaged with its roles. That's what keeps candidate trust (and candidate traffic) the foundation of the whole product.
July 2026 (2026-07-30) — Companies: 30-day free premium trial + one-click profile generation
- Every company now gets 30 days of Employbl Premium free the first time someone claims it — no card required. The trial includes the full MCP toolkit for up to 10 teammates, profile analytics, and profile generation. Your dashboard shows days remaining; when the trial ends, your published profile stays live.
- Generate your company profile in one click. The dashboard's new Generate button drafts your profile from your website with AI — positioning, tech stack, hiring focus, culture — and publishes it immediately. Polish anything afterwards in the editor. (PR #447)
- A welcome email series now walks new company reps through the trial: what's included, generating the profile, connecting your team to the MCP server, how teammates get seats (sign in with your work domain — no invites), and what happens at trial end.
July 2026 (2026-07-30) — View other candidates' profiles
- Logged-in members can now view each other's profiles. Visiting another member's profile URL previously showed you your own page; it now renders their profile read-only — about, experience, education, projects, and skills. Email, resume, and job-search preferences stay private, and members who opted out of profile visibility remain hidden. Logged-out visitors get a sign-in prompt. (PR #444)
July 2026 (2026-07-30) — Company dashboard sign-in reliability
- Fixed: company reps could get bounced between their dashboard and the public company page in a repeated sign-in loop. A database hiccup during a session check could silently drop the rep's company access mid-session. Company access is now established once at sign-in and survives transient database failures. (PR #442)
- Under the hood: session replay enabled for signed-in candidates and company reps (never anonymous traffic), sign-in events now recorded on every auth path, and MCP/API tool calls keep an audit trail (inputs + result preview) so we can review answer quality. (PRs #443, #445)
July 2026 (2026-07-28) — Email verification no longer errors on re-click
- Fixed: reloading or re-clicking an email verification link showed a false "Invalid or expired verification token" error. The link is single-use under the hood, so the first click always worked — but any second visit (reload, second device, mail-app browser handoff) told already-verified users their signup was broken. The page now recognizes verified accounts and shows "you're all set" instead. (PR #438)
July 2026 (2026-07-28) — Company reps: flag incorrect tech-stack entries, plus editing polish
- Flag a wrong tech-stack detection in one click — the company edit page shows the technologies detected from your open roles as read-only chips; each has a small flag (⚑) so reps can report an incorrect entry for review instead of emailing us. Flags land directly in our review queue.
- Shipped earlier in the same polish pass (PR #379): the tech-stack editor suggests canonical technology names as you type (free text still works), spellings normalize automatically on save ("ReactJS" → "React") so declared stacks merge cleanly with job-derived ones, and admins can un-review curation misfires from
/admin/technologieswithout SQL.
July 2026 (2026-07-25) — Trial-end mirror email
- On the last day of your trial, we now show you what you built — companies researched, applications started, jobs saved, searches watching — and what staying keeps running: saved-search alerts, Funding Radar, and your tracker. If you didn't get a chance to dig in, the email says that honestly instead of inventing stats.
July 2026 (2026-07-21) — Email cleanup: one-click unsubscribe everywhere, consistent sender, tidier inbox
- Every recurring Employbl email now has a working opt-out: the trial welcome series gained a one-click unsubscribe (and respects your email preferences), and companies receiving weekly profile-analytics reports get a "stop these reports" link. Job-search alerts remain controlled per-search from Saved Searches.
- Monday emails staggered — weekly job alerts, Funding Radar, and company analytics no longer all land in the same minute.
- Consistent sender + look: everything now comes from the same address with the same visual style (goodbye leftover indigo templates), with UTF-8 fixed so em dashes render properly in every client.
- Saved-search email alerts now default to weekly when you save a search (you can still pick daily); the dashboard Watchlist tile now opens your bookmarked companies directly.
- Under the hood: dead saved-jobs code removed, verification-email resends rate-limited, one shared email client.
July 2026 (2026-07-21) — Funding Radar: personalized weekly funding digest
- Funding Radar is live — a weekly email for members: companies matching your stage, industry, and location preferences that just raised, with open-role counts and investor names. Fresh capital becomes headcount first; the radar tells you before the roles hit the boards. Free members get the top matches as a teaser; premium and trial members get the full list. Sends Mondays.
- One-click unsubscribe — every radar email footer carries a signed unsubscribe link (
/api/email/unsubscribe); preferences remain editable from the dashboard. - Guardrails baked in: only verified accounts with preferences set receive it, never an empty digest, corrupt funding amounts are suppressed rather than shown, and a 7-day dedup window prevents double-sends.
July 2026 (2026-07-18) — API developer experience: OpenAPI spec, usage meters, rate-limit headers
- Machine-readable OpenAPI 3.1 spec at
/api/v1/openapi.json— import into Postman, generate clients, or point an AI agent at it. Derived from the same source as the docs page, so it can't drift. - Every API response now carries
X-RateLimit-Remaining-Minute/-Dayheaders so clients can pace themselves before hitting a 429. - API key page upgraded —
/profile/mcpis now "API keys & MCP": each key shows a live usage meter (calls today against the 5,000/day budget), and the copy reflects that one key drives both the MCP server and the REST API. - Copy buttons on every code block in the developer docs.
- The API is now a listed premium feature on the pricing page, in the site navigation, and in
llms.txtfor AI answer engines.
July 2026 (2026-07-18) — Public REST API v1 for premium members
- Six read-only REST endpoints under
/api/v1— companies search, company profile, similar companies, jobs search, job listing detail, and recent funding rounds — serving the same indexes as the MCP tools over plain HTTP for scripts, notebooks, and integrations. Authenticated with the same Bearer token as the MCP server (Profile → MCP), premium-gated, with per-token rate limits (60/min, 5,000/day) shared across MCP and REST and every call audit-logged. Responses use a consistent{ data, meta }envelope with typed error codes.
July 2026 (2026-07-18) — Developer docs redesigned: three-column layout, REST API reference
/docsrebuilt in the editorial design system — Laravel-docs-style three-column layout with a sticky section nav on the left, an "on this page" rail on the right (scroll-spy highlights the section you're reading), and the content restyled from generic gray/blue cards to the paper/ink editorial kit. On mobile the nav becomes a pinned horizontal strip.- New REST API reference section documenting all six v1 endpoints with parameter tables, curl examples, the response envelope, and error codes.
- Docs content now lives in data files (
lib/docs/) so tool and endpoint changes update the page without touching layout code.
July 2026 (2026-07-18) — AEO phase E: collection-page summaries + AI-referral tracking; simpler top nav
- Every company-collection page now opens with a plain-language summary computed from live data at build time — company count, how many are venture-funded, the most common headquarters city, tracked capital, and open roles. Stat tiles are great for humans scanning; a prose sentence is what an AI answer engine can actually quote.
- AI answer-engine referrals are now tracked in PostHog — a saved weekly insight breaks down visitors referred by ChatGPT, Perplexity, Claude, Gemini, Copilot, and Bing. The baseline surprised us: ChatGPT already sent 707 visitors in the last 30 days, more than any channel we weren't measuring.
- The top navigation for signed-out visitors is simpler: Product, Pricing, Blog, Docs. Company search and job search now live inside a two-column Product panel alongside funding rounds, tech stacks, the map, and the intelligence tools — one menu for the whole product instead of three overlapping ones.
July 2026 (2026-07-17) — AEO phase B: FAQ blocks with structured data across the key pages
- Six pages now answer the questions people actually ask AI assistants — homepage, About, company search, tech stacks, company collections, and funding rounds each carry a visible FAQ block with matching
FAQPagestructured data, rendered from the same content so the schema can never drift from the page. Questions ladder to the brand positioning: what Employbl is, where the data comes from (ATS-verified listings, job-derived tech stacks), how it differs from LinkedIn/Indeed/Wellfound, and what's free vs. premium. - Fixed: the company search page was invisible to crawlers. The entire page body — including its breadcrumb structured data — rendered behind a client-side auth check, so search engines and AI crawlers saw only a loading spinner. Schema and the FAQ block now server-render outside the gate.
- About page refreshed — stale "20,000+ companies" copy corrected to canonical numbers, meta description now carries the positioning one-liner, and 230 lines of dead commented-out markup removed.
July 2026 (2026-07-17) — AEO phase A: llms.txt, blog schema, honest dataset numbers
llms.txtis live at the site root — a map for AI answer engines (ChatGPT, Perplexity, Claude) with Employbl's positioning one-liner, key facts, and the canonical pages worth citing. Pairs with the existing AEO-tuned robots.txt (citation bots allowed, trainers/scrapers blocked).- Blog posts now emit
BlogPostingstructured data (JSON-LD) — author, dates, images — so answer engines can parse and cite the data-journalism pieces properly. - Dataset numbers corrected sitewide — the site was underselling. Pages claimed 25,000+ companies and 61,000+ jobs; production actually holds 30,000+ companies, 90,000+ live ATS-verified listings, and 75,000+ funding rounds (verified against the database 2026-07-17). Every page, meta description, MCP tool description, and stat tile now cites the same canonical numbers from one shared constant, so they can't silently drift apart again.
July 2026 (2026-07-17) — Funding rounds dashboard for members
- Signed-in members now get a live funding-rounds dashboard at
/funding-rounds— filter 59K+ tracked rounds by round stage (Pre-seed through Series D), industry, amount raised (min/max USD), announcement date range, and headquarters state, with a "hiring now" toggle and recency/amount sorting. Each result shows the company (linked to its profile), a one-line description, industry, round, amount, date, named investors, and open-role count. Anonymous visitors still see the marketing landing at the same URL. The search API and results UI had been built during Phase 17 but never mounted on a page; this wires it up, adds the date-range, max-amount, and industry filters, and drops the free-text search box in favor of pure filters. Investor-name filtering needs a Typesense schema change and is deferred to a follow-up. - Funding rounds search index now carries industry + tagline. The Typesense
funding_roundscollection gained a facetedsectorfield (curated industry taxonomy) and a display-onlytagline(premium-profile tagline, falling back to the company description) — rebuilt with zero downtime via the versioned-collection alias swap. - "Funding Rounds" added to the logged-in sidebar under Explore.
July 2026 (2026-07-17) — Companies search: all filters always on screen
- The "All filters" slide-over drawer on
/companiesis gone. Every filter — industries, investment stage, tech stacks, HQ location, VC firms, company collections, plus the hiring/public toggles — now sits in an always-visible panel between the search bar and results, same pattern as the funding-rounds dashboard. Active filters render as dismissable pills at the top of the panel, and a "Clear all filters" action lives inline. Removed the drawer, the chip bar it launched from, and three orphaned filter components that nothing rendered.
July 2026 (2026-07-16) — Blog editorial audit: 10 posts rewritten with real findings, 12 removed; draft-leak fix (#407)
- Every post published since June 1 audited against a new bar: a data finding worth reading, ≥800 words, evidence for every claim. All 22 were 133–378-word stubs. Ten were rewritten from live dataset research and republished at their original URLs — including "Forward Deployed Engineer Is Now a Bigger Job Market Than Sales Engineer" (638 active FDE listings across 241 companies, up from 3 companies in 2024 — now out-listing Sales Engineer at 426), "AI Companies Rank 7th in Tech Hiring. Defense Ranks 4th.", and "Waymo Has 397 Open Roles and Only 4 of Them Are Sales". Every piece now states the scope of our index (Greenhouse/Lever/Ashby, startup-heavy), offers competing hypotheses for why the finding looks the way it does, and closes with methodology. Twelve posts with no insight and near-zero traffic were unpublished.
- Fixed: Sanity drafts were publicly renderable (#407). The server-side Sanity client's token routed reads to the raw perspective, so any studio draft — including "unpublished" posts — rendered at
/blog/<slug>and appeared in the blog index. The client now reads the published perspective only, and single-post queries apply the same status/date filter as list queries.
July 2026 (2026-07-16) — Blog restored after 20-hour outage + editorial covers live sitewide (#404, #405)
- Every blog post page had been crashing since 2026-07-15 14:38 UTC ("Application error: a client-side exception has occurred") — found while verifying the new covers, invisible to error tracking. Root cause:
pages/blog/[slug]re-exports its data fetching from a shared feature module, which defeats Next.js server-code elimination, so the Prisma client had silently shipped in the blog's browser bundle for months; PR #398's connection-pool hardening added a server-env read at Prisma module scope, and the env guard (correctly) crashed the browser for it. Fixed with a type-only import + dynamicimport()ingetStaticProps(#405); the blog's browser bundle no longer contains Prisma at all. Bisected via Vercel's per-deployment URLs. Repeat visitors may hold the broken page in browser cache for up to 10 days (the sitewideCache-Controlmax-age); fresh loads are fine. - The 67 backfilled editorial data-covers are live on
/blogand every post page — PR #403 touched only scripts/docs so it never triggered a deploy (build-skip rules working as intended); #404's deploy flushed the ISR cache and shipped them. - External job links no longer go through the Next.js router (#404) — malformed ATS URLs (double-slash paths) threw Invalid-href errors on job pages. Also fixed 10 company slugs containing "/" (legacy slug generator) that made those profile URLs unroutable — e.g.
/dev/agentsis now reachable at/companies/dev-agents.
iPhone Safari rendered the homepage at about two-thirds width with a white gutter down the right side. One element — the MCP section's one-line install command — was intrinsically wider than the phone viewport, which forced the mobile layout wider and made Safari zoom the whole page out to fit. The command now scrolls inside its own box, and long inline code on /changelog wraps instead of stretching the page. All public pages audited at iPhone width; the site fills the screen edge-to-edge again — just in time for the traffic the newsletter and LinkedIn push is about to send here.
July 2026 (2026-07-13) — Smarter job alerts + personalization fixes (#388, #390)
- "Jobs For You" is back and much better. A data regression had silently emptied the dashboard feed for two weeks (a freshness filter pointed at a column the job sync stopped writing). Fixed — and the scoring got a rebalance while we were in there: what role you want (your saved-search keywords, your skills in the title) now outranks how similar a company is to ones you follow, jobs at companies you watchlist get a direct boost, and no single employer can sweep your whole feed. Engineers stop seeing sales jobs.
- Saved-search alert emails redesigned. Each job now shows company, funding stage, location, remote, and salary range at a glance; the subject line tells you how many matches and where ("23+ new Remote engineer roles — Reddit, Anthropic + more"); and "See all matches" reopens the live search with your filters applied.
- Your industry preferences actually matter now. Stated interests (AI/ML, FinTech, …) were being matched against a retired tag system and never influenced recommendations; they now score against the curated industry taxonomy.
- Company claim flow unstuck. Signing in with the wrong Google account no longer loops silently — the page names the mismatch and offers the account picker.
July 2026 (2026-07-12) — Public changelog page
This document went public: new /changelog page renders it with the editorial layout kit (Masthead + typography pass, .md cross-references neutralized to plain text), linked from the footer. Static at build time — the changelog only gains entries when we deploy anyway. Part of the GTM activation plan (docs/content/gtm-activation-plan.md): build-in-public trust signal, standing feed for the newsletter's "what we shipped" section and Friday LinkedIn posts, and an AEO surface. Also: scripts/screenshot-section.ts now passes --no-sandbox when running as root, so remote-container sessions can take screenshots.
July 2026 (2026-07-12) — First-principles search UX: /jobs + /companies get one-row filter chips (#381, #382, #383)
Both in-app search pages rebuilt around the same principle: search bar → one chip row → results. No duplicate filter surfaces, no page furniture.
/jobs (#381):
- Job result rows lead with the job: full-weight wrapping title, decision line (company · stage chip · location · Remote badge · green salary), relative recency right-aligned, letter-tile logo fallback. Killed the per-row "View Job" button, company taglines, and the company pill wall (including the "$614174976 raised" string bug).
- FilterChipBar replaces FacetStrip: Remote toggle, Level / Funding stage dropdowns with live facet counts, Salary ($100K+…$250K+), Posted (day/week/month), "All filters" opens the drawer.
- Save-search affordance rides the filter row (right-aligned chip): "Save search" when unsaved; "Saved · {name}" opening the edit modal when the current query matches a saved search. In-page saved-search list removed — recall lives in the sidebar's pinned searches +
/saved-job-searches.
/companies (#382, #383):
- Same chip row: Hiring toggle, Funding stage + Industry dropdowns with live Typesense facet counts (
sectoradded tofacet_by; the search API now returnsfacets). "All filters" opens the drawer; drawer-applied filters (tech, investors, HQ, collections, keyword, map area) surface as dismissable indigo chips in the row. Header's duplicate Filters button and the ActiveFiltersPills row deleted. Chip primitives shared across both pages (components/filters/FilterChipPrimitives.tsx). - Bug fix:
useCompaniesSearchnever forwardedsector,founded_after, orfounded_beforeto the API — the Phase 26 industry filter and part of Phase 23 NL-compiled searches were silently ignored (Fintech + Kubernetes + hiring returned 723 results; correct answer is 105). Verified on prod post-deploy: sector=Fintech + Hiring → 428 companies, Airwallex/Stripe/Okx up top.
July 2026 (2026-07-12) — Job pages load reliably after deploys (#384)
Job detail pages intermittently timed out (10s, billed as Function Duration) in bursts right after every deploy: the deploy empties the ISR cache, crawlers regenerate many pages at once, and each regeneration made an HTTP round-trip to the Go API. Two fixes:
- Page data now comes from direct database queries (
lib/jobs/jobListingPage.ts) — no HTTP hop, response shape verified field-by-field against the old endpoint. Bonus: a transient error can no longer cache a permanent 404 for a job that exists. - Database connection pool settings tuned for serverless concurrency (the previous config serialized concurrent page renders through a single connection).
Measured on the next deploy: the regen burst cleared in about a minute with a handful of cold-start stragglers, then hundreds of straight 200s — previous deploys stormed timeouts for minutes. Also closed out: Phase 23 NL search live-API test suite passed 6/6, and the Phase 26 industry classification backfill completed (28.5K companies classified, 0 API failures on the final run).
July 2026 (2026-07-11) — Curated industry taxonomy replaces Diffbot classifiers (Phase 26)
Diffbot's DBpedia-style industry tags ("Software Companies" covered ~90% of the database) replaced with an operator-vocabulary taxonomy, end to end in one day:
- 26a:
industries(17 sectors → 58 subsectors, seed of record inlib/industries/industryTaxonomy.ts) +company_industriestables;/admin/industries. - 26b: LLM classification (Haiku batches; description + premium copy + Diffbot hints + derived tech stack + job titles → primary sector + ≤3 subsectors with confidence). Nightly
classify-company-industriesjob for new companies; full backfill run at ship time. Spot-checks: Okta → Cybersecurity/Identity & Access, Sezzle → Fintech/Payments, Anduril → Aerospace & Defense/Defense Technology. - 26c: Typesense
sector(facet) +subsectorfields;/companiesIndustries filter now sector-grouped taxonomy names (sectorparam; legacyindtag-id URLs still resolve); MCPfind_companiessectorparam (server v0.8.2,/docsupdated); new/industries+/industries/[slug]SEO pages (stats, subsectors, who's-here grid, adjacent subsectors); industry chips on company profiles. - 26d:
populate-company-tagsno longer writesindustryor legacycompany-tech-stackSpatie tags (investors still sync). Existing taggings stay read-only;/company-collectionsunaffected.
July 2026 (2026-07-11) — Homepage profile proof + dual pricing + taxonomy-backed tech filter (Phases 21a + 21e)
Homepage (21a):
- "What's in a profile" blocks 02/03 finally have product shots: Waymo's job-derived tech-stack section and Sierra's founder cards (Bret Taylor career chips); block 01 re-captured as a tight crop of Waymo's funding section. Captions name the intelligence ("TECH STACK FROM OPEN ROLES · NOT SELF-REPORTED").
- Company Premium card on the homepage — $99/mo · 10 seats alongside the candidate card (previously a footnote link).
pricing_cta_clickedPostHog events on both CTAs withsource: "homepage".
Search + MCP (21e):
/companiestech-stack filter sources the curated taxonomy via new/api/technologies(searchable by name or category); filter values are technology names matched against the Typesense union —ts=Kubernetes→ 767 companies. Legacy tag-id URLs still work.- MCP
find_companiesadvertises job-derived stack matching (server v0.8.1,/docsupdated).
July 2026 (2026-07-10) — Tech-stack section rebuild + technology intelligence pages (Phase 21c)
The tech stack becomes a first-class surface — on company profiles and as its own page network.
- Company profile tech-stack section rebuilt (
features/companies/TechStackSection.tsx, replaces the flat mono chips inPremiumProfile): grouped by category (engineering leads), simple-icons logos, per-tech open-role counts, and provenance labels — "From open roles" (job-derived) vs "Company-reported" (declared) — the same transparency instinct as the profile firewall. Renders for every company with derived data (2,434 companies), not just the 11 published premium profiles. New "Tech Stack" TOC entry. /tech-stacks/[slug]— 304 technology intelligence pages (editorial kit: Masthead + StatStrip): one-line definition, open-role/company/remote-share stats, who's-hiring grid (top 12 companies by listing count), co-occurring technologies, common role titles.getStaticProps+fallback: "blocking", norevalidateper the Vercel cost rules./tech-stacksindex rewired from legacy company-collection tag links to the curated technology pages, grouped by category with live job counts.skills.descriptionbackfilled with LLM one-liners (264 technologies) viascripts/backfill-technology-descriptions.ts.- Derived-stack JSON now carries
logo_slug(aggregation re-run across all 2,434 companies).
July 2026 (2026-07-10) — Company tech stacks aggregated from job listings (Phase 21b-3)
job_skills now rolls up to a per-company derived stack.
companies.tech_stack_from_jobs— denormalized JSON ([{id, name, slug, category, listings}], top 20, confidence ≥ 0.5). Backfilled: 2,434 companies across 299 technologies. Waymo → C++/Python/PyTorch/TensorFlow/JAX; Vercel → Next.js/Vercel/TypeScript.aggregate-company-tech-stackworker job (daily 8:15am UTC, after extraction), full-rebuild idempotent, clears stale stacks; admin Run Now + CLI script.- Typesense
tech_stackfilter now matches a union: job-derived stack ∪ Spatie tech tags ∪ published premium-profile declared stacks — unenriched companies become findable by technology for the first time. Applies on next companies sync. - Premium
tech_stackcolumn deliberately NOT renamed (live-prod breakage); two-table split keeps derived vs declared sources separate. - Taxonomy pruning continues: CMS, EDR, ACH, APT unreviewed (generic acronyms/standards, not products) — 297 technologies now.
July 2026 (2026-07-10) — Job-derived tech-stack extraction (Phase 21b-2)
The dead job_skills pipeline (last write March 2025, ~1% coverage, out-of-repo) is replaced by an in-repo, dictionary-first extractor. No LLM calls at extraction time — the curated taxonomy's names + aliases compile into one word-boundary matcher.
- Backfill: 187,282
job_skillsrows across 45,683 active listings (51%) covering 2,560 companies, confidence-scored by mention count. extract-job-skills-scheduledworker job (pipeline queue, daily 7:15am UTC after the job-listings sync) keeps new listings extracted incrementally via the newjob_listings.skills_extracted_atstamp; admin Run Now wired. CLI:scripts/extract-job-skills.ts(--forcerescans after taxonomy changes).- False-positive control (
lib/skills/technologyDictionary.ts): deny list for English/domain collisions ("GTM" is go-to-market, "Assembly" is General Assembly, "backbone of our platform") and job-post boilerplate (LinkedIn/Instagram/Zoom mentions aren't stack signal); ambiguous English-word technologies (Go, React, Swift, Excel, Segment…) match case-sensitively, never at sentence start, never in hyphen compounds ("Go-to-market"), and single mentions get a confidence discount. Verified by two sample-inspection passes against prod before the full run.
July 2026 (2026-07-10) — Canonical technology taxonomy (Phase 21b-1)
Foundation for StackShare-style tech-stack intelligence (roadmap.md § Phase 21). A "technology" is now a skills row with category IS NOT NULL AND reviewed = true — the never-finished category/aliases/reviewed columns are finally in use.
- 311 curated technologies across 17 category buckets (
lib/skills/technologyCategories.ts), seeded byscripts/curate-technology-taxonomy.ts: candidates gathered fromjob_skillsfrequency + the 1,804 draftedpremium_company_profiles.tech_stackarrays + Spatiecompany-tech-stackstags, LLM-classified (strict: no soft skills, methodologies, protocols, or generic categories), then upserted onto canonicalskillsrows. Classifications cache to.cache/so re-runs are free; script is idempotent and skips already-reviewed technologies. - New
skills.slug+skills.logo_slugcolumns (docs/sql/2026-07-10-skills-technology-taxonomy.sql). Slugs are unique (will power/tech-stacks/[slug]in Phase 21c); logos resolve deterministically against thesimple-iconspackage (154/311 matched; server-side only — seelib/skills/simpleIconMatch.ts). - Alias clusters folded into canonical spellings —
normalized_namevariants like "gcp"/"Google Cloud Platform", "Postgres"/"postgresql", and case-dupes now all group under one canonical name, with the variants preserved inaliases(these feed the Phase 21b-2 dictionary extractor). /admin/technologies— admin review page grouped by category: logo, slug, aliases, job-listing usage counts across each technology's whole cluster.
July 2026 (2026-07-10) — Mercury-style app navigation: no top navbar for logged-in users
Logged-in chrome rebuilt around the sidebar (modeled on Mercury's app shell); public pages untouched.
- Top navbar removed on desktop for authenticated users.
AppLayoutno longer rendersLegacyNavbar. The newAppSidebaris the only desktop chrome: brand at top, grouped nav (Home + AI Chat, then EXPLORE: Search Jobs / Companies / Map / Learn, then YOUR SEARCH: Job Tracker / Saved Searches / Profile), user menu pinned to the bottom (avatar + name/email + Premium/Free plan, opening up to Your Profile / Billing-or-Upgrade / Sign out). Nav config lives in one place (components/layout/appNavLinks.ts) shared by desktop and mobile so they can't drift. - Saved Searches finally has a nav entry —
/saved-job-searcheswas previously unreachable from the sidebar. - Active states now match nested routes (
/jobs/[slug]highlights Search Jobs,/profile/*highlights Profile) instead of exact-path only. - Collapse state persists in
localStorageacross pages and sessions; collapsed rail redesigned (no more dark-hover artifacts). - Mobile: slim app header (logo + hamburger) replaces the marketing navbar; the hamburger and bottom-nav "More" open a new app-specific slide-over (
AppMobileMenu) that mirrors the sidebar instead of showing marketing links (About/Pricing/Register). Bottom-nav Home now points at/dashboard. - Job tracker layout collapsed into
AppLayout fullHeight— tracker no longer renders a marketing Footer under the kanban board. - Viewport-height fixes for the navbar-less world:
/chatand the explore map size to the full viewport on desktop (100dvh) and account for the 3.5rem mobile header.
Follow-up (same PR):
- Pinned saved searches in the sidebar (Mercury's "Bookmarks"): up to 5 saved searches render under the Saved Searches link with a new-jobs-this-week badge. New
/api/auth/saved-searches/new-countsendpoint (capped at 5 COUNT queries, SWR-deduped 5 min per session). - Trial urgency in the user area: "Trial — N days left" replaces the plan label in sidebar + mobile menu, upgrade card copy switches to keep-your-access framing, Billing menu item hidden during trial (trial users have no Stripe subscription to manage).
- Accent unification: indigo → tangelo/ink across all authed surfaces (chat page + panel, dashboard sections, recommendations). Indigo is now retired from app chrome per the redesign brief.
- Tracker cards fixed: cards showed only "Created over a year ago" because board syncs hard-delete stale
job_listingsrows. Now: title/company/URL snapshotted intoexternal_*columns at save time,||fallbacks tolerate legacy empty-string values, and orphaned rows render "Listing no longer available" instead of blank cards with dead links. AppPageHeaderstandardizes the slim title + primary-action row (dashboard, saved-searches pages).
Dashboard command center (same PR):
- Setup-first dashboard. New
SetupPanelowns the top slot until the user has BOTH a profile signal (jobs-for-you profileStrength ≥ 20) and saved preferences. Step 1: resume upload (existing parse flow) or paste LinkedIn/resume text into new/api/auth/profile/import-text(reuses the MCPupload_profile_from_textpipeline, refactored into a sharedapplyProfileTextcore — platform key, no credit metering). Step 2: preferences are AI-drafted from the profile via new/api/auth/preferences/suggest(Claude call constrained to the PreferenceCard option slugs, deterministic heuristic fallback), prefilled into the preference chips for one-tap save. - Recommendations gated on setup. Jobs For You and Recommended Companies render only when profile + preferences exist — no more matching on an empty profile. "Skip for now" collapses setup to a resume-setup banner.
- Mercury-style command center once set up: "Welcome back, {name}", quick-action pills (Search jobs / Ask AI / Track application / Saved searches), and a stat row — active applications, new saved-search matches this week, profile strength, watchlist size. Stats ride two new COUNTs on
/api/dashboardplus the existing new-counts and jobs-for-you endpoints (shared SWR keys, no extra requests).
July 2026 (2026-07-10) — /jobs fixes for signed-in users: salary on cards, working save button, honest copy, skills deep-link
- Salary pill on job cards.
minimum_salary/maximum_salarycame back from the search API but were never rendered — now a green💵 $140K – $180Kpill. - Save-job button fixed. It gated on
isPremiumwith a "You must be logged in" toast (shown to logged-in users) and its optimistic state never rolled back on API failure. Now gates on authentication (the endpoint only requires a session), rolls back on error, and has an aria-label. - Funding pill consolidation. Three separate yellow "Latest Round" pills (name / date / amount) → one
Series B · $50M · Mar 2025pill. - Honest copy + dead controls. Free-user pagination said "You must login to search for jobs" to logged-in users → now an upgrade message. The no-results "Reset search" button did nothing → now links back to a clean
/jobs. - "Add skills" CTAs now land in the skills editor.
/profile?add=skillsdeep-link opensAddEditSkillsModaldirectly (query preserved through both profile redirects); wired into the /jobs For You tab (both CTAs) and the dashboard For You section. Previously these dropped users on the profile page with no hint where skills live.
July 2026 (2026-07-10) — AI Chat fixed: markdown rendering, investor filtering, no more truncated answers
Connor's screenshot of /chat surfaced four defects, all fixed:
- Raw markdown. Assistant messages rendered as plain text (
###,**, bare[text](url)). Now rendered viareact-markdown+remark-gfmthrough a sharedChatMarkdowncomponent (typography-plugin styling; internal links go throughnext/linkwithprefetch={false}), used by both/chatand the slide-inChatPanel. - "Investors isn't a filterable field" — it is. The chat's tool schemas in
lib/chat/tools.tshad drifted from the MCP definitions: noinvestorsparam, so Claude told users the database couldn't do it and padded answers from training data. Addedinvestors(with full-firm-name guidance: "Andreessen Horowitz", not "a16z" — verified against the tags table),min/max_employeesto both search tools, andmin_salary+posted_within_daystosearch_jobs. System prompt now states investor filtering exists and forbids padding results from training data. - Answers cut off mid-sentence. Per-round
max_tokensraised 1024 → 2048 (session cap unchanged at 15K). - Glued paragraphs. Multi-round answers concatenated text across tool rounds without a separator ("…specifically:Good news…"). The client now inserts a paragraph break when a tool round starts.
July 2026 (2026-07-10) — Phase 10 begins: company-side monetization + candidate/company segmentation
First slice of Phase 10 (roadmap § Phase 10, unblocked by the funnel + churn signals — see now.md investigation #7):
- Profile field firewall formalized (
lib/companies/profileFirewall.ts). The descriptive-vs-objective boundary was previously implied by route field-omission; now one module sanitizes every company-authored write. Reps lost edit access tonotable_investors+funding_narrative(funding intelligence — the rep editor now explains why instead of showing the inputs). All rep writes stampprofile_source=company_submitted+profile_updated_at; the admin review queue lists company submissions alongside AI-enriched drafts. - Company MCP tools (server v0.8, 24 tools).
get_my_company(view counts, completeness, publish state) andupdate_company_profile(descriptive fields, partial updates) — gated by a newrequireCompanyClaimguard: activeusers_companiesclaim required, no candidate subscription needed. Candidate tools keep their premium gate; everything stays authenticated + rate-limited + audit-logged. All four docs surfaces synced (22→24). - Self-serve $99/mo checkout.
POST /api/company-rep/companies/[id]/checkout(rep-session-gated; the existingmetadata.type="company_profile"webhook branch handles fulfillment) + an upgrade card on the rep dashboard with a "Premium active" state. Previously the only path was an admin-generated payment link. NewisCompanyPremium()helper mirrorsisPremiumUser()on the company side. - Rep segmentation signal. Claiming a company now tags the user
user_types.type="employer"(candidate paths already wrote"candidate"). - Public company funnel. New
/for-companiesmarketing page (editorial layout: how-it-works, free-vs-$99 pricing, the trust-model explainer, FAQ); "For companies" section on/pricing; "For Companies" footer link; "Hiring?" one-liner under the homepage pricing card (kept quiet deliberately — homepage stays candidate-first per positioning.md). - Self-serve new-company creation. A rep signing in with an unindexed work domain (e.g. octavehq.com) now lands on
/companies/new: enter the company name, website pinned to the verified Google Workspace domain (the domain IS the verification), company row + email_domain + claim + employer tag created in one POST, straight to the dashboard. Guards: consumer domains rejected, existing-match race check, ambiguous-domain 409 routed to support, unique-slug generation. The claim page and company-login dead ends ("email connor@") now route here instead. Slack ping on every creation. - $99/mo = full MCP for up to 10 teammates. New
hasCompanyPremiumSeat(): an active claim on a company with an active subscription grants the full 24-tool MCP (everything candidates get for $25) to the company's first 10 active claimers — computed in the MCPverifyToken, accepted by the candidate-tool guard alongside candidate premium. No invite system: domain-match claiming IS the seat mechanism; cancelling revokes all seats at once; claimer #11 degrades gracefully to profile tools. Dashboard +get_my_companyshow seat usage;/docsexplains both tiers. - Claim-outreach export (
scripts/export-claim-outreach.ts). Instantly.ai-ready CSV of high-traffic unclaimed profiles with 30-day view counts + claim URLs — the "{N} people viewed {Company} last month" hook from gtm.md § Cold email outreach. Enriched-first by default.
Phase 10 hardening (same day, PRs #367–#368) — see company-signup-flow.md:
- Duplicate route fix (#367). Deleted stale
pages/for-companies/index.tsx($299/mo intake form). Production had been serving the wrong page alongsidepages/for-companies.tsx($99 self-serve) —next buildpicked one unpredictably. - Inline Google sign-in on
/for-companies.GoogleSignInButton— reps sign in without leaving the marketing page (callbackUrl=/companies/new). - Async LLM signup screening.
qualify-company-signupBullMQ job (Claude Haiku) on every self-created company; creation is never blocked — flagged signups go to Slack +/admin/company-signupsreview queue with one-click deactivate. - Legacy intake retired.
/api/intake/company-profile(old $299 intake) → 410, redirects reps to/for-companies. - Ops follow-up: Railway worker redeploy required so
qualify-company-signupruns in production.
July 2026 (2026-07-10) — MCP server v0.7: company comparison + saved-search email alerts
Four more tools (22 total), building on v0.6:
compare_companies— 2-5 slugs → one markdown table: what they do, HQ, founded, size, total raised, latest round, open roles, work model, tech stack, notable investors. Pure read overcompanies+premium_company_profiles; preserves the caller's column order and reports unknown slugs.save_search/list_saved_searches/delete_saved_search— "watch for staff-level AI infra roles in SF and email me weekly" is now one tool call. Writes the samesaved_job_searchesrows (and the same query-JSON shape) the website and the notification worker already use, so MCP-created alerts ride the existing daily/weeklysend-job-notificationsschedule with zero new infra. Default frequency weekly; capped at 25 saved searches per user; deletes are scoped to the caller's own rows.- All four docs surfaces synced (18→22):
/docsToolCards + new "Saved Searches & Alerts" section + v0.7.0 history entry,/mcpcatalog,/features/mcpstats.
July 2026 (2026-07-09) — MCP server v0.6: geo search + profile-from-text, landing-page copy un-stales
Two new MCP tools (the first two items of the MCP-server campaign), bringing the server to 18 tools:
find_companies_near_me— free-text US location (address, "City, ST", or zip) → Mapbox geocode → nearest companies by HQ via PostGIS KNN (company_locations.geo <-> point, 29K+ geocoded HQs). Returns distance in miles, one-liner, open-role count, profile link; optionalradius_milesandhiring_onlyfilters. The campaign's marketing differentiator — no other job-search MCP does geo.upload_profile_from_text— paste a resume / LinkedIn About / bio → one Claude extraction call (credit-metered likesummarize_company, BYOK-aware) → applies via the existingupdate_profile/add_work_experience/add_skillshandlers so validation stays in one place. Replies with what was written and what's still missing so the client model can follow up.- Docs surfaces synced per the CLAUDE.md rule:
/docsToolCards + count (16→18) + v0.6.0 version-history entry;/features/mcpstats. Also fixed drift found during the audit:/mcpandlib/mcp/tools.tsstill said "Seven tools" from v0.3 — the tool catalog there now lists all 18.
July 2026 (2026-07-09) — Map pin popup: anchored at the company, full company info
The /map company card used to be a fixed bottom-right overlay showing only name + "View company". Now it's a real mapboxgl.Popup anchored at the clicked pin (React card portaled into the popup via createPortal; chrome reset scoped under .company-map-popup in globals.css), and it carries the full company snapshot: logo, tagline (premium tagline/one_liner, falling back to truncated description), employee range, latest round + year, open-role count, website, and the profile link. /api/maps/feature/[id] enriched accordingly (also returns HQ lng/lat so sidebar-driven opens anchor correctly). Card extracted to components/maps/CompanyPopupCard.tsx to stay under the 500-line frontend cap.
July 2026 (2026-07-09) — Homepage funding-desk strip: screenshots of recently funded, hiring-now companies
New "Where the money just landed" section on the homepage (PR #341): top-6 venture rounds (Series*/Seed/Angel) closed in the last 12 months where the company is hiring today and has a website screenshot from the capture-company-screenshot pipeline. Each card links to the company profile. Data snapshots at build time via getStaticProps (lib/home/fundedSpecimens.ts) — no ISR; images use per-image unoptimized (no Vercel image-transformation cost); links prefetch={false}. ~223 companies qualify, ranked by round size, so the strip refreshes naturally with each deploy.
July 2026 (2026-07-08) — Diffbot credit budget corrected: 250K/month confirmed active, funding rounds back on Diffbot
A 2026-07-05 session concluded the Diffbot account had lapsed to a "250 credits/month free tier" and kicked off a provider-replacement effort (People Data Labs evaluation, Crunchbase Pro CSV import for funding rounds). Verified live against Diffbot's account API on 2026-07-08 (curl "https://api.diffbot.com/v4/account?token=$DIFFBOT_API_KEY"): that was a misread. The personal250 plan is active with 250,000 credits/month, free — usage history confirms it (254,000 credits spent across 2026-07-04/05 on 10,160 founder Person Enhance calls, i.e. ~25 credits per Enhance, before 429s began; 359,350 credits on 2026-06-17).
- PDL replacement dropped. Frontend PR #338 and go-cli PR #3 closed unmerged. Diffbot stays the primary source for company discovery, refresh, and founder enrichment.
- Funding rounds back on Diffbot. The Crunchbase Pro CSV import path is dropped (importer PR closed unmerged; the planned Crunchbase Pro subscription was never actually purchased, so there is nothing to cancel).
sync-funding-rounds-from-diffbotis primary again, with a dedup-key fix landing inemploybl-go-cli(match onamount_raised+currencyviaIS NOT DISTINCT FROMinstead of exactround_date— Diffbot shifts round dates ±1 day between pulls) plus a one-timededupe-funding-roundscleanup command. - Budget discipline documented (
data-pipeline.md§ Credit budget): ~25 credits per Enhance → ~10K enhances/month total; cycle resets ~the 4th, no rollover; currently exhausted until ~2026-08-03/04. Both recent cycles burned the full 250K in 1–2 unthrottled bulk days — new rule is ~2–2.5K enhances/week, and theenrich-all-foundersjob now caps each run at 2,000 founders by default (payload accepts alimitoverride) and reports the remaining backlog in its Slack summary.
July 2026 — Funnel-tracking regression found and fixed, testing + monitoring infra added
Digging into why Phase 13f's company-surface click-through looked terrible found the real story: the company page stopped rendering <BriefCard> on 2026-07-04 (a legitimate cleanup — see the Phase 17 entry below) and the replacement, GatedContent, shipped with zero analytics. The company page — the single highest-traffic surface — had no conversion-click visibility at all for 2 days; the 8,004 "impressions" in the original Phase 13f pass were dead-code telemetry, not a live rate.
GatedContentnow firesgated_content_impression/gated_content_cta_clickwithsurface/tracking_idproperties, wired into both call sites (company-page funding gate,FoundersSection's founders gate).- Closed the attribution loop. The gate's
fromquery param (e.g.company-funding-{slug}) was generated on every register link but never read anywhere. Now threaded intoregister_form_submittedanduser_signed_upPostHog properties. - New
checkFunnelHealthworker job (daily, 9am UTC): compares eachbrief_card_impressionsurface's volume against its own 14-day trailing baseline and alerts Slack if a surface that normally fires goes silent, plus cross-checks Postgres signups againstuser_signed_upevents. Would have caught this regression the same day it shipped instead of via manual investigation two days later. - Added a test runner. No test framework existed in this repo before now — added
vitest+@testing-library/react(npm run test, folded intonpm run check), with unit tests forBriefCardandGatedContent's tracking behavior so a future refactor that silently drops analytics fails CI instead of going unnoticed.
See roadmap.md § Phase 13f for the full investigation and the corrected (still directional, not final) verdict.
July 2026 — Spam filter gap patch + admin moderation tooling
Follow-on to the June 2026 signup-quality overhaul. Investigation of live signups found a concrete post-cutover escape (moviesdownload021@gmail.com, "Movies clips", via Google OAuth on 2026-07-05) that the name/business-token filter didn't catch, plus two tooling gaps that made it hard to act on spam even when spotted.
lib/auth/spamFilter.tsgained aspam_keyword_namecheck. Catches content-farm / crypto-slang "names" (download,movies,chain,web3,nft,official, etc.) that don't contain a business-name token or unusual-TLD email suffix. Documented as reactive/whack-a-mole, not exhaustive./admin/signupsprovider column was silently broken.authProviderwas derived fromgoogle_id, which the OAuth signup path (google-sync/route.ts) never populates — every Google signup showed as"email". Now derived frompassword === null.- Deactivation now actually works.
users.deleted_at(previously unused for users) is now checked inhandleLogin,resolveBearerToken(covers every bearer-token API route), and the Google OAuth re-auth path ingoogle-sync/route.ts— a deactivated account can no longer mint a new session through any path. NewPOST /api/admin/users/[id]/deactivateroute + a one-click "Deactivate" button on/admin/signups. - New
cleanup-unverified-signupsjob. Daily (7am PST), soft-deletes credential-path signups that never verified email within 14 days. Hard-floored at the 2026-06-30 verification-gate cutover date — the 4,318 pre-cutover users who never verified are historical noise, not spam, and are explicitly excluded from this job.
July 2026 — Phase 17 remainder: close the last public funding-data leaks
Audited the roadmap's stated Phase 17 remainder ("BriefCard's LAST RAISED callout and the /funding-rounds pages") and found that framing itself was stale — no BriefCard instance renders that callout anywhere, and /funding-rounds is a static page with no live data. Found two real, unflagged gaps instead.
- Company-page
StatStrip"Last raised" figure gated./companies/[slug]'s stat strip was showing the exact dollar amount above the fold, ungated, even after the funding-rounds section below it was gated the day before.buildCompanyCallouts()now takes ateaseFundingoption that swaps the amount for "Sign in to view" while keeping the round name visible. /api/companies/searchand/api/funding-rounds/searchnow require premium server-side. Both returned raw funding amounts and investor names with zero auth check at the API level — the frontend only self-gated client-side onisPremium(SWR key wasnullotherwise), so the routes themselves were a live, unauthenticated scrape target. Both now callisPremiumUser()and return 401 otherwise, matching the frontend's own trust boundary. No legitimate caller was affected (verified no other code calls either route).- Phase 18 (dashboard redesign) confirmed shipped. Git archaeology found it had already landed — bundled into PR #332 under a misleading title — so the roadmap never reflected it until now. See below.
July 2026 — Phase 16: traffic-ranked profile drafting at scale
Scaling premium company profiles from 10 toward ~100, prioritized by PostHog traffic (roadmap.md § Phase 16).
- Traffic-ranked target query.
lib/reporting/trafficRankedTargets.tsranks/companies/[slug]pages by PostHog page views, cross-referenced against publish/claim/Diffbot-cache status in Postgres. New CLI script (scripts/rank-companies-by-traffic.ts) plus an admin "Targets" page (/admin/premium-profiles) with a per-company "queue draft" action. - Drafting job now uses cached Diffbot data.
draftPremiumProfile.tspreviously used only Firecrawl + Claude; it now also reads the already-cacheddiffbot_companies.diffbot_response(customers, description, categories, investments — zero additional Diffbot cost) as corroborating context, and populatesnotable_customersfor the first time (the field existed in the schema and on the public page, but nothing wrote to it before). Falls back to Diffbot-only drafting when a site fails to scrape but a cache exists, instead of failing the whole draft. - New batch job, human-review gate preserved.
backfillPremiumProfiles.tsfans out drafts over the traffic-ranked list — it never auto-publishes (human review required). New admin "Review" page (/admin/premium-profiles/review) lists batch-drafted candidates (profile_source = enriched, unpublished) for a human skim-and-publish pass through the existing editor. - Fixed a real bug found along the way: company claims were never persisted. The Google OAuth claim flow (
lib/auth/nextauth.ts) recomputedtoken.companySlugon every JWT tick but never wrote anything to the database — a stateless per-session grant, not a durable claim. Now upserts ausers_companiesrow on a genuine sign-in match, so "is this profile claimed?" is finally a real, queryable fact./companies/[slug]shows a tasteful unclaimed-profile banner (copy differs for thin vs. already-enriched profiles) instead of an always-on "Claim this profile" link that ignored actual claim status. - PR #333.
July 2026 — Company profile fixes: broken screenshots, redundant brief, access gating
A batch of fixes to /companies/[slug], mostly found via live QA on the OpenAI profile.
- Website screenshot URLs fixed and backfilled.
captureCompanyScreenshot.tswas building virtual-hosted-style S3 URLs (bucket.s3.region.amazonaws.com/...), which silently fail to load because the bucket name (s3-images.employbl.com) contains dots — the wrong URL style for that bucket. Switched to the path-style URL used everywhere else in the codebase and backfilled all 685 existing rows via a one-off SQL rewrite. Screenshot now renders on every company profile regardless of premium-publish status (previously gated behindpublished_at, which meant it never showed for any of the thousands of unpublished profiles). - Removed the redundant "Section I · The Brief" teaser. It duplicated the masthead (title/subtitle/stat callouts) verbatim on every company page and showed a generic "locked" premium-profile table of contents even for companies with no premium profile at all.
- Founders and capital-raised sections now blurred for signed-out visitors. Same
filter: blur(5px)convention as job listings elsewhere in the app, with a "Sign up to see..." CTA. Logged-in users see everything. (First slice of Phase 17 access-control gating.) - Removed the Waymo sample-profile section from the homepage. The static "lead brief" demo card and its unused content/component were cut.
July 2026 — Phase 15h/i/j: screenshot capture, competitive landscape, founder deep profiles + Phase 18 dashboard redesign
Three data-side follow-ons to the June profile refresh, plus a full dashboard redesign that got bundled into the same PR under a title that didn't mention it (see the Phase 17 entry above — the roadmap only caught up to this on 2026-07-05).
- 15h — Website screenshot capture. New
capture-company-screenshotBullMQ job (ScreenshotOne → S3 →premium_company_profiles.website_screenshot_url), an admin trigger button on the premium profile editor, throttled to stay under ScreenshotOne's 20 req/min account cap (a burst of jobs tripped the limit almost immediately on first rollout).--early-onlyflag added to the backfill script to prioritize Pre Seed/Seed/Series A companies per city. - 15i — Competitive landscape section. New
competitive_context Json?field onpremium_company_profiles, an adminCompetitiveLandscapeSectionfor authoring competitor rows (name/stage/funding/differentiator), rendered as a structured table inPremiumProfileGroup E. - 15j — Founder deep profiles.
/companies/[slug]'s Leadership section now sources from Prismafounders(bio, prior companies, education pills extracted from cacheddiffbot_responsevialib/founders/extractDiffbotSummary.ts) instead of the Go API's name-and-headshot-only data. Falls back toceo_namewhen a company has zero founder rows. New admin "Enrich founders" button fans outenrich-founder-diffbotjobs. - Phase 18 — dashboard declutter + AI Chat promotion.
pages/dashboard/index.tsx's primary-action panel is now mutually exclusive (only one of preference card / onboarding / interests signal renders at a time). The old floating "Ask AI" button is gone, replaced by a prominent promo card with a tier-aware CTA; AI Chat moved fromSideNavbarslot #7 to slot #2. Three stackedCompanyListCards collapsed into one tabbed discovery card; MCP card moved below the fold. Dashboard +SideNavbarchrome swapped from indigo-gradient to the same ink/paper/tangelo tokens the public redesign runs on. - PR #332.
July 2026 — Fixed stale company-rep sessions on Google account switch
The jwt callback in lib/auth/nextauth.ts only recomputed the signed-in company via findCompanyForEmail inside if (user?.email), which is only true on the very first sign-in. Switching Google accounts in the same browser (e.g. signing out of an employbl.com account and back in with a personal Gmail) left the old token.companySlug in the JWT, routing the new account to the previous company's dashboard. Now recomputes the match on every JWT tick and unconditionally resets the company fields when there's no match. PR #331.
July 2026 — Beehiiv env hardening
The June auto-subscribe-at-signup feature had been silently no-oping for two months: BEEHIIV_API_KEY/BEEHIIV_PUBLICATION_ID were never set in Vercel, and subscribeToNewsletter() short-circuits on missing keys without throwing. 617 of 856 opted-in users never landed in Beehiiv as a result. Both vars are now required in production (optional in dev, same defense as the earlier NEXTAUTH_SECRET fix), and the subscribe helper warns once per process to Sentry when short-circuiting in prod so the next regression doesn't hide for months. Backfill script pacing bumped 120ms → 300ms after the first run hit Beehiiv's write rate limit. PR #328.
July 2026 — Vercel cost reduction: block preview deploys on non-main branches
Follow-on to the June cost cuts. The prior git.deploymentEnabled: { "main": true, "*": false } block in vercel.json was a misleading no-op — Vercel's object form takes literal branch names, not glob patterns, so every non-docs commit on any PR branch still burned build minutes.
ignoreCommandnow gates onVERCEL_GIT_COMMIT_REF. Any commit not onmainshort-circuits withexit 0before the diff-filter, and Vercel records the deploy as CANCELED without spinning up build resources.maincommits still respect the existing docs/scripts/worker/prisma/*.md/.github exclusion.- Dropped the dead
deploymentEnabledblock. The rule lives in the ignoreCommand where it can't drift from a dashboard toggle. PR #326.
July 2026 — MCP server v0.5: profile, preferences, recommendations, similar companies
The /api/mcp server picked up 9 new tools and a public documentation page. Server bumped 0.3.0 → 0.5.0 (16 tools total).
- Profile management (4).
get_profile,update_profile,add_work_experience,add_skills— read + update basic profile, work history, and skills over MCP. Selective-section fetch; Zod-validated input. - Preferences (2).
get_preferences,update_preferences— read + partial-updateusers.user_interests(seniority, industries, stages, locations, comp floor). - Personalized recommendations (2).
get_recommended_jobs,get_recommended_companies— Typesense-backed matching against the caller's preferences, optional reasoning mode, sort by recency / relevance / recent funding. Falls back to a helpful "set preferences first" message for users with an emptyuser_interests. - Similar companies (1).
find_similar_companies— look up by name or domain, return the target company plus curatedpremium_company_profiles.similar_company_slugs. Useful for competitive discovery. - New
/docspage. Full public catalog of all 16 tools with example queries, install steps for Claude Desktop, auth + rate-limit notes. Indexed for SEO and used as the onboarding target from the marketing surfaces. - Vercel build resilience. Landing this exposed a pre-existing OOM in
next build's ESLint step (recommended-requiring-type-checkingruns a full TS program per file → 7m12s to lint one file locally). Two fixes: aregisterTool<TInput>helper increateServer.tsthat erasesShapeOutputinference across the 16 tool calls, and--no-linton the build command so ESLint runs only in dev / manual passes. Type-check still runs duringnext build. PR #325.
July 2026 — Data pipeline observability + Diffbot founders backfill
Two independent workstreams landed together, both targeting the Sunday Slack pages about SLA breaches and OpenAI failures.
- Worker Sentry wiring. New
worker/lib/sentry.ts(thin wrapper over@sentry/nextjs) called fromworker/index.tsat boot.worker.on("failed")/worker.on("error")capture per-queue tags; fatal boot errors flush with a 2s deadline.getLocationFromOpenAInowSentry.captureException(err)inside its catch — the ~40% openai_failed skips are no longer silent. populate-job-listingsno longer fails on one flaky board. Rewrote the parent rollup to only mark the run failed when either the fatality rate exceeds 25 % or the run produced zero real work. A single Ashby 429 or a deleted board now shows up as a partial-failure with the top 5 board errors surfaced in the Slack summary, rather than flipping the whole 24 h SLA red.check-pipeline-healthaccepts productive-but-errored runs. SLA check now matchesstatus='success' OR (status='error' AND inserted+updated>0). Ends the "SLA page every 2 h until the next daily cron" loop.- Diffbot founders backfill. Revives the founders pipeline (dead since May 2024). New
backfill-founders-from-cachejob readsdiffbot_companies.diffbot_response.founders[]and upsertsfoundersrows keyed by(company_id, diffbot_person_id)— zero credits, pure DB fan-out over cached data. Companionenrich-founder-diffbotcalls Person Enhance (kg/v3/enhance?type=Person&refreshEntity=...) at 1 credit per founder, skipping rows fresher than 90 d. Wired into the admin scheduled-jobs page and/api/admin/triggerallowlist. - PR #324.
July 2026 — Profile-aware AI Chat + public-page conversion refinements
New standalone product: /chat — a profile-aware AI research assistant with access to Employbl's own data through 7 MCP tools. Built as a conversion + retention lever alongside further public-page polish.
/chatpage. Full-page chat under AppLayout. 4-column starter-prompt grid, streaming SSE responses with a tool-status pulse indicator, upgrade CTA for non-premium users, resume-upload nudge for anyone without a parsed profile.- Profile-aware system prompt.
lib/chat/candidateContext.tsassembles seniority, industries, target locations, skills, and comp floor fromusers+user_interestsand injects it as a context block — the agent defaults to the candidate's preferences without asking. - All 7 MCP tools available in-session.
findCompanies,searchJobs,getCompany,summarizeCompany,personalizeSearch,getJobListing,recentFundingRounds. ShareduseChatSessionhook powers both/chatand the in-appChatPanel. - Cost model corrected. Credit metering now bills Sonnet ($3/MTok in, $15/MTok out) instead of Haiku pricing — Sonnet is the actual model in use.
- AI Chat in the nav. New
SideNavbarentry; replaced Application Tracker inBottomMobileNavigation. Copy added to pricing feature list, registerVALUE_PROPS, and the homepage AI section. /companies/[slug]locked-content refinement. The black-bar redacted section is now a clean numbered section-name list + "Locked" indicator. Anon visitors see only 5 jobs with blur on jobs 4-5 and a "View all N roles →" CTA to/register.
June 2026 — Signup quality overhaul (spam floor + verification gate + LinkedIn upfront + domain-match)
The register funnel had three problems on one form: too much spam getting in, a bare experience for the real candidates who did make it, and no fork for people signing up with their work email. Shipped as one PR because the phases are load-bearing on each other (verification gates the trial that the spam accounts were consuming; LinkedIn feeds the enrichment that runs after verification; domain-match branches both the redirect and the verification-email copy). Baseline before ship: ~6% real-candidate rate on 49 signups/30d.
Spam floor
- New
blocked_registrationstable. Every rejection is logged with email, name, reason, and IP so we can tune the ruleset from data instead of anecdote. lib/auth/spamFilter.tsruns on both signup paths. Rejects business-name-in-name-field (SFI Insurance,Genre Sports), digits/URLs in name, role-email addresses (marketing@…,admin@…) on business domains, and.seo/.co/.aus/.us/.ltdsuffix locals likenconturbines.seo@gmail.com. Runs in bothhandleRegister(email/password) andgoogle-sync/route.ts(Google OAuth) — the OAuth path was letting the same spam through.
Verification-gated trial
- Verification email actually sends now.
worker/processors/sendVerificationEmail.ts— SES-based BullMQ processor on thenotificationsqueue, both candidate and domain-matched copy variants, logged vialogSentEmail. Enqueued from register + from the auth-gated resend endpoint. trial_ends_atmoved from register to verify-email success. Spam accounts that never verify never consume trial state, and/admin"active trials" reflects real intent. Same 7d window still applies.- Full-page verify wall on
/dashboard. NewVerifyEmailWallcomponent replaces the whole dashboard for unverified users with a resend button. Public/jobssearch stays open. - Diagnosis note on Google OAuth. The plan claimed "0 Google signups in 30d." Actually 7 happened in June — the regression was already resolved before this PR started. Left the audit note so future sessions don't chase the same ghost.
LinkedIn upfront + PDL enrichment
- Optional LinkedIn URL field on the register form.
linkedin.com/in/…pattern-validated in both react-hook-form and the Zod register schema, persisted tousers.linkedin_url, threaded throughsignUp()→register()→handleRegister. enrich-new-userBullMQ processor. Runs on first successful verification. Prefers LinkedIn URL as the query key (email hit-rate is much lower on the PDL free tier), best-effort — misses and gated-field responses are silent. Writes results touser_interests+candidate_skillsvia the sharedapplyEnrichmenthelper inlib/pdl/applyEnrichment.ts.- PDL wrapper bug fix.
lib/pdl/enrich.tswas callingdata.location_locality?.trim()— but PDL returns literaltrueon gated-tier fields, which threwTypeError. NewpickString()guard applied to every consumed string field. All response fields now typedunknownat the API boundary so the guard is the only way through. - Manual PDL "enrich my profile" card removed. With enrichment now running automatically post-verify, the dashboard's
ProfileEnrichCard(and itsPOST /api/auth/enrich-profilebacking route) were the manual escape hatch nobody needs anymore. Deleted both.PreferenceCardremains as the empty-state fallback for users whose enrichment missed or who signed up via Google OAuth.
Company-email fork
findCompanyForEmailextracted fromlib/auth/nextauth.tsintolib/companies/emailDomainMatch.tsso both NextAuth (Google reps) andhandleRegister(email/password) share one implementation of the domain→company match.- Domain-match at register.
handleRegistercallsfindCompanyForEmailbefore user create, inserts ausers_companiesrow on match, returnscompanyMatch: { slug, name }in the response. Frontend reads it and redirects to/companies/[slug]/claiminstead of/dashboard. - Verification email copy branches. Candidates get "Verify your email to start your Employbl trial." Domain-matched users get "Verify to manage {CompanyName} on Employbl" with a link to their company dashboard.
/api/auth/detect-companyblur-detect endpoint. Register-form email onBlur pings this; if it resolves, a nudge banner appears: "Looks like you work at {CompanyName}. Signing up will link you to their company page." Non-blocking — they can still proceed with password.
Migration docs/sql/2026-06-30-blocked-registrations.sql was applied to prod via psql before this PR opened; the Prisma schema is in sync.
June 2026 — Search/filter consolidation across /jobs, /companies, and global nav
Four UX issues from the live signed-in product, fixed together.
/companiesskeleton no longer gets stuck.RecommendedCompaniesSectionwas rendering a 6-cell loading skeleton forever when the/auth/recommended-companiesfetch errored — SWR returnsdata === undefinedfor both loading and error states, and the component only checked the former. Now treatserroras an empty result so the section disappears cleanly instead of bleeding gray boxes across the top of the page./jobskeyword search consolidated into the Filters drawer. The standalone "Search Tech Jobs" form (big orange Search button at the top of every results page) was pulling eyes away from the structured filters. Candidates come to the job board to discover roles, not to look up a known title. The keyword search is now a "Keyword" text input at the top of the Filters drawer on both desktop (JobSearchFilters) and mobile (MobileFilters), binding toquery.qvia the samereplaceInQuerypattern other filters use (commits on blur or Enter). The existing removable "Keyword(s): … ×" pill still works.- Job Locations filter now shows clean USA cities only. The autocomplete was sourcing from
job_listings_locations.citywhich contains dirty rows like"33 New Montgomery, 1100, San Francisco, CA"and"Babcock Hills Veterinary Hospital - San Antonio, TX". Regex/length filters caught some but not all of the garbage ("Bay Area","California","Nyc"slip through any cleaning rule). Replaced the SQL query with a curated static list of ~120 top USA cities (lib/server/us-job-cities.ts) sourced from the actual job-count ranking. The canonical search "Series A + San Francisco + Software Engineer" composes correctly:/jobs?q=software+engineer&raiseround=Series+A&job_locations=san+francisco_CA_US. - Global header search bar removed.
<GlobalSearchAutocomplete />was rendering in every authenticated page header — redundant with page-level filters. Removed fromLegacyNavbarand deleted the orphaned component file. Each page now shows just logo + premium badge + avatar in the header chrome.
June 2026 — Homepage production cleanup + MCP positioning + locations bug fix
Three production-quality fixes to the public site, shipped together.
- Homepage placeholders removed. The hero "PRODUCT SCREENSHOT — TO BE ADDED" box and the "DOSSIER SCROLL MOCK-UP — TO BE ADDED" box were shipping to prod as bordered empty
<div>s. Both are now commented out withTODOmarkers so the layout intent is preserved for whoever drops the real assets in. - "Dossier" → "company profile" everywhere. The word "dossier" was doing too much work as the central product noun (12 visible sites). All of them now read "profile" / "company profile" —
WHAT'S IN A PROFILE,INSIDE A PROFILE,Browse all 25,000+ profiles →, etc.STAT_ITEMSandINCLUDEDupdated to match. JSDoc cleanup inMasthead.tsxandStatStrip.tsx. - Hero repositioned as data platform + newsletter. Masthead kicker is now
EMPLOYBL · COMPANY INTELLIGENCE PLATFORM + WEEKLY NEWSLETTER; subtitle names the web, API, and MCP surfaces plus the Saturday newsletter explicitly. - MCP server now featured. New
HairlineSectionbetween Geographic Discovery and the stats strip —FOR AI AGENTS AND DEVELOPERS · Bring Employbl into Claude.with the install command and a link to a new/mcplanding page. /mcplanding page. New Pages-Router static page with the install command (OAuth + bearer alternative), the full 7-tool list with descriptions sourced fromlib/mcp/createServer.ts, auth/rate-limit/audit notes, and CTAs to/subscribeand/profile/mcp.- Shared install command. Extracted
MCP_OAUTH_COMMAND,MCP_URL,mcpBearerCommandintolib/mcp/install.tsso the homepage section,/mcppage, and the authenticatedMcpServerCard.tsxstay in sync. /company-locations/[slug]zero-results bug fixed. All city-state slugs on the homepage (miami-fl,denver-co,austin-tx,seattle-wa, etc.) were rendering empty tables because the slug parser inapp/api/_lib/companyCollections.tsblindly replaced dashes with spaces, turning"miami-fl"into"miami fl"which matched no rows in the DB. NewlocationSlugToCity()helper strips a trailing 2-letter US state code (checked against a fixed set so cities like "Santa Fe" don't get truncated) before converting remaining dashes to spaces. Miami's page now lists its 357 HQ-in-Miami companies.BottomCtasimplified. The "Stop guessing. Start knowing." trial-CTA block was removed (redundant with the masthead and pricing-card CTAs). The Beehiiv newsletter signup is retained.
June 2026 — Company profile refresh (Contrary-inspired) + similar-companies section
Full editorial rewrite of /companies/[slug] and its premium profile, in the visual language of Contrary's portfolio pages — quieter, more editorial, more data-dense.
- Sticky table of contents. Long premium profiles get a right-rail TOC that highlights the active section as you scroll.
- New founders section. Names + headshots pulled from the Go API, rendered as a proper hero block instead of an inline text mention.
- Editorial
PremiumProfile. Grouped narrative sections (product, tech & business, culture, hiring) replace the old flat card stack. Reviews removed — the signal wasn't there. - 15g — Similar companies. New
similar_company_slugs String[]field onpremium_company_profiles(migration applied); horizontal-scroll cards of logo + name at the bottom of the profile. Populated manually during profile authoring for now; auto-population via industry-tag cosine similarity is future work. - Phase 15h/i/j (website screenshot, competitive landscape, founder deep profiles) shipped 2026-07-03 — see the July 2026 entry above.
June 2026 — Resume-parse onboarding (Phase 9)
Prefill the candidate profile from a resume upload instead of making everyone pick pills by hand.
- Resume upload on
/profile(also surfaced insidePreferenceModalon first run). - Anthropic parse. Extracts
{ skills, seniority, cities, compMin?, summary }from PDF / DOCX text. - Diff-and-confirm UI. Extracted fields shown side-by-side with current values; user accepts or edits before saving.
- Writes flow into the scoring loop. Skills persist to
candidate_skills; the rest intouser_interests— drops straight into jobs-for-you scoring on the next request. - Out of scope, by design. No resume review, grading, or AI feedback. The point is to fill the profile, not judge it.
June 2026 — Admin conversion intelligence (Phase 14)
/admin had aggregate counts but no people. Every conversion question ("who churned?", "which signups never came back?") was a one-off SQL query. Five new admin sub-pages, each answering a specific funnel question at a glance.
/admin/signups(14a). Last 25 users with auth provider (Google vs email), trial-status chip, subscription check, saved-jobs count. Postgres-backed./admin/funnel(14b). Live PostHog funnel — brief impressions → register submit → signed up → subscription created. 5-minute in-memory cache on/api/admin/funnel./admin/cohorts(14c). Weekly cohort retention table (W+0/1/2/4/8). "Active" = saved a job or search in that window. Color-coded cells./admin/subscriptions(14d). MRR waterfall (new / churned / net) plus a list of churned subs in the last 30d with Stripe cancel reason./admin/subscribers(14e). Sortable table of every active subscriber: email, start date, days-to-paid, MRR, cumulative spend. Joins Stripe → Postgres.- Housekeeping. Extracted
CommandRunsTableto a component and broughtpages/admin/index.tsxback under the 500-line ESLint cap.
June 2026 — MCP OAuth 2.1 + Streamable HTTP transport
The /api/mcp server got two upgrades so third-party MCP clients (Claude Desktop, Cursor, etc.) can connect via standard OAuth instead of a hand-copied bearer.
- OAuth 2.1 PKCE server. New
/api/mcp/oauth/**routes implement authorization, token, and revocation with PKCE. /.well-known/oauth-protected-resourceroute. Standard discovery endpoint so MCP clients can auto-configure. Required a firewall bypass for.well-known/*.- Streamable HTTP transport.
/api/mcpsupports the newer Streamable HTTP flow alongside the legacy SSE transport; also required a firewall bypass. - UX and security follow-ups. Simplified the
/profile/mcpinstall flow (PR #317) and patched a set of OAuth 2.1 spec-compliance gaps in the same pass. /mcpmarketing landing page. Public-facing page with the install command (OAuth + bearer alternative), the full 7-tool catalog with descriptions sourced fromlib/mcp/createServer.ts, auth/rate-limit/audit notes, and CTAs to/subscribeand/profile/mcp.
June 2026 — Public landing redesign: "The Dossier"
We rebuilt every high-traffic anonymous landing surface to convert on the first pageview. Sessions average 1.13 pageviews — the offer has to land immediately.
BriefCardcomponent. A reusable editorial-brief module that appears above the fold on every public surface. Warm paper background, Fraunces serif headline, IBM Plex Mono data callouts, redacted "Section II — Restricted" reveals for bots and AI assistants to index (labels visible to search engines, bars visible to humans), tangelo CTA.- City pages (
/company-locations/[slug]) — previously had no above-fold CTA. BriefCard now appears above the company table with callouts for company count, open roles, top investor, and hottest employer. - Company pages (
/companies/[slug]) —CompanyUpsellHeroreplaced by BriefCard with founded, headcount, open roles, and last funding round callouts. - Job pages (
/jobs/[slug]) — 200–500 anonymous views per top job, previously no above-fold offer. BriefCard now appears above JobHeader for unauthenticated visitors, with location, company size, posted date, and ATS source callouts. Six redacted sections (comp band, equity, interview rubric, hiring manager, team context, offer timeline). - Company collections (
/company-collections/[slug]) — BriefCard added above the company grid. - New editorial nav.
MarketingNavreplacesLegacyNavbaron public pages — a SaaS-style top nav with Companies and Tools dropdowns, no search form for anonymous users (search is a premium feature). - Homepage rewrite. Dead search bars removed, new
Herocomponent, trust stats bar, "Filter by what actually matters to you" section. - Feature landing pages.
/job-application-tracker,/profile,/funding-roundsnow have proper marketing landing pages with conversion CTAs. - Always sidebar when logged in.
MarketingLayoutproxies toAppLayoutfor authenticated users — all 36+ public pages automatically show the sidebar once you're signed in. - PostHog telemetry.
brief_card_impression,brief_card_cta_click,brief_reveal_hoverevents fire on every BriefCard surface.
June 2026 — Company logo backfill
We now have logos for 30,498 of 30,709 companies (99.3% coverage).
- Previous coverage was incomplete — Clearbit (acquired by HubSpot) shut down its logo API and Diffbot proxy URLs had expired.
- Google faviconV2 API (
t0.gstatic.com/faviconV2) used as the primary source, with a size filter (>800 bytes) to reject generic fallback icons. - Direct
/favicon.icofetch added as a first-pass strategy for newer companies that Google hasn't indexed yet — picked up an additional 17 logos that the Google API missed. - S3 pipeline. Logos uploaded to
s3-images.employbl.com/company-images/{slug}-logo.png,image_url_s3andimage_urlupdated in thecompaniestable. - 211 companies remain without logos — all are either very new startups or sites without accessible favicons. Script is idempotent and can be re-run as coverage improves.
- Script lives at
scripts/backfill-company-logos.ts.
June 2026 — Auto-subscribe to the newsletter at signup
We wired the "subscribe to the newsletter" checkbox on /register directly into Beehiiv. Before this, the checkbox just set a flag in our database; growing the list meant manually exporting CSVs and re-importing them — which is why ~850 opted-in candidates were sitting on the wrong side of the wall.
- New signups go straight into the publication. Opted-in registrations POST to Beehiiv's v2 subscriptions API at signup time. Fire-and-forget — registration never blocks on Beehiiv being down.
- Backfill script for the historical opt-ins (
scripts/backfill-beehiiv-subscribers.ts). Dry-runs by default; refuses to apply past the Launch plan's 2,500 cap. - No change for Google OAuth signups — we never captured an explicit newsletter opt-in on that path, so they're not enrolled.
June 2026 — Maps: see where AI startups are hiring
We launched a full Mapbox-powered map of where the AI startup ecosystem is hiring, end to end.
- New
/mappage. A single interactive map you can pan across the US. Toggle between companies and an open-jobs heatmap, filter by metro, and click any pin for the company or role. Search inside the map to jump to a company by name. - City landing pages. Dedicated pages for the nine biggest tech metros — NYC, SF, LA, Seattle, Chicago, Austin, Miami, Denver, San Diego — each with live company counts, top hiring companies, and the neighborhoods they cluster in. Great for browsing, great for search engines.
- Mini-maps on every company and job page. Premium and trial members now see an embedded map on
/companies/[slug]and/jobs/[slug]showing HQ, branch offices, and on-site role locations — no more squinting at an address. - Search & filters synced to the URL. Send a teammate a link to a filtered view of the map and they'll see exactly what you see.
- Backend. 29K+ company locations and 45K+ job locations now have precise lat/lng coordinates, powered by PostGIS and a US cities lookup table.
- Retired the old Leaflet map.
/companies/mapand the state-level maps now redirect to the new/map.
June 2026 — Reverse trial + marketing site overhaul
We rebuilt the path from "landed on Employbl" to "paying subscriber."
- 7-day free trial, no credit card. New accounts get full premium access for seven days with no payment up front. A trial countdown banner lives on the dashboard, with an urgency nudge in the final 48 hours.
- Welcome email drip. A 5-day onboarding email sequence walks new candidates through the features they're most likely to convert on.
- New homepage hero. Sharper positioning ("Get the inside track on AI startup hiring"), a showcase of premium company profiles, and a single "Start free trial" CTA.
- Rewritten pricing page. Accurate feature list, FAQ, and a real explanation of the $25/mo value prop.
- Redesigned register page. Two-column layout with the value props and testimonials right next to the signup form.
- Trial-end /subscribe page. When a trial ends, the new
/subscribepage recaps what the user actually built — skills added, companies watchlisted, jobs saved, applications tracked, searches saved — alongside a 7-day refund guarantee, testimonial, and FAQ. - Sticky conversion bars on company, job, and blog pages prompt anon visitors to register.
- Pricing link in the footer and an upgrade CTA in the sidebar for expired-trial users.
June 2026 — Smarter onboarding and personalization
A first-time experience that actually learns what you're looking for.
- First-run preferences modal. New users pick seniority, target compensation, and locations on day one. Those preferences feed directly into the "For You" job feed.
- Editable preferences on your profile. Update seniority, comp expectations, target cities, and skills any time.
- Profile prefill from your resume context. New accounts get skills and seniority prefilled from People Data Labs where possible, so the profile isn't blank from minute one.
- Better "For You" scoring. Seniority and salary now count toward how a job is ranked, not just skills and location.
- Cleaner dashboard. Featured companies always show a logo. The onboarding cards collapse once you've completed them. The "personalize your job feed" prompt disappears once you have at least two skills.
June 2026 — Companies: claim your profile
Recruiters and founders can now own and edit their company's Employbl profile.
- Sign in with Google. Companies log in with their work Google account; we use the email domain to verify they belong to the company.
- Claim flow.
/companies/[slug]/claim→/companies/[slug]/dashboard→/companies/[slug]/edit. - Company dashboard. See PostHog page views and unique visitors for your profile over the last 30 days, your open job count, and a profile completion bar.
- Self-serve editor. Edit your company's description, tags, locations, and other profile fields directly — no more emailing us to fix typos.
- Candidate Google login. Job seekers can now sign in with Google too.
June 2026 — Editorial blog redesign
We rebuilt the blog as a real publication, not a knowledge-base sidebar.
- Editorial homepage. New
/blogindex with hero post, secondary features, and a clean grid of recent posts. - Redesigned post pages. Typography, layout, and inline-CTA system rebuilt from scratch.
- AI-generated post images. Every published post now gets a unique header image, generated via gpt-image-1.
- Better link rendering. Inline links inside post bodies now render properly.
- Premium company profile upgrade. The ten flagship premium profiles (Waymo, Sierra, Hightouch, Vestwell, Decagon, ElevenLabs, ClickHouse, Vanta, Ramp, Verkada) got a content and layout refresh.
June 2026 — Faster, more reliable search
A multi-week migration to Typesense for instant search across our 25K+ companies and 61K+ jobs.
- Typesense-powered search for companies, jobs, and funding rounds with sub-100ms results.
- In-map search. Find a company by name from inside the
/mappage and the camera jumps to it. - Funding rounds search trimmed to the most recent 5 years, dramatically faster and no more out-of-memory crashes.
June 2026 — New background job system
We migrated all of our background jobs (job board sync, company aggregates, email sends, enrichment) off Trigger.dev and onto a self-hosted BullMQ + Redis stack on Railway. End users see this as: fewer stalled syncs, faster recovery from failures, and a much shorter feedback loop on data issues.
- New
/admin/pipelinepage. A live operational view of every queue, every recent run, every worker — with one-click "run now" buttons. - Daily job sync now chains into company aggregates. Funding totals, headcount, and other aggregates refresh automatically the moment a sync completes.
- Hardened the data pipeline. Better handling of missing Ashby boards, smarter fan-out, automatic zombie cleanup.
June 2026 — Vercel cost reductions
Several rounds of infrastructure cleanup to keep hosting costs sustainable.
- Killed the
/jobs/search/*route that was burning a disproportionate share of the bill. - Tightened ISR settings on programmatic SEO pages.
- Disabled per-branch preview deploys to save on build minutes.
- Stopped polling
/api/auth/sessionon anonymous traffic. - Per-route function execution caps.
- Server-side auth cookies (previously set client-side, which doubled some requests).
Not user-visible, but it's why we can keep the lights on.
Earlier in 2026
For older work — Phase 1–5 (admin tools, premium profiles, candidate auth, jobs-for-you scoring, content ops) — see git history.