NewIntegration pages redesign + main sitemap fix
The 138 /integrations pages got a full visual upgrade per DESIGN-GUIDELINES: hero product artifact (sample contact form → Zapier bolt → destination-app card with live-feed rows), subtle grid texture, stats strip, iconed workflow cards with a "Popular" flagship badge, a field-mapping panel artifact (form fields → app fields), numbered setup steps, animated-caret FAQ, app-monogram related pills, and a charcoal closing CTA band. Verified by actual headless-Chrome renders (desktop full-page + narrow), which caught unsized inline SVGs rendering giant. Sitemap fix: formbuilder.com/sitemap.xml was silently serving the SPA's index.html (Pages fallback shadowed the worker route — crawlers got HTML, not XML). The generator now writes a static sitemap.xml with 29 marketing/help URLs plus all 304 template pages (ids extracted from the same src/data/templates/*.js the template pages render from), 333 URLs total; static files always beat the SPA fallback.
NewZapier integration: real REST-hooks app + public API expansion
FormBuilder now supports a genuine Zapier app (code in new zapier/ dir, Zapier Platform CLI) instead of the old paste-a-webhook-URL approach. New v1 API surface behind the existing developer tokens: GET /v1/me (auth test), GET /v1/forms/:id/fields (field schema → Zapier dynamic output fields), POST /v1/hooks + DELETE /v1/hooks/:id (REST hook subscribe/unsubscribe, new api_hooks table, migration 0018, per-user cap 100, https + anti-SSRF validation). Triggers: New Form Response (instant — dispatchApiHooks fires from the submission pipeline alongside existing integrations; held over-cap responses never fire; 410 Gone auto-deletes dead hooks; deliveries logged to integration_logs as zapier_hook) and New Form (polling). New hooks:manage token scope; revoking a token deletes its hooks. Settings gained an "API & Zapier" card (create/copy/revoke keys — first UI for the token system); the editor's webhook panel links to it. Free on all plans (matches Tally/Jotform/Typeform). Zapier app still needs zapier register/push from the Zapier account (see zapier/README.md).
NewProgrammatic SEO: 138 static integration pages
New build-time mini-SSG (frontend/scripts/generate-integration-pages.mjs, runs after vite build) renders /integrations/ (hub), /integrations/zapier/ (pillar), and 136 /integrations/zapier/<app>/ landing pages ("Connect FormBuilder to Salesforce/Slack/Notion/…") from a single dataset (zapier-apps.mjs: unique per-app blurbs + category use-case templates + custom flagship use cases). Fully static HTML on Cloudflare Pages — crawlable without JS, editorial design system, FAQPage + BreadcrumbList JSON-LD, per-category internal linking, sitemap-integrations.xml, robots.txt updated (static + worker), footer "Integrations" link. Growing the page set = appending to the dataset and rebuilding.
NewOwner-facing form analytics page
New /forms/:id/analytics page (linked from the responses page header) finally surfaces the analytics the API was already collecting but never showing anyone: views/starts/submissions/completion-rate stat tiles, a 30-day daily views-vs-submissions trend chart (inline SVG with hover tooltips, colorblind-validated amber/emerald palette with dashed-vs-solid line encoding), a respondent funnel (viewed → started → submitted with rates), and top countries. The existing GET /api/forms/:id/analytics endpoint gained funnel, daily (30-day series merged from product_events views and form_responses submissions), and quiz fields. No schema changes.
NewQuiz performance aggregates for owners
The analytics page shows a quiz block when a form has graded responses: average score, average percent, graded-response count, and a score distribution (0–19% … 80–100% buckets). Computed by new summarizeQuizScores() in the scoring service (unit-tested, 3 new tests) over the most recent 1,000 responses' stored metadata.scoring.quiz.
NewAutosave + resume for respondents
On forms with "save progress" enabled, answers now autosave to the respondent's browser (localStorage, 800ms debounce, 7-day expiry) as they type; returning respondents get the restore prompt (the previously-orphaned ResumeFormModal, now wired in) offering to continue where they left off, including the right page on multi-page forms. Cleared on submit or discard; server-side resume links (the existing manual "save progress" flow) take precedence when present. Closes the gap where leaving without clicking save lost everything.
NewQuiz functionality: auto-graded questions with respondent scores
Radio, select, checkbox, and text fields can now be marked as quiz questions in the builder (correct answer + points, checkbox answers require the exact set, text matches case-insensitively). Grading runs server-side on submission via the existing scoring service and is stored in metadata.scoring.quiz on each response. Respondents see a score card on the thank-you screen ("7 / 10 — 7 of 10 questions correct") unless the owner turns off the new "Show Quiz Score" toggle (settings → General, appears once a field has a correct answer; stored in new forms.quiz_settings column, migration 0017). Owners see the score plus per-question Correct/Incorrect badges in the response detail modal. Security: answer keys live in field settings and are stripped from all public form payloads — respondents can never fetch the key, and grading happens server-side only.
New47 new templates closing every AI-demand gap + new Logistics category
The July 24 template review cross-referenced ~120 production AI-generator prompts against the library and shipped templates for every recurring theme with no coverage: Education 4→16 (multiple-choice quiz, exam answer sheet, class test, student assessment, assignment submission, primary school enrollment, daycare registration, college application, attendance sheet, parent-teacher conference signup, field trip permission slip, mentorship matching); new Logistics category (delivery note, delivery order, proof of delivery, delivery incident report, delivery driver application, courier pickup request); audits & inspections (store audit, site survey, internal audit checklist, RCSA, workplace safety inspection, general inspection checklist); HR 6→13 (supervisor feedback — the demanded supervisor-selection variant, leave request, overtime request, shift swap, performance self-review, workplace incident report, onboarding checklist); Food & Beverage 3→9 (restaurant customer review with dine-in/takeaway/drive-through split, takeaway feedback, online food order, private event booking, menu item survey, delivery driver signup); plus refund request, roadside assistance, car rental booking, equipment rental, visitor sign-in sheet, group volunteer registration, dance workshop registration, early access signup, dormant account reactivation, and training feedback survey. Every template has a distinct theme (varied palettes and font pairings). Contract test extended with the full id list. Note: quiz/exam templates collect answers only — no grading engine yet (next conversation).
FixedTemplate search analytics never logged
logTemplateSearch existed and the /api/template-search endpoint worked, but no page ever called it, so the admin Template Searches dashboard has been empty since launch. The templates page now logs settled searches (800ms debounce) with result counts, so future template-gap decisions can run on real zero-result search data.
FixedUnsubscribe audit: every recurring email now has a logged-out opt-out
Conditional-notification recipients (form-integration emails with no stored recipient row) previously had NO unsubscribe link; they now get an HMAC-signed /unsubscribe-notifications/:payload link (login-free, tamper-rejecting, warm-paper pages) with opt-outs persisted in KV and filtered before every send. Backfilled the one production recipient row missing an unsubscribe token. All three unsubscribe flows (marketing/lifecycle, per-form recipient, conditional) verified live and logged out end-to-end.
FixedEmail round 2: full lifecycle coverage + deliverability hygiene
(1) Create nudge (code 100): signed up 24h+ ago with zero forms → starter ideas + template/AI links; completes the nudge ladder (signup→create was the funnel's biggest single drop at 47%). (2) Weekly digest (code 103, Mondays): owners whose forms had views/responses in the last 7 days get "Your week on FormBuilder: X views, Y responses" with a per-form table; quiet accounts get nothing. (3) Unsubscribe hygiene: all lifecycle sends now carry an unsubscribe footer to the (restyled, warm-paper) /marketing-unsubscribe page; List-Unsubscribe header not possible with the current EMAIL binding shape (no headers field) — revisit if the binding changes. (4) Bounce suppression: provider-reported permanent bounces are logged as bounced, and any recipient with a bounce or 3+ failures and no successes in 30d is skipped (suppressed log status) to protect sender reputation. (5) Deleted the never-wired welcomeSeries day-2/5 drip templates and sendWelcomeSeriesDay dead code. Admin preview endpoint now sends all five sample emails.
FixedLifecycle activation emails
The product now follows up after the two moments where funnels die. Daily cron sends (once per user, future accounts only, marketing_emails_enabled respected): a draft nudge when a real draft (3+ fields) sits unpublished for 24h with nothing else live, and a share nudge when a published form still has zero views after a day — including the live link, a mailto share shortcut, and a deep link that reopens the share modal. The first response a form ever receives now sends a celebration variant ("🎉 Your first response") with momentum copy. All email templates restyled to the editorial design system (warm paper, charcoal CTAs — the retired blue chrome is gone from every email). Send history in welcome_series_emails (codes 101/102); admin preview endpoint POST /api/admin/lifecycle/preview-emails.
FixedDashboard visibility for the zero-views problem
Published form cards now show view counts, and a live form with 0 views and 0 responses gets an amber "No views yet — share your link" callout that opens the share modal. Publishing from anywhere on the dashboard now opens the share modal immediately (the share moment), instead of just a toast.
FixedPersistent Share in the editor
A published form's editor nav now leads with a charcoal Share button (reopens the share modal any time); Unpublish is demoted to a quiet text action. Publish/Share buttons restyled from retired blue to charcoal.
ImprovedFree plan form cap now binds at publish, not create
Drafts no longer count against the 3-form free cap (production data: 72 free users were locked out of creating anything, 49 of them without ever publishing; templates — the #1 creation method — hit an upsell wall). Creation is now effectively unlimited (anti-abuse ceiling of 100 total forms), and the cap is enforced when transitioning a form to published (402 PLAN_FORM_LIMIT_REACHED, counts other published forms only so republishing never trips it). Removed the client-side 3-form block on the templates page.
FixedDashboard "Get your first response" alert could unpublish a live form
The alert targeted forms[0] blindly: its "Publish" button toggled publish state (silently unpublishing an already-live form) and "Copy link" could copy a draft's dead URL. It now spotlights a live form awaiting its first response (Copy link / Share / Send test) or the newest draft (Publish, which then opens the share modal), and never copies unpublished links.
Fixedanonymous_publish_instant events were silently dropped
The instant-publish success event fired by the frontend was missing from the server's allowedEventNames whitelist, so the new flow's key metric never recorded. Also: template usage now fires template_selected and form_created product events (the #1 creation path was invisible in the create funnel).