Nxord

Helping a food distributor get orders into their ERP without typing them out again.

In productionnxord.com ↗  ·  demo video ↗

Overview

I co-founded Nxord after seeing how a food distributor receives orders: PDFs, WhatsApp photos, emails, and voice messages, often with shorthand product names. We built a tool that reads them, matches the items to the catalogue, and lets an operator check the order before sending it to the ERP.

It is now used every day by a wholesale distributor and still fits around the ERP they already depend on.

What the distributor was dealing with

Orders arrived in different inboxes and chats. Staff had to read each one, work out the product nicknames, look up the catalogue codes, and type everything into AS400. There was also no single order history they could use to see what each customer was buying over time.

What we changed

The operator now has one place to drop an order and one place to correct it:

  1. Read the source. PDFs, photos, audio, email, and plain text go through the same intake screen.
  2. Match the catalogue. Exact codes come first, then fuzzy text, then pgvector similarity for the awkward cases.
  3. Keep the history. The same structured records feed customer history, year-over-year trends, basket analysis, and short sales briefs.
  4. Return to the old system. Checked orders leave in the fixed-width AS400 format over SFTP, with an audit trail.

Reading and matching orders

Reading the files

Finding the products

Implementation notes

Offline vs online computation. The analytics pipeline is split: a batch job processes the sales history CSV in pure Python and stores pre-computed JSON reports in PostgreSQL. At request time, the API does a single SELECT — no computation, no ORM joins.

pgvector for semantic matching. Every product name is embedded at upload time. Order items are matched by cosine similarity. The 3-stage pipeline (exact → fuzzy → semantic) keeps precision high.

Authentication. I used Django session cookies with credentials: include, so sessions can be revoked on the server.

Streaming briefs. Gemini takes 5–15 seconds to produce a brief. Server-Sent Events let the operator start reading while the rest is being generated.

Tech Stack

Layer Technology
Frontend React 19, TypeScript, Vite 7, Bootstrap 5.3
Backend Python 3.12, Django 5.2, Django REST Framework
Database PostgreSQL 16 + pgvector
Task Queue Celery 5 + Redis 7
AI Google Gemini (multimodal extraction + streaming brief)
Infrastructure Docker Compose, Cloudflare Tunnel, Hetzner VPS
Quality pre-commit (black, mypy, ESLint, tsc, bandit), GitHub Actions CI

What I worked on

I built the Django API, Celery jobs, pgvector matching, SFTP export, React interface, Docker setup, and CI. I worked on the analytics with my colleagues, handling the data format and its integration with the API.

In production. First client: active daily use since early 2026. Pre-revenue — expanding to additional distributors in Q2/Q3 2026.

nxord.com ↗