Short answer
RaftLabs built a gas station platform for a US operator with 40+ stations on Gilbarco Passport POS. A Tauri app exchanges POS XML files with the cloud about every 10 minutes, so sales update inventory and price changes reach it. Gemini 2.5 Flash reads invoices, UPC data checks lines, and staff confirm. The V1 build took 12 months, including discovery.
Engagement
The engagement
- Client
- A US gas station operator (name under NDA)
- Sector
- Fuel retail and convenience stores in the United States
- Network
- 40+ stations, all on Gilbarco Passport POS
- Timeline
- About 12 months including discovery; the platform build ran from January to September 2025
- Team
- Two backend engineers, two frontend engineers, a desktop app engineer, QA, and a project manager, with AI engineering support on invoice scanning
- Scope
- Owner, station admin, and super admin web apps; a desktop POS sync app; invoice AI; inventory, pricing, and reporting
- Status
- V1 built and tested; production results not yet published
The situation
The operator ran more than 40 gas stations in the US, every one on a Gilbarco Passport POS. Each POS knew its own sales. None of them shared stock, invoice, or price data with the others. Inventory lived in spreadsheets, staff typed vendor invoices in line by line, and a price change meant updating every station.
Replacing 40+ POS terminals wasn't on the table. So the job was to work with the Passport systems already in place: read their sales, write price and product changes back, and turn vendor paperwork into stock records without retyping.
RaftLabs built three connected pieces: web apps for owners and station admins, a desktop app that syncs each POS with the cloud, and an AI pipeline that reads vendor invoices. Reading the characters is only the first half of that job. The platform also has to answer which number is the total, which line is which product, and how certain the read was, because a wrong total that reaches the stock record unreviewed costs far more than one that gets flagged. The AI turned out to be the easier part. The POS integration carried most of the risk: Gilbarco's integration documentation was thin, and vendor support was slow.

