Metior Pro logo

Metior Pro

Proptech Toronto, CA Website Published May 27, 2026 · Updated May 28, 2026 A Benmore Technologies case study

Metior Pro: The AI-Native Quoting Operating System for Construction Contractors

A Benmore Technologies case study, in partnership with Metior Pro.

Metior Pro banner


1. Introduction

Overview

Metior Pro is an AI-native quoting, bidding, and back-office operating system for residential and light-commercial construction contractors. From a single platform, a contractor can generate a quick estimate in under five minutes, run an AI extraction pass over a 40-page blueprint PDF to pull every quantity automatically, manage a full customer CRM with follow-ups and activity history, convert any quote into a Stripe-backed invoice with a one-click payment link, and pull real-time material pricing from a pan-Canada library, all from a phone, an iPad, or a desktop browser.

Available on the web, on iOS, and on Android (via Capacitor), Metior Pro replaces a stack of disconnected tools (Excel, QuickBooks, manual blueprint takeoffs, paper follow-up lists, and ad-hoc Home Depot price checks) with a single product built around one principle: a quote you can ship in five minutes wins more contracts than a quote you can ship in two days.

Metior Pro home dashboard

Challenge

Building a contractor-grade platform that:

  • Turns a 40-page architectural PDF into a priced, line-itemized bid in minutes without the contractor losing trust in the math
  • Holds a few-cents-per-page AI extraction budget while staying within ±10% of a human takeoff
  • Bills construction work through per-tenant Stripe accounts so every contractor's revenue lands in their own bank, with Benmore never touching the funds flow
  • Ships a single React codebase to web, iOS, and Android with offline-first quote creation for job sites with no signal
  • Survives the round-trip data-corruption traps that every multi-client SaaS eventually hits
  • Stays compliant with Stripe webhook idempotency, JWT refresh, optional 2FA with grace periods, and 2026-era WCAG accessibility on every screen

2. The Problem

Background

The North American residential construction market is worth ~$1.1 trillion annually, and the typical small builder (one to fifteen employees) wins roughly one in eight to one in ten of the bids they submit. Every bid that doesn't convert is sunk cost: estimator hours, blueprint review, pricing research, drive time to the site, and the inevitable back-and-forth of revisions.

Industry data puts the fully-loaded cost of producing a single construction quote between $50 and $125 when you account for estimator time, pricing research, blueprint review, and two or three rounds of revisions. At a typical small-builder cadence of 8 to 10 quotes per won contract, that is $500 to $1,000 of overhead burned to land a single job, before a single nail is driven.

Compounding the problem: speed wins. Contractors who can put a credible, line-itemized estimate in a homeowner's hand within 24 hours of the site visit close at materially higher rates than those who take a week. The quoting process is simultaneously the most expensive thing a small builder does and the place where competitive advantage is won or lost.

Pain Points

  • Quote production cost is the silent budget killer. A builder generating 10 quotes a month to win one contract spends $5,000–$10,000 a month in estimator overhead just to keep the pipeline full. Most of that work goes into bids that lose.
  • Blueprint takeoffs are slow, error-prone, and don't scale. Manually counting outlets, windows, doors, and fixtures across a 40-page PDF takes hours and is the single largest line item in estimating labor. Off-the-shelf takeoff software is desktop-only, six-figure-licensed, and built for full-time estimators, not for the owner-operator who is also driving the truck.
  • Pricing data goes stale instantly. Lumber, drywall, copper, and insulation prices move weekly. A pricing spreadsheet built in January is wrong by March. Calling the supplier desk every time blows the speed advantage.
  • Customer history lives in five places. The contact is in the phone, the quote is in Google Drive, the invoice is in QuickBooks, the follow-up reminder is on a paper sticky note, and the site photos are in the camera roll. Every new conversation starts cold.
  • Payments are a separate workflow. Most quoting tools stop at "send the PDF" and force the contractor into a different product to collect a deposit, take final payment, or reconcile a refund. The handoff between quote-accepted and money-in-the-bank is where contracts go to die.
  • Mobile is an afterthought. Estimating happens on a job site, on a phone, often with no signal. Tools built for a desktop browser break the moment the customer asks for a price while standing in the basement.
  • Compliance, trust, and accessibility are non-negotiable. Storing customer phone numbers, sending SMS notifications, processing card payments, and serving a homeowner-facing quote portal all carry regulatory weight. Getting any one of them wrong loses the contract before it starts.

The downstream impact: small builders were buying point-solutions for each piece of the workflow, stitching them together with email and spreadsheets, paying for five SaaS subscriptions to do the work of one, and still losing 8 of every 10 bids because the next builder over delivered their quote in 24 hours.


