← All work
Aviation trade · B2B SaaSSole product engineerIn production · multi-tenant

Fenavia

AI-assisted trading platform for aviation parts

Next.js
TypeScript
PostgreSQL / Prisma
Microsoft Graph
LLM extraction
Multi-tenant SaaS
Chrome extension
A sourcing campaign with supplier replies matched back to the request that triggered them, and the best offer surfaced per condition.
A sourcing campaign with supplier replies matched back to the request that triggered them, and the best offer surfaced per condition.

Fenavia is a multi-tenant B2B platform that runs an aviation parts trader's entire commercial cycle — inbound RFQ, supplier sourcing, quote, sales order, invoice and airworthiness paperwork — with an AI email pipeline doing the reading and a confidence gate keeping a human on every decision that reaches a customer.

Client context

The first tenant is an aviation parts trading company that buys, repairs and resells rotable and consumable aircraft components. Its customers are airlines, MRO shops and other brokers; its suppliers are the same population, reached mostly through the ILS and PartsBase marketplaces. The company name is withheld here; the platform is described by its product name.

The platform began as a single-tenant internal system for that company and was re-architected into a multi-tenant SaaS product, with the original operator becoming tenant number one. That migration is the spine of the whole engagement: every module had to keep working for a live business while the data model grew a tenant boundary underneath it.

The operational challenge

Parts trading is a speed business with a compliance tail. Four things made it hard to run on spreadsheets and a shared mailbox:

  1. Demand arrives as unstructured text. A request for quote is an email — sometimes a marketplace notification, sometimes three lines of free text, sometimes a scanned PDF. Whoever answers first with a credible price usually wins the order. Reading and re-typing that traffic by hand is both the bottleneck and the error source.
  2. Sourcing is a many-to-many broadcast. Answering one customer request means asking twenty suppliers, then reconciling replies that arrive in every conceivable format, days apart, against the request that triggered them.
  3. Supplier identity is disposable. Every marketplace search surfaces suppliers who are then forgotten, so the same discovery work is repeated forever instead of compounding into an asset.
  4. The paperwork is regulated. A shipped part needs traceability and an ATA 106 form. Documents assembled by hand at the end of the process are where deals stall.

Product strategy and approach

Three decisions shaped the product more than any framework choice.

Email is the integration surface, not a feature. Counterparties will not adopt a portal. So the platform subscribes to the tenant's own Microsoft 365 mailbox and treats every message as a candidate business event — an inbound RFQ, a supplier quote, or noise — rather than expecting anyone to change how they work.

Automate the reading, not the deciding. The language model classifies and extracts; it never independently commits to a customer. A per-tenant confidence gate decides whether an RFQ earns an automatic draft quote or an acknowledgement plus a human queue entry. Every automated reply is bounded by rules the tenant sets: minimum confidence, inventory-match and price-source requirements, quiet hours, an hourly reply ceiling and a kill switch.

Assume the model will be wrong and build for that. Every parse is logged with its model, prompt version, latency and verbatim output, and is reviewable and correctable in the UI. That is what makes the AI part of the product operable rather than a demo.

The core solution

The platform ships as one application with distinct operating surfaces:

Surface What it covers
Commercial pipeline RFQs, quotes, sales orders, invoices, credit memos
Supply side Purchase orders, repair orders, receiving, supplier sourcing campaigns
Inventory Part-number master, stock with a dual physical/business status model, traceability history
Finance Receivables, payables, payments, multi-currency with FX caching, profitability reporting
Compliance ATA 106 generation with signature capture, full-trace paperwork, complete document packages
Administration 47 granular permissions across 6 role templates, per-tenant branding, mailbox onboarding, AI behaviour settings, email templates
Platform operations Tenant management, parse-log review, service health, cross-tenant AI defaults

A companion Chrome extension bridges the two marketplaces that have no usable public API for this workflow, syncing inventory and capturing supplier data from search results under the operator's own logged-in session.