Before and after
From 40 separate POS terminals to one inventory record
- Stock counts lived in spreadsheets and paper slips at each station
- Staff keyed vendor invoices in line by line
- Sales data stayed inside each Passport POS, with no cross-station report
- A price change meant updating every station by hand
- No system tracked stock movement against purchases, which led to stockouts and overstock
- Each station's sales files reach the platform about every 10 minutes and update stock line by line
- Staff scan an invoice, check the AI's suggested lines, and confirm
- Price and product changes become Passport files that the sync app drops into the POS inbox
- Low stock, out of stock, expiring soon, and expired statuses update on their own
- Owners get period-over-period trends and a printable store close report
The decisions that shaped V1
The Gilbarco integration had to be worked out by trial and error
Passport's integration documentation was thin. Gilbarco support answered slowly, and the time-zone gap made each question cost days. Waiting on the vendor would have stalled the whole project.
So we worked with the files the POS already exchanges with its back office. Passport writes sales to a
BOOutBoxfolder and imports changes from aBOInBoxfolder. A desktop app on the station's back-office PC reads the outbox and writes to the inbox over the local network share. Getting the formats and timing right took many rounds of trial and error.The result: no new hardware at the station, and no change to how cashiers use the POS.
Real-time sync gave way to a 10-minute batch
The original plan said
real-time sync. Discovery showed a problem with that. The sync compares files before upload so the same sales file never gets counted twice, and that check made short intervals unsafe.V1 syncs in batches about every 10 minutes. Real-time sync moved to a later version.
The trade is a few minutes of freshness for stock counts owners can trust. A duplicate upload would double-count sales and corrupt every figure downstream of it.
Stock math had to follow the coffee cup, not just the barcode
A POS line item isn't the same as a stock movement. Selling a coffee also uses a cup and a lid. A refill uses supplies but isn't a sale. The same product can sit on the shelf as single units and in unopened boxes.
The platform converts each XML sales file to JSON and turns every line into stock movements. Supply items link to the products that consume them. Refill items deduct supplies without counting a sale. When single units run out, the system draws from box stock and tracks how many boxes remain across boxed and loose items. A database trigger flags low stock, and a scheduled job keeps statuses current.
Mixed unit and boxed stock was the case that caught us off guard. It now sits inside the deduction logic instead of in a manual count. For comparable inventory management software, put unit-and-box rules in the first data model.
Invoice scanning went through three model plans
Discovery tested six OCR tools and three LLMs, then recommended AWS Textract with Claude 3.5 Sonnet through Zerox. The first build, in April 2025, sent each PDF page to Claude 3.7 Sonnet and parsed the text it returned. In May 2025 the pipeline moved to Gemini 2.5 Flash through the Vercel AI SDK, which returns the whole invoice as one schema-checked object. It started on a preview build and moved to the general release in June.
Fuel invoices and product invoices follow separate extraction paths. Each product line is checked against UPC barcode data from UPCitemdb, one of five UPC APIs compared in discovery. The AI then maps each line to the store's categories. Staff see the result as suggestions and confirm before anything touches stock. Most invoice processing automation works best in this order: the model extracts, a reference database checks, and a person confirms.
The UPC lookup and AI categorization slowed every scan. The fix was a Redis cache in front of both: products the lookup finds stay cached, and empty results expire after five minutes so a newly listed UPC gets another try. It's the technical decision we're proudest of on this project. No extraction accuracy or speed benchmark has been verified for publication.
Refills were quietly inflating the sales numbers
The raw transaction data couldn't be taken at face value. Coffee and fountain drink refills showed up as units sold, which inflated quantity and revenue on every report. The numbers looked fine until someone reconciled them.
Sales counts now exclude refills. Refunds, discounts, and net sales sit apart from gross. Every card, chart, and table reads from one set of numbers, so an owner never sees one total on the dashboard and another in the table. Charts show the top sellers plus a grouped "Others" total, so a category with dozens of products stays readable and the total stays correct.
System flow
How data moves between the POS and the platform
A simplified view based on the project repositories. It shows the operating path an owner depends on, not every service.
Station
Gilbarco Passport back office
The POS writes sales files to its outbox folder and reads price and product changes from its inbox. The cashier's workflow doesn't change.
Sync
Tauri desktop app
Signs in through AWS Cognito, moves new outbox files to S3, and writes pending change files into the inbox. It retries every five minutes when a connection fails.
Processing
AWS Lambda
Parses XML into JSON, applies stock movements, writes Passport files for price and product changes, and runs invoice extraction with Gemini, UPCitemdb, and a Redis cache.
Data and apps
PostgreSQL, Hasura, and three web apps
Stores stations, products, supplies, invoices, and stock. Triggers and scheduled jobs keep stock and expiry status current for the owner, station admin, and super admin apps.
the build
What owners and station staff work with
The platform splits into three web apps for owners, station admins, and super admins, plus the desktop sync app. These four workflows carry most of the daily work.
Staff check AI-read invoice lines instead of typing them
Staff scan or upload a vendor invoice. The AI returns the vendor, invoice number, date, total, and line items. Each line is checked against UPC data and mapped to a category. Staff fix anything that's wrong and save. Fuel delivery invoices and product invoices each have their own extraction path.

Each POS syncs through a desktop app that runs in the background
A Tauri app sits in the system tray of the station's back-office PC and can launch at startup. It watches the POS outbox, uploads new files, and delivers pending price and product files to the POS inbox. Owners see each station's last connection and sync time in the web app, so a station that stops syncing is easy to spot.

Price changes queue for the next sync, with status tracked
Owners change a product or fuel price once. The platform writes a Passport file, stores it, and marks it pending. The sync app delivers it to the station's POS on its next cycle. Price history stays on record, so owners can see what changed and when.

Reports that reconcile, including a printable store close
Owners see supplies and c-store sales by station, category, and period, each with a trend against the previous period. Barcodes carry no expiry date, so staff type it on the first add and the platform fills it in after that. A database procedure moves stock from fresh to expiring soon to expired. The end-of-day store close report downloads as a print-ready PDF.

