Skip to content

System Architecture

Birdhouse is made of two parts:

  1. A visual interface in Strapi CMS — where non-technical teams (Ops, BizDev, Product, Marketing) assemble reusable building blocks to configure questionnaires, policy detail pages, and vertical metadata
  2. A technical solution built with code — a sync pipeline that pulls CMS content into the app, the qnr-framework rendering library, and app-level components that bring configurations to life in the browser

Before Birdhouse, all of these building blocks lived entirely in code maintained by Engineering. Adding a question, changing a policy page section, or updating a vertical’s branding required a pull request. Now these are reusable components in Strapi that can be assembled without technical expertise.

Under the hood, the technical side is a pipeline of three systems that each handle a different concern: authoring, building, and rendering. Understanding how they connect helps you reason about where to make changes and what happens when something goes wrong.

URL: strapi.product.f14e.com/admin

Strapi is a headless CMS where Product and Ops teams define everything about a questionnaire: its questions, rules, quote pages, pricing, and verticals.

The Strapi instance is heavily customized for Birdhouse:

Aspect Details
Content types 28 types including Questionnaire, Vertical, Quote-Page, Quote-Price, Table, Policy-Page, and more
Question components 55+ component types in the questions dynamic zone — from simple inputs (radio, date, text) to complex screens (quote, checkout, processing)
Custom plugins questionid-selector (autocomplete for question IDs), quote-price selectors, preview-button, and others
API transforms Custom service layer (questionnaire.js) that reshapes data before the API returns it

When you edit a questionnaire in Strapi, you are working with a structured data model — not writing code. The CMS enforces validation, provides a visual editor, and manages draft/published states.

The sync pipeline is a set of CLI scripts that bridge the CMS and the React app. They pull data from the Strapi API and generate TypeScript files that the app can import directly.

Key scripts:

Script Purpose
sync:questionnaire Fetches a questionnaire from Strapi and generates a .ts file in apps/app/src/syncedData/
sync:questionnaire:reverse Reads a local .ts file and pushes changes back to Strapi
sync:vertical Syncs vertical definitions, generating TypeScript lookup maps

The generated files are checked into the repository. This means a questionnaire change goes through the normal code review and deployment process, providing a safety net before changes reach production.

@getpopsure/qnr-framework is a React library that takes a questionnaire definition (the generated TypeScript file) and renders it as an interactive, multi-step flow in the user’s browser.

Core concepts:

Concept Role
Questionnaire The top-level React component that orchestrates the entire flow
QuestionnaireRouter Manages URL-based navigation between questions
Rules engine Evaluates conditional logic on each answer to determine the next question
State manager Tracks all user answers and determines which questions are complete

The app extends qnr-framework with custom components specific to Feather’s needs — things like GenericCheckout (Stripe integration), Processing (backend policy creation), and various custom quote page components.

Birdhouse provides three distinct builders, each with its own set of building blocks in Strapi:

The most mature builder. It provides building blocks for defining multi-step flows:

Building block Examples
Question types Radio, date, text, address, number, currency, and 50+ others
Screens Quote page, checkout, review, blocker, intro, success
Rules Conditional logic that controls navigation between questions
Calculated fields Derived values computed from user answers
Validations Input constraints applied to individual questions

These building blocks can be combined to create signup, claim, adjustment, and cancellation flows for any vertical.

Configures what users see when managing an existing policy in the app:

Building block Examples
Sections Groups of information displayed on the policy page
Buttons Actions available to the policyholder (e.g., file a claim, cancel)
Modals Informational overlays triggered by user actions
Conditional rules Visibility logic that shows or hides elements based on policy state

Manages product-level metadata that is reused across the app:

Building block Examples
Visual assets Color codes, icons, illustrations
Descriptions Product names, taglines, plan names
Policy modals Content for policy-related informational overlays
Regions Geographic availability configuration
flowchart LR
    A["<b>Strapi CMS</b><br/>Content editors define<br/>questionnaires, rules,<br/>quote pages, verticals"] -->|REST API| B["<b>Sync Scripts</b><br/>CLI tools fetch data<br/>and generate TypeScript"]
    B -->|Generated .ts files<br/>committed to repo| C["<b>React App</b><br/>Imports questionnaire<br/>definitions at build time"]
    C -->|Passes definition to| D["<b>qnr-framework</b><br/>Renders multi-step flow<br/>with rules & state"]
    D -->|Interactive UI| E["<b>User's Browser</b><br/>End user answers<br/>questions & signs up"]

Product teams own the content (Strapi). Engineers own the rendering logic (qnr-framework and app code). The sync pipeline is the controlled handoff point between the two.

Because synced files are committed to the repo, every questionnaire change goes through a pull request. This catches issues like broken rules, missing translations, or incorrect question IDs before they reach users.

Questionnaire definitions are bundled into the app at build time — there is no runtime API call to load a questionnaire. This means instant rendering with no loading spinners.

The CMS handles the common case (standard question types, rules, screens), while the app codebase handles anything that needs custom behavior. You can mix CMS-defined questions with hand-coded components in the same flow.

What Where
Questionnaire definitions (CMS) Strapi admin → Content Manager → Questionnaire
Generated questionnaire files apps/app/src/syncedData/
Sync scripts apps/app/src/syncedData/scripts/
qnr-framework source @getpopsure/qnr-framework package
Custom app components apps/app/src/features/
Vertical definitions (CMS) Strapi admin → Content Manager → Vertical
Quote page definitions (CMS) Strapi admin → Content Manager → Quote-Page