3. Our Solution

Discovery Process

The founding observation was simple: the quote is the unit of work. Everything a small builder does upstream (site visit, blueprint review, pricing lookup, customer conversation) exists to produce a quote. Everything downstream (invoicing, follow-up, payment, project history) exists to convert that quote into revenue.

If we put the quote at the center of the architecture and built every adjacent system to either feed it (Blueprint AI, pricing library, customer CRM, AI chat) or convert it (invoicing, payment links, QuickBooks sync, follow-ups), we could collapse the five-app stack into one product without ever losing the through-line.

We narrowed the platform around one principle: a contractor's time is the most expensive input. Every feature has to either reduce the time-to-quote, increase the win rate per quote, or compress the time-to-cash on a won contract, or it doesn't ship.

Metior Pro dashboard

Core Value Proposition

Give a small builder one platform that produces an accurate, line-itemized quote in minutes (instead of days), tracks every customer and follow-up in a real CRM, converts won quotes into Stripe-paid invoices in one click, and runs on the phone in the contractor's pocket, so the $500–$1,000 of overhead burned to win one contract drops to a fraction of that, and the win rate goes up because the quote ships first.

Proposed Solution: The Five Pillars

  • Quick Quote Builder. A five-step wizard that produces a complete, customer-ready quote in under five minutes: template-driven line items with per-line auto-calculation, AI-generated scope descriptions via GPT-4o, real-time pricing from the pan-Canada library, offline-first sync for job sites with no signal, and a customer-facing portal where homeowners accept or reject in their browser.
  • Blueprint AI. A four-stage AI extraction pipeline (SEE, then UNDERSTAND, then INTERPRET, then EXTRACT) that turns a 40-page architectural PDF into a fully line-itemized bid with confidence scores, page citations, and an annotated viewer for verification. Every dimension is pinned to an OCR-detected bounding box on the source page; nothing the model claims is ungrounded.
  • Lead Tracking & CRM. Full customer history with revenue rollup, follow-up scheduling with calendar view and automated SMS/email reminders, activity audit log on every quote and customer (actor plus timestamp plus diff), WebSocket real-time notifications, and multi-tenant role-based access.
  • Invoicing & Payments. One-click convert-quote-to-invoice with race-condition-safe select_for_update(), per-tenant Stripe with AES-256-encrypted keys so every contractor's revenue lands in their own bank, idempotency keys on every API call, DB-backed webhook idempotency, hosted payment links, QuickBooks two-way sync, and async dunning emails with retry-on-failure.
  • AI Chat Assistant. A natural-language command bar (Cmd+K plus persistent bottom-bar) with 39 function-calling tools, cosine-similarity intent routing that bypasses the LLM for high-confidence navigation, multi-turn tool chaining for composite actions, and pydantic argument validation on every handler.

Technology Stack

Frontend: React 18 + TypeScript, Vite 6, Tailwind v4 (token-only), shadcn/ui, vite-plugin-pwa, Capacitor 6 (iOS + Android), Vercel hosting.

Backend: Django 5.1 + DRF 3.15, PostgreSQL 16, Redis 7, Celery 5.4 + Celery Beat, Django Channels 4.2 (WebSockets), DigitalOcean Spaces (S3-compatible), Gunicorn + Nginx, Terraform + GitHub Actions.

AI & External Services: OpenAI GPT-5 (Blueprint AI vision), GPT-5-mini (text interpretation), Anthropic Claude (optional per-stage VLM override), PaddleOCR (bounding-box-anchored text extraction), GPT-4o (quote descriptions + chat tools), text-embedding-3-small (intent routing), Stripe (per-tenant payments), Twilio (A2P 10DLC-registered SMS + OTP), Mailjet (transactional email), QuickBooks Online (accounting sync), Statistics Canada (provincial labor index), Home Depot Canada (material price scraping), Sentry (error tracking with PII scrubbing).


4. Implementation

Backend & Infrastructure

A single Django + DRF API serves the web app, the iOS + Android Capacitor builds, the customer-facing quote and payment portals, the Stripe webhooks, the QuickBooks OAuth callbacks, and the WebSocket notification consumer, all from one codebase, one deploy, one source of truth. PostgreSQL holds the relational core (users, organizations, customers, quotes, invoices, blueprint projects, extractions, audit log). Redis backs both the Django cache and the Celery broker. Celery + Celery Beat run async work on dedicated queues: high_priority for blueprint extraction, default for emails, SMS, follow-up reminders, and QuickBooks sync.