Proof
What V1 delivered, and what this page can't claim yet
The platform completed V1 build and testing. Production figures such as daily transaction volume, time saved on invoices, or stock accuracy haven't been verified for publication, so they aren't shown.
| Result | What changed | Period or context | Evidence and limitation |
|---|---|---|---|
| POS integration | Two-way file exchange with Gilbarco Passport, no POS replacement | V1 build, 2025 | Project-recorded: sync app and XML handlers in the project repositories |
| Sync cadence | Batched sync about every 10 minutes | V1 design decision | Project team; real-time sync deferred to a later version |
| Invoice pipeline | Gemini 2.5 Flash extraction with UPC checks and staff confirmation | Third model plan, after discovery tested 6 OCR tools and 3 LLMs | Project-recorded; no accuracy figure verified for publication |
| Reporting accuracy | Refills excluded from sales; refunds, discounts, and net split from gross | Reporting work, 2025 | Project-recorded; the size of the earlier overcount isn't quantified here |
The lesson
Don't schedule POS integration work until you have the documentation
An older POS with little documentation and slow vendor support across time zones set the pace of this project. We got through it by trial and error, and trial and error takes time that's hard to plan for.
If you're connecting an older POS or back-office system, get the integration documentation first. Then set up a direct line between your engineers and the vendor's technical support team. Do both before the integration goes on the schedule.
Where to go next
What V1 left out on purpose
Scope decisions made during the engagement. None of these are part of the delivered V1.
- Next 01
Other POS brands
V1 supports Gilbarco Passport only. Wayne Nucleus, Tokheim, and Lightspeed were deferred.
- Next 02
Supplier EDI for invoices and purchase orders
Staff create purchase orders in the app, but ordering happens outside it. Electronic data interchange (EDI) of invoices and POs with suppliers didn't make V1.
- Next 03
Real-time sync
The roughly 10-minute batch covers V1. Live sync is planned for a later version.
- Next 04
Owner billing and delivery apps
Stripe subscriptions for station owners came out of scope in March 2025. Uber Eats and DoorDash integrations moved to V2.
How it runs
- React
- Three role-based web apps (owner, station admin, and super admin) share components and data hooks in one Nx monorepo, so a reporting fix reaches every app at once.
- Tauri and Rust
- The sync app runs on the back-office PCs stations already own. Tauri with a Rust core gave a small system-tray app with native network-share access, auto-start, and auto-update, without bundling a full browser runtime.
- Hasura and PostgreSQL
- Stock rules live next to the data. A trigger sets low-stock status, a stored procedure moves shelf-life status, and Hasura exposes the model to all three apps through GraphQL.
- AWS Lambda
- XML parsing, stock deduction, invoice extraction, and scheduled jobs for daily sales, stock snapshots, and inventory status run as functions, so 40+ stations syncing on the same cycle don't need servers sized in advance.
- Gemini 2.5 Flash via the Vercel AI SDK
- Structured output returns each invoice as a schema-checked object. That replaced page-by-page prompts and free-text parsing in the first version of the pipeline.
Common questions
Yes. This platform works with the files Passport already exchanges with its back office. Passport writes sales to an outbox folder and imports changes from an inbox folder. A small desktop app on the back-office PC moves those files to and from the cloud, and cashiers keep using the POS as before. V1 supports Gilbarco Passport only; other POS brands were deferred.
Because a duplicate upload would count the same sales twice. The sync compares files before uploading so each sales file is processed once, and discovery showed that check made shorter intervals unsafe for V1. A batch about every 10 minutes keeps stock counts trustworthy. Real-time sync is planned for a later version.
Staff upload or scan an invoice. Gemini 2.5 Flash reads the vendor, invoice number, date, total, and line items and returns them as structured data. Each product line is checked against UPC data and mapped to the store's categories. Staff review the suggestions and confirm before stock changes. Fuel and product invoices use separate extraction paths.
They need their own rules. A coffee sale also uses a cup and a lid, so supply items link to the products that consume them. A refill uses supplies but isn't a sale, so it's excluded from sales counts. When single units run out, the system draws from box stock and tracks how many boxes remain across boxed and loose items.
This V1 took about 12 months including discovery, with the platform build running from January to September 2025. That covered three web apps, a desktop sync app, POS integration, invoice AI, and reporting. The POS integration drove most of the uncertainty. A scope with documented POS access or fewer apps would take less time. See our fuel retail software service and pricing guide.
Related work
Related operations and document AI work

Musgrave receipt-scanning loyalty platform
A 12-week loyalty campaign platform for SuperValu and Centra, with 1,610 receipts processed in four weeks.

Mobile POS app for UAE merchants
A UAE FinTech operator replaced hardware POS terminals with a mobile payment app that processed 10,000 transactions in its first three months.

UrShipper multi-carrier shipping software
How RaftLabs helped UrShipper recover a stalled shipping platform and move 214 existing customers into the portal.