Veltra Logic
2025–2026A national mobile-accessories distributor

The warehouse packs it. The courier just knows.

Automating the path from packing bench to courier waybill for a national distributor — three systems, three validation gates, no human in the happy path.

Warehouse WMSpacking slips · SOAPCourierREST APIintegration pipeline · postgresql① Fetcharchive raw XML② Enrichaddress · invoice③ Validate3 gates④ ShipGINV waybillSage ERPread-only · SQLinvoice-gatedcustomerboxesitemsany mismatch → blocked + alertwebhooks: address validation · labels · tracking
packed in the warehouse → validated against the ERP → live with the courier · no human in the happy path

Context

The client is a South African distributor of mobile accessories, shipping to resellers and retail partners across the country. Orders flow through three systems that were never designed to talk to each other: Sage Evolution (the ERP where orders are invoiced), a warehouse management system (where orders are picked and packed), and a national courier.

The problem

Every parcel that left the warehouse needed a waybill. Creating one meant a person logging into the courier's portal, finding the delivery address, typing it in, creating the consignment, and printing labels — for every single order, every day.

Manual re-typing is not just slow; it's where errors live. A mistyped address becomes a failed delivery. A missed box becomes a partial shipment. And because the person typing waybills worked from what was in front of them, there was no systematic check that what the warehouse packed matched what the customer was invoiced.

Constraints

  • The ERP could not be touched. Sage is the commercial system of record; the integration had to read from its SQL Server database without ever writing to it.
  • The warehouse API is one-shot. Its SOAP interface auto-acknowledges packing slips on fetch — retrieve a slip and it will never be offered again. Lose it, and it's gone.
  • The courier's consignment IDs are permanently consumed. Even a cancelled consignment's ID can never be reused. There is no undo.
  • Wrong shipments are worse than late shipments. A parcel to the wrong customer, or a box left behind, costs more than a consignment that waits for a human to look at it.

What I built

The engagement ran end to end: a business requirements spec I authored and all three companies signed — the distributor, the integrator, and the courier themselves — then joint UAT against the courier's staging environment, a phased production rollout (fetch-only, then enrichment, then live uploads), and production operation since.

The build is the four-phase pipeline in the diagram above — it turns "the warehouse marked it packed" into "the courier is coming," with no human in the happy path.

  1. Fetch — packing slips are pulled from the warehouse system and archived as raw XML before parsing, because the API only offers each slip once.
  2. Enrich — each slip is matched to its ERP order: delivery address, contact details, and critically the invoice number. Un-invoiced orders simply wait and retry; nothing ships before it's invoiced.
  3. Validate — three independent gates must all pass (more below).
  4. Ship — a consignment is uploaded to the courier with a deterministic waybill number derived from the invoice number. Webhooks then stream back address validation results, thermal-printer label data, and every tracking event from collection to proof of delivery — all stored as a full audit trail.

A small admin dashboard exposes the pipeline: live status counts, per-slip detail down to individual boxes and items, one-click retry, and mismatch resolution.

Three problems worth telling

1. Three systems, three versions of the truth

The pipeline's core stance is that it refuses to ship disagreement. Before any consignment is created, three gates must pass:

  • Customer gate — does the warehouse's customer code match the ERP's account code for that order? This catches the worst failure mode: a parcel addressed to the wrong company.
  • Box gate — three signals must agree: the box count the warehouse header claims, the boxes actually present in the slip, and the "2 of 2" totals on the pallet labels. They genuinely diverge in production. If the courier expects one parcel, the second box stays behind.
  • Item gate — every line on the packing slip is reconciled against the invoice: substitutions, quantity mismatches, and extra items are all caught before the parcel leaves.

Any mismatch blocks that order, alerts operations with the exact discrepancy, and waits for a human decision. Everything else flows through untouched. The design goal wasn't "no humans" — it was humans only where judgment is needed.

2. ERP addresses were written for invoices, not couriers

The ERP stores delivery addresses as six free-text lines whose meaning drifts by customer: line 5 might be a province, or a postal code, or nothing. Couriers need structured, validated addresses. The enrichment layer parses and normalizes — extracting street numbers, mapping province abbreviations, inferring provinces from postal-code ranges when absent — and the courier's own validation closes the loop: addresses its system can't resolve are corrected by human operators and pushed back via webhook, so the pipeline learns what the correct address actually was.

Storing every correction in full was never a requirement — it quietly became one of the project's most valuable outcomes. Across the first ~6,000 validated consignments, the system accumulated a cleaned dataset of delivery addresses: street corrections on 99% of them, province corrections on 97%, and over 3,000 postal-code corrections — 1,800 of which were routing-critical changes that would otherwise have sent parcels to the wrong hub. The client now owns a data asset it can use to fix its ERP addresses at the source.

An honest note: automation moved this work, it didn't erase it. Continuous automated uploads created a new recurring task — monitoring flagged addresses — that previously didn't exist as a category, because a human was implicitly doing that validation while typing. Good integration work is honest about these trade-offs.

3. Integrating systems that offer no second chances

Two of the three external systems are unforgiving. The warehouse system hands over each packing slip exactly once — so the pipeline archives raw XML before any parsing can fail, and a saved response can be replayed through the parser at any time. The courier burns every consignment ID forever — so waybill numbers are generated deterministically from invoice numbers (unique by construction) rather than invented, and uploads are retried with care rather than enthusiasm: three attempts, then a human.

The unglamorous safety net matters just as much: any slip stuck mid-process for 30 minutes is automatically reset to its last safe state, and the ERP connection is wrapped in a read-only guard that structurally blocks every write statement — enforced in code and covered by its own test suite, not just promised in a comment.

Results

First five weeks in production:

  • 6,200+ consignments created with zero manual waybill capture — roughly 180 orders a day that previously each required a human typing into a courier portal — with ~6,000 address validations and 8,600+ tracking events flowing back through webhooks.
  • The validation gates earn their keep: in the first UAT session, 57% of orders were blocked for box-count mismatches — faulty shipments that would previously have left the warehouse. In production, only 2.6% of uploads failed on validation or data errors, each flagged with the exact discrepancy for operations.
  • A webhook synchronization defect cost zero data. Because every raw payload is archived before any processing, a backfill script reconciled ~6,000 address and tracking statuses with nothing missing — the archive-first design paying for itself.
  • Every parcel has an end-to-end audit trail — from packing slip XML to proof-of-delivery webhook — queryable per consignment, with operational dashboards the client's team uses without developer involvement.
  • The pipeline ships with ~211 automated tests, including tests that prove the ERP connection cannot write.
  • The delivered system was reconciled line-by-line against the signed BRS in a compliance matrix — every requirement delivered, superseded items documented with reasons.

Role

Sole engineer — requirements (BRS), architecture, build, UAT, operations.

Python · FastAPI · PostgreSQL · SOAP (zeep) · SQL Server / IPsec VPN · Sentry