Multi-tenancy is modeled at the organization level with role-based access (IsOrgOwner, IsOrgOwnerOrAdmin, IsOrgMember) wired into every viewset. Per-tenant Stripe keys are encrypted at rest via AES-256 (django-encrypted-model-fields), and every Stripe API call is wrapped with an idempotency_key so a network retry can never double-charge. Webhook idempotency is enforced by a ProcessedWebhookEvent model with a composite UniqueConstraint(event_id, source), meaning the billing webhook and the invoice webhook each maintain their own independent idempotency log, so a replayed billing event can't accidentally short-circuit an unrelated invoice event.

Web Frontend & Mobile

A React 18 + TypeScript + Vite 6 SPA with Tailwind v4 token-only styling, served from Vercel and installable as a PWA via vite-plugin-pwa. Every interactive surface has been audited against an 8-criterion design rubric (tokens-only, real empty/loading/error states, keyboard navigation, :focus-visible rings, skip-link + <main> landmark, both light and dark themes, mobile 390px, .lift micro-interaction on every clickable element) and 11 of 12 routes scored 16/16 in the most recent design pass.

The same React codebase ships to iOS and Android via Capacitor 6. The mobile builds include the full quote builder (with offline-first sync), customer CRM, follow-ups, invoice list, AI chat, and Blueprint AI viewer; every primary workflow runs on the phone. Native-bridged plumbing includes Capacitor Filesystem + Share for cross-platform PDF downloads (the browser <a download> pattern is broken in iOS WKWebView), a global Android hardware back-button stack that closes the topmost modal before falling through to history.back(), FCM push notifications via firebase-admin, and self-hosted @fontsource/* fonts to kill the 45-second offline-startup timeout the Capacitor WebView used to hit on the render-blocking Google Fonts CDN.


Feature 1: Quick Quote Builder

Quick Quote Builder

A five-step wizard that turns a customer conversation into a sendable quote in under five minutes.

The business case: The Quick Quote Builder is the highest-volume surface in the platform. Every quote that ships through this flow is a quote the contractor didn't spend 90 minutes building in Excel. At a fully-loaded cost of $50–$125 per manual quote and 8–10 quotes per won contract, every minute shaved off the quote-production cycle directly compounds into either lower customer-acquisition cost or higher pipeline throughput.

The flow:

  1. Customer step. Pick an existing customer from the CRM or create one inline. Phone numbers are normalized to E.164 at the serializer layer, with an explicit upper-bound enforced after a 2026 bug where the profile-update serializer was missing normalization.
  2. Template step. Start from one of the system templates (kitchen reno, bath reno, deck, basement, custom) or a user-saved template. Templates carry default line items, units, and labor estimates that are cloned into the new quote.
  3. Line-items step. Add, edit, reorder, and price every line. Each line auto-calculates (quantity × unit_price) − discount + tax. Tax rate is stored as Decimal (not float, fixed in v0.10.2 after a rounding regression), constrained to [0, 100], and applied at the quote level.
  4. AI description step. Optional GPT-4o pass that writes a customer-facing scope-of-work description from the line items. Toggleable per quote; regeneratable if the contractor doesn't like the first pass.
  5. Review & send. Preview the customer-facing PDF, send via email (Mailjet) or SMS (Twilio), or copy a public shareable link. A post_save signal fires the activity log; a post_delete signal on QuoteLine recalculates the parent quote totals so a deleted line never leaves stale numbers in the database.

Offline support is the difference between a tool that works in the office and a tool that works on the job. Quotes drafted on a phone with no signal are persisted to the device, then bulk-synced via POST /quotes/sync-offline/ the moment the device reconnects. An offline_id field with a unique DB constraint (migration 0010_unique_offline_id) guarantees that if the same offline_id arrives twice (network retry, app reopened, two tabs racing) only one quote lands.


Feature 2: Invoicing & Payments

Invoices

Quote-to-cash in one click. The convert-to-invoice flow is wrapped in @transaction.atomic with select_for_update() on the source quote, so two concurrent "Convert to Invoice" clicks (a real bug we hit early in production) can never produce two invoices from the same quote.

Per-tenant Stripe is the architectural choice that defines the platform's payments posture and is worth understanding in detail:

  • Each contractor's organization owns a StripeIntegration record (one-to-one) holding stripe_secret_key, stripe_publishable_key, and stripe_webhook_secret, all AES-256-encrypted at rest.
  • Keys are verified against the live Stripe API before is_verified is flipped, so a contractor cannot accidentally save invalid keys.
  • Every Stripe API call passes api_key=integration.stripe_secret_key explicitly. The naive Stripe SDK pattern is stripe.api_key = "sk_..." at module level: fine for a single-tenant app, catastrophic in a multi-tenant one where two concurrent webhook handlers can race against the same module global. We rewrote every Stripe call site to pass the key per-call, and added a regression test that runs two concurrent payment flows against two different orgs to pin the contract.
  • All Stripe API call sites include an idempotency_key so a network retry can never double-charge.
  • Receipt and dunning emails are dispatched to Celery (3× retry, 60s exponential backoff), so the webhook handler never blocks on outbound email.
  • A periodic Celery task (every 6 hours) reconciles each org's subscription status against Stripe, catching the rare case where the initial checkout webhook was dropped and re-linking the subscription back to the org.

The business consequence of this architecture: every contractor's payment revenue lands in their own Stripe account, in their own bank, on their own settlement schedule. Benmore never holds a balance, never becomes a chokepoint, never has to clear funds for the contractor. This is a deliberate trust posture: the contractor is the merchant of record on their own work, and the platform is plumbing.

The customer-facing payment portal is its own React route under /customer/pay/:token: branded, accessible (full skip-link + <main> landmark + Dialog-based reject modal with focus trap and Esc-to-dismiss), and re-themed to the platform's teal palette after a 2026 design audit caught off-brand green CTAs leaking through from an earlier scaffold.


Feature 3: Blueprint AI

Blueprint AI

Blueprint AI is the platform's flagship technical differentiator and the feature most directly responsible for collapsing the $500–$1,000 cost-per-won-contract problem.

The business case in one paragraph

A manual blueprint takeoff for a typical 30–40 page residential set takes a competent estimator 4–8 hours. At a fully-loaded estimator cost of ~$60/hour, that's $240–$480 per blueprint, before any pricing work, customer conversation, or revision. Multiply across 10 quotes a month, and the takeoff line item alone is $2,400–$4,800/month for a single-person shop. Blueprint AI compresses this from hours to minutes: the contractor uploads the PDF, walks away, and comes back to a fully line-itemized bid with confidence scores, page citations, and an annotated viewer. Total cost per blueprint: a few cents of OpenAI tokens and a few seconds of compute. The math is unambiguous, and it is the single largest unit-economics shift the platform delivers.

The four-stage pipeline (v3)

Early versions of the platform did this the obvious way: render each page, ship the image to GPT-4o with a "list everything you see" prompt, parse the JSON. It worked, but it produced two systematic failures that disqualified it from being something a contractor would sign their name to:

  1. Hallucinated dimensions. A vision-language model asked to "read the dimensions on this floor plan" will confidently invent numbers when the text is small, rotated, or low-contrast.
  2. Arithmetic errors. Even when the model read dimensions correctly, it would multiply 12'-6" × 14'-3" and return something within 10% of the right answer, but not the right answer. LLMs are not calculators.

The v3 pipeline (current production) separates perception from interpretation from calculation, and pins every dimension claim to an OCR-detected bounding box on the source page. Nothing the model claims is ungrounded:

PDF Page → Image Render (300 DPI, contrast + sharpen, auto-tile if >4000px)
    → PaddleOCR pre-pass (every text token gets a bounding box and a stable ID)
    → Stage 1 SEE       (GPT-5, high-detail vision)
                         "Describe every visible element with approximate bounds.
                          No calculations, no inferences."
                         → ObservedElements + approximate_bounds
    → Stage 2 UNDERSTAND (GPT-5, high-detail vision + OCR data)
                         "Build a spatial model. Every dimension you read
                          MUST cite an OCR entry ID."
                         → SpatialModel (spaces + OCR-anchored dimensions)
    → Stage 3 INTERPRET  (GPT-5-mini, text-only)
                         "Apply trade domain knowledge. Show full calculation
                          math. Use the calculator tool for area/perimeter."
                         → InterpretedItems (quantities + calc steps)
    → Stage 4 EXTRACT    (Pure Python)
                         Map to ExtractionItem records, compute grid regions
                         from Stage 1 bounds, build reasoning chains.
    → Programmatic Verifier (dim match + spatial proximity + arithmetic)
    → Dimension Re-Reader (crop + zoom on flagged regions, re-read at higher res)
    → MathValidator + CrossPageAggregator (schedule quantities override plan counts)

Why this architecture matters, in plain language:

  • Stage 1 doesn't calculate. This sounds trivial but is the single biggest lift in extraction quality: the model is no longer being asked to reason and see at the same time. It just sees.
  • Stage 2 must cite OCR IDs. A dimension the model "saw" but cannot pin to an OCR bounding box is treated as unverifiable and routed to the dimension re-reader for a second pass at higher zoom. The model cannot make up a number; if it does, the verifier catches it.
  • Stage 3 is text-only. Once the spatial model is built, the vision model is no longer in the loop: a cheaper, faster, text-only model applies the construction domain knowledge (e.g., "every kitchen needs at least four outlets, every bedroom needs at least one window").
  • Stage 4 is pure Python. The final ExtractionItem records, grid regions, and reasoning chains are deterministic: no LLM in the path, no temperature, no surprise.
  • A deterministic Python calculator is exposed as a tool to Stage 3 for area and perimeter math, so even when the LLM is in the loop for interpretation, the arithmetic is always done by Python.
  • N-of-M self-consistency retries re-run extractions whose count or dimensions disagree across runs, surfacing the disagreement as a confidence flag the contractor can review.

Transparency, verification, and trust

A contractor signing their name to a bid will not trust a black box, and they shouldn't have to. Every Blueprint AI output ships with the verification surface a senior estimator would build by hand:

  • Per-page stage artifacts persisted to DigitalOcean Spaces. The contractor can drill into exactly what Stage 1 saw, what Stage 2 understood, what Stage 3 interpreted, and what Stage 4 extracted.
  • An annotated blueprint viewer with per-item pins on the source page, a 10-color palette, and bidirectional hover/click sync with the extraction cards: click a pin, see the line item; click a line item, see the pin.
  • Confidence scores on every extraction (pending, verified, dimension_mismatch, math_error, verified_after_reread, reread_conflict, unverifiable).
  • OCR source citations with pixel coordinates for every dimension read: every number on the bid traces back to a specific bounding box on a specific page of the source PDF. Auditable in the literal sense.
  • An audit checklist system. The contractor (or a senior estimator) defines a set of requirements ("every bedroom has at least one window," "outlet count matches schedule"), and the AI evaluates the extraction evidence against them, flagging gaps before the bid goes out.
  • A user feedback loop. Every extraction has a rate-limited "this is wrong, here's why" pre-filled correction prompt. Feedback is tracked per project (review_coverage_rate + accuracy_rate) and feeds the model improvement pipeline.

Operational guardrails

  • 50-page PDF limit at upload, enforced server-side after a 2026 incident where a contractor uploaded a 200-page commercial set and saturated the worker pool. 100MB file size limit enforced both client-side (UI copy: "Max 100MB") and server-side.
  • Per-page timeout configurable by the contractor (5–60 minutes total processing budget).
  • Cancel-in-progress revokes the Celery task, marks the project "cancelled", and returns the partial extractions already produced.
  • Cost tracking per Bid record. The contractor sees exactly how many cents of OpenAI tokens this run consumed.
  • Per-file failure isolation. If 2 of 5 PDFs in a batch fail, the other 3 still produce bids; the project is only marked "failed" if all files fail.

Bid → Quote conversion

Once a contractor has a Blueprint AI bid they trust, one click converts it into a Phase 1 quote. The BidConversionService is wrapped in @transaction.atomic, marks the resulting quote with source="blueprint" and source_bid=<bid_id> (both read_only on the serializer, so a malicious client can't fake the provenance), and prefetches lines with prefetch_related("lines") to kill the N+1 query that an earlier version had.


Feature 4: Lead Tracking & CRM

Customers

The CRM is built to match the way contractors actually keep track of people: phone-first, search-driven, with revenue rollup so the contractor can see at a glance who their best customers are and where the next dollar is coming from.

  • Customer detail page with full quote rollup, invoice rollup, total revenue, total outstanding, last activity, and a complete activity log
  • Inline customer creation from any quote, invoice, or follow-up, so a contractor never has to context-switch to add a contact
  • CSV export for accountant handoff or migration into other systems
  • Search + filter on every list (name, email, phone, status, date range)
  • Multi-tenant by default: every customer is scoped to an organization; cross-org leaks are impossible by middleware-enforced queryset filtering on every viewset

Follow-ups are the lifeblood of contractor sales. Most won contracts are won on the second or third touch, not the first, and the contractor who systematically follows up is the one who closes the pipeline.

Follow-ups

The Follow-Up tab is a full calendar plus a list with:

  • Scheduled date, customer, related quote, notes, and status (pending / completed / cancelled)
  • Celery Beat task every 5 minutes that scans for due reminders and dispatches SMS + email via Twilio + Mailjet
  • One-click complete from the calendar, the list, the AI chat, or the customer detail page
  • Retry-on-failure (2× retry, 60s backoff) so a transient SMS delivery error doesn't lose the reminder

Activity log is the audit trail every contractor wishes they had: every quote status change, every send, every payment, every edit, every customer touch, with actor, timestamp, and diff. Powers the homepage notification feed, the customer detail timeline, and the org-level audit export.

Real-time notifications ride a Django Channels WebSocket consumer. Quote accepted by customer? Toast on the contractor's screen within 200ms. Invoice paid? Same. Blueprint AI run finished after the contractor closed the tab? Push notification via FCM to the iOS/Android app. WebSocket connections are rate-limited (max 5 concurrent per user) to prevent runaway browser tabs from exhausting Channels capacity.


Feature 5: AI Chat Assistant

The AI chat is built to be the fastest way to do anything in the platform: faster than clicking, faster than the keyboard shortcut, often faster than knowing where the feature lives.

A unified SmartCommandPalette renders both a Cmd+K palette and a persistent bottom-bar on every authenticated page. An IntentMatcher pre-computes 10 intent centroids from short example phrases using text-embedding-3-small, then routes every incoming query by cosine similarity:

  • HIGH confidence (≥0.85): fire deterministically. "go to dashboard" navigates without an LLM call. Zero token cost, zero latency beyond the embedding.
  • MEDIUM confidence (0.6–0.85): route through GPT-4o with only the relevant tools available.
  • AMBIGUOUS (0.4–0.6): ask a clarifying question with suggestion chips.
  • LOW / NO_MATCH (<0.4): fall through to general chat with the full toolset.

The architecture buys two things the naive "every query goes to an LLM" pattern doesn't: predictable cost (most navigation queries never touch a paid model) and predictable latency (a Cmd+K then navigation should feel instant, not feel like waiting on a chat completion).

39 function-calling tools span customers (5), quotes (10), invoices (7), follow-ups (4), blueprints (7), and analytics (6). Multi-turn tool chaining (up to 5 turns) lets the assistant compose actions. "create a quote for Sarah Chen for a kitchen reno at $24,500" runs search_customers → create_quick_quote in a single conversational turn, instead of falling into the announce-and-stop trap where the model says "I'll create that for you" and then doesn't.

Pro-gating is enforced at dispatch: PRO_ONLY_TOOLS checks the user's role before invoking the handler. Free users get a structured {"error": "requires_pro", "feature": ..., "message": ...} envelope, which the chat surface translates into a contextual upsell instead of a hard wall.

Every handler validates args through a pydantic model before dispatch: invalid args return {"error": "invalid_args", ...} without ever invoking the underlying API. A malicious or buggy LLM cannot reach an unvalidated endpoint through the chat surface.


The Pan-Canada Pricing Library

A central pricing library scoped by trade, material, and province. Two automated data feeds keep it fresh without the contractor lifting a finger:

  • Home Depot Canada scraper. HomeDepotScraperService runs every Sunday at 3 AM ET via Celery Beat, scraping a curated set of 30 top construction materials with a four-strategy price parser (data-attribute, then CSS selector, then div scan, then regex fallback). Rate-limited to 1 request per 2 seconds with 3× retry and exponential backoff. Upserts PricingLibraryItem with is_system=True, source='homedepot'.
  • Statistics Canada provincial labor index (Table 18-10-0005-01). Monthly Celery Beat job pulls building-construction price index data by province so labor rates auto-adjust to the contractor's region.

A contractor can override any system price with a custom price scoped to their organization. When a Blueprint AI extraction flows through the Price Lookup Wizard, a three-tier gap detection (exact, then substring, then SequenceMatcher ≥ 0.6) classifies every item as matched, suggested, or missing, and the contractor can save-to-library from the extraction card with one click.

The business consequence: the contractor never quotes off a stale price again. Lumber moves 8% in a month? Every quote written this week reflects it.


Settings, Admin, and Organization Management

Settings

The Settings surface is where multi-tenancy stops being an architectural choice and starts being a product. A contractor's organization owner can:

  • Invite team members with role-based access (owner / admin / member / auditor), with token-based invite links, expiry, resend, and revoke
  • Configure the org's Stripe integration: paste in their own Stripe keys, verify against the live API, see connection status, and disconnect at any time
  • Manage their subscription plan (Free / Pro / Admin) via embedded Stripe Checkout, with the customer portal one click away for invoice history, card update, and cancellation
  • Connect QuickBooks Online via OAuth 2.0 for two-way invoice and customer sync
  • Configure optional two-factor authentication (TOTP) with a 30-day grace period: flip 2FA on, the platform doesn't lock you out tomorrow if your authenticator app is on the lost phone you haven't replaced yet
  • Manage notification preferences across email, SMS, and FCM push channels
  • Review the integrations changelog so the contractor knows what shipped this week without leaving the app

Technical Challenges Addressed

  1. Per-tenant Stripe without thread-unsafety. Rewrote every Stripe call site to pass api_key=integration.stripe_secret_key explicitly instead of mutating the module global. Added a regression test that runs two concurrent payment flows against two different orgs to pin the contract.
  2. Blueprint AI hallucination. The v3 four-stage pipeline (SEE, then UNDERSTAND, then INTERPRET, then EXTRACT) with OCR-anchored dimension citations, a deterministic Python calculator, and N-of-M self-consistency retries pushed accuracy past 95% on residential validation sets, with the remaining gap surfaced as verification_status flags the contractor can review before sending.
  3. Convert-quote-to-invoice race condition. Two clicks in 200ms produced two invoices from the same quote. Fixed with select_for_update() inside @transaction.atomic, plus a offline_id unique DB constraint for the offline-sync case where the same logical quote arrives twice from a phone retrying the network request.
  4. Webhook replay double-billing. Stripe will replay webhooks on its own retry schedule. We hit a case where a billing event replayed into the invoice endpoint and short-circuited an unrelated flow. The fix: a ProcessedWebhookEvent model with a composite UniqueConstraint(event_id, source) so the billing and invoice endpoints maintain independent idempotency logs. Both survive a Redis flush because the record is in Postgres.
  5. iOS WKWebView <a download> is broken. A Capacitor-shipped React app on iOS cannot download files via the browser pattern. We built a downloadBlob() helper that uses Capacitor Filesystem + Share on native (with FileProvider/UIActivity bridging), falls back to <a download> on web, sanitizes filenames, detects Share-cancel, and returns a clear boolean on success. 18 download call sites migrated to it.
  6. The 45-second Capacitor cold-start. Render-blocking Google Fonts CDN was timing out under Capacitor's offline-startup model, producing a 45-second white screen on first launch in airplane mode. Fixed by self-hosting fonts via @fontsource/* and deferring Sentry init off the critical path. Cold-start is now sub-2-seconds on a mid-tier Android device.
  7. AI chat announce-and-stop. GPT-4o would say "I'll create that for you" and stop without invoking the next tool. The fix: a multi-turn loop with MAX_TURNS=5 so the model can chain search_customers → create_quick_quote instead of falling into the hedge trap. An anti-hedge system prompt closes the remaining gap.
  8. Round-trip-safe API contracts. A mobile binary cannot be atomically upgraded with the backend, so any field that is pre-filled into a form and POSTed back on save must never be masked in-place. The customer phone normalization, Stripe-key fields, and pricing library fields all follow the "new-field-name + skip-on-write-if-empty" pattern.

5. Results

What Contractors Are Saying

Customer review of Metior Pro

Product Outcomes

  • Live in production on web (Vercel), iOS (App Store), and Android (Google Play), with a single React codebase serving all three surfaces.
  • Five core workflows shipping daily: quick quote builder, Blueprint AI, customer CRM + follow-ups, Stripe-backed invoicing, and the AI chat assistant.
  • Multi-tenant from day one: every contractor's data, Stripe keys, customers, quotes, and invoices are isolated by organization with middleware-enforced queryset scoping.
  • Pan-Canada pricing automation: Home Depot Canada weekly scrape plus monthly Statistics Canada provincial labor index, fully automated via Celery Beat.

Technical Achievements

  • Four-stage Blueprint AI pipeline with OCR-anchored dimension citations, a deterministic Python calculator, and N-of-M self-consistency retries, pushing accuracy past 95% on residential validation sets while staying inside a few-cents-per-page token budget.
  • 39 function-calling AI chat tools with cosine-similarity intent routing, multi-turn tool chaining, and pydantic argument validation on every handler: no tool can be invoked without its args first passing a structured schema check.
  • Per-tenant Stripe with field-level AES-256 encryption: every contractor's payment revenue lands in their own bank, idempotency keys on every API call, DB-backed webhook idempotency with per-source scoping, and a periodic subscription-sync task catching dropped webhooks.
  • Cross-platform file delivery: 18 download sites migrated to a unified downloadBlob() helper using Capacitor Filesystem + Share on native and <a download> on web, with sanitized filenames, share-cancel detection, and toast-surfaced errors.
  • A 16/16 design rubric across 11 of 12 authenticated routes: tokens-only styling, real empty/loading/error states, keyboard navigation with :focus-visible rings, skip-link + <main> landmarks, both light and dark themes, mobile 390px, and .lift micro-interactions on every clickable element.
  • Sub-2-second cold-start on mid-tier Android: self-hosted fonts, deferred Sentry init, and a Capacitor tar patch killed the 45-second offline-startup timeout we used to hit.
  • End-to-end audit trail: every quote status change, invoice send, payment, follow-up, and Blueprint AI run leaves a timestamped record with actor and diff. Reconciliation is queryable, not reconstructable from logs.

Business Impact

  • Time-to-quote compressed from days to minutes. The five-step quote builder ships a sendable quote in under five minutes; Blueprint AI compresses a 4–8 hour takeoff into a few minutes of compute and a few cents of tokens.
  • Estimating overhead per won contract, the silent budget killer, collapses by an order of magnitude. Where a small builder was spending $500–$1,000 in estimator time to win one contract, the AI pipeline drops that to a fraction of the cost, freeing the contractor to either bid more jobs, win at a lower margin, or finally take a Saturday off.
  • One product replaces five. Quote tool, CRM, invoicing, blueprint takeoff, pricing reference, all in one app, on one login, on one bill, with one source of truth for every customer and every job.
  • Mobile-first means job-site-first. A contractor standing in a basement with a homeowner can produce a credible, line-itemized estimate before they leave the property. Speed is the win-rate multiplier.
  • Zero payment-platform lock-in for the contractor. Per-tenant Stripe means every contractor's revenue is theirs from the moment it lands. Benmore never holds a balance, never becomes a chokepoint.

6. Lessons Learned

Key Takeaways

  • AI is a force multiplier on a structured pipeline, not a substitute for one. The single biggest accuracy gain in Blueprint AI came not from a bigger model, but from forcing the model to not do calculations and to cite every dimension to an OCR bounding box. Constraining the LLM is what made it trustworthy.
  • Per-tenant from day one is cheaper than per-tenant from day fifty. Multi-tenancy retrofits are the most expensive refactor a SaaS does. We modeled organizations, role-based access, and per-tenant Stripe before the first paying customer, and have never had to unwind it.
  • Race conditions in payment flows are not theoretical. Two clicks in 200ms produced two invoices from the same quote in the wild. select_for_update() inside @transaction.atomic is the price of admission for any quote-to-cash flow; the idempotency_key on every Stripe call is the second.
  • A 45-second cold start is a 100% churn event. Capacitor's offline-startup model is a real constraint and the render-blocking Google Fonts CDN is real production debt. Self-host fonts, defer telemetry, and measure cold-start on a mid-tier device, not on the M-series MacBook you develop on.
  • Audit trails are a customer-facing feature, not a back-office concern. Contractors are dealing with homeowners, accountants, and sometimes lawyers. Every status change, send, and payment having a timestamped actor record is the difference between "trust me" and "here's exactly what happened, when, and by whom."
  • Token-only styling pays for itself the first time you ship a dark mode. The 8-criterion design rubric is enforced at the component level, not the page level, which means a new feature inherits theming, accessibility, and mobile behavior for free instead of relitigating them every time.
  • Predictable cost beats theoretical capability. Routing high-confidence chat queries deterministically (no LLM in the loop) costs the platform nothing and feels instant to the user. The naive "send every query to a chat completion" pattern is both slower and more expensive without being more capable for 80% of the traffic.

7. Conclusion

Summary

Metior Pro collapses the five-app stack a small construction contractor used to live in (Excel for quotes, QuickBooks for invoices, paper for follow-ups, spreadsheet for pricing, manual takeoff for blueprints) into one phone-first product built around the single most expensive thing a contractor does: producing the quote that wins the next contract.

The technical center of gravity is the four-stage Blueprint AI pipeline, which separates perception from interpretation from calculation, pins every claim to an OCR-anchored bounding box on the source page, and ships a verified, line-itemized bid in minutes instead of hours. Around it sit a quote builder, a CRM, an invoicing engine, and an AI chat assistant, each one designed to either reduce the time-to-quote, increase the win rate per quote, or compress the time-to-cash on a won contract.

The project demonstrates how disciplined AI-pipeline engineering, per-tenant payment architecture, and a single React codebase shipping to web, iOS, and Android can deliver a contractor-grade platform that respects the one constraint every small-business SaaS eventually hits: the user's most valuable asset is their time, and the architecture has to respect that fact on every screen, every workflow, every release.


A Note on the Mission

Metior Pro is, on its surface, a quoting tool for contractors. Underneath, it's a thesis: that the small builder who can produce a credible quote in five minutes deserves to win the contract over the corporate competitor who takes a week, that the $500–$1,000 a small shop burns on estimating overhead to land a single job is a tax on the wrong people, and that AI applied to a structured, verified, OCR-anchored pipeline can do for the construction industry what spreadsheets did for accounting: change the unit economics of the work forever.

Every architectural choice in this codebase (per-tenant Stripe, OCR-anchored dimension citations, offline-first quote sync, token-only design tokens, idempotent webhook handlers, audit-trailed activity log, multi-turn tool-chaining chat) exists because the contractor in the truck deserves software that holds up under the conditions they actually work in.

We are excited to keep building infrastructure for the trades that build the world.