Primary workflows

Inbound RFQ → quote. A message arrives in the tenant mailbox. A Microsoft Graph webhook acknowledges it in milliseconds and enqueues a durable parse job — a delta-query poller backstops any notification the webhook misses. A worker classifies the message, extracts part number, condition, quantity and delivery terms, and escalates image-only or scanned attachments to a vision model. Sender rules suppress known non-business traffic before any model call. The RFQ is matched against inventory and part history; if it clears the tenant's gate it becomes a draft quote and a threaded reply with a branded PDF, otherwise the customer gets an acknowledgement and the request lands in a human queue with the reason it was held.

Sourcing campaign → best offer. When nothing is in stock, the RFQ is raised into a market-search campaign. The platform ranks candidate suppliers from its own accumulated graph — response rate, recency and win rate — and dispatches by email where a verified address is known, falling back to the marketplace lane for net-new discovery. Supplier replies come back into the same mailbox and are matched to the originating request through a layered matcher: marketplace reference, then sender address, then part-number overlap, then fuzzy name. Every response records its match confidence, the reason it matched and a link to the source email, and a misfiled reply can be reassigned. The result is a best offer per condition code, convertible to a customer quote in one action.

My role

Sole product engineer: discovery with the operator, product and data-model design, all implementation, test strategy, deployment and production operations. The work included the multi-tenant migration of a live system, the AI ingestion pipeline end to end, the browser extension, and the observability needed to run all of it.

Technical approach

Next.js App Router with a strict server/client split, PostgreSQL and Prisma. Tenant isolation is enforced in the data layer: a scoped Prisma client injects tenantId on every query, with a second owner-scope layer for per-user record visibility, so isolation cannot be forgotten at the route level. Email ingestion is queue-backed so a slow model call can never stall a webhook acknowledgement. Documents are generated server-side and stored behind an abstracted storage layer. Deployment is Docker on Coolify with wildcard subdomain routing per tenant. Scale of the codebase: 86 Prisma models, 97 migrations, 215 API routes, 92 portal screens, 165 test files.

Outcomes and evidence

Claims here are limited to what the codebase supports:

Model accuracy is deliberately not claimed as a percentage: the human-labelled ground truth is too sparse to support one, and building that evaluation set is the current priority — which is itself the honest state of most production LLM systems.

Confidentiality

The operator is anonymized. Screenshots are taken against a development environment with company names replaced; no customer, supplier or pricing data is published.


Need a similar product or workflow? If you run a business where the real interface is a mailbox — quotes, RFQs, orders, claims — the same pattern applies: durable ingestion, extraction with a confidence gate, an audit trail, and a human on the decisions that matter.

Screens

A parsed RFQ beside the source email that produced it.
A parsed RFQ beside the source email that produced it.
Every LLM parse is logged with model, verdict, self-reported confidence and latency — and can be corrected by a human to build ground truth.
Every LLM parse is logged with model, verdict, self-reported confidence and latency — and can be corrected by a human to build ground truth.
The per-tenant automation gate — confidence floor, inventory and price requirements, quiet hours, hourly reply ceiling, kill switch.
The per-tenant automation gate — confidence floor, inventory and price requirements, quiet hours, hourly reply ceiling, kill switch.
The tenant portal — one pipeline from demand to paperwork.
The tenant portal — one pipeline from demand to paperwork.
Sourcing campaigns raised from real customer demand rather than a static supplier list.
Sourcing campaigns raised from real customer demand rather than a static supplier list.
Offer comparison across suppliers before a quote goes back to the customer.
Offer comparison across suppliers before a quote goes back to the customer.
47 granular permissions across six role templates.
47 granular permissions across six role templates.

Captured against a development environment. Company names, contacts and identifiers are replaced and monetary values are scaled — the frames are evidence of the interface, not of anyone's financial figures.

Architecture

System contextscroll to explore
Email → quote, including the sourcing loopscroll to explore
← Back to all work