Case study 04 · Cross-border package-forwarding platform · 2018 – present
We moved a shipping business off spreadsheets and a team of more than ten people onto one platform, one manual job at a time, over eight years.
That includes the year its biggest partner replaced its API.
Written by Hesham Ghoniem, lead engineer · ·
- Sector
- Cross-border logistics, package forwarding from the US into a Caribbean island market
- Engagement
- Ongoing since 2018 · design, build, running and maintenance
- Scope
- The whole platform: customer portal, warehouse screens, admin and billing · 3 databases
- Stack
- PHP 8
- Laravel 10
- Vue 3
- Inertia
- Tailwind
- MySQL
- Redis
- Cloud object storage
- 8 years — Continuous development, one data-model rebuild, zero downtime
- 10+ to 1 — Staff working in spreadsheets, replaced by one platform · client's figure
- 1 day to 1 min — Time to prepare one customer invoice, before and after · client's figure
- 0 — Minutes the business stopped trading while we replaced its data model
- 29 — Routines on a timer, the fastest every ten seconds
Overview
We built them a system. Then we took the manual jobs out of it.
Most US online stores won't ship to a small Caribbean island. Our client gets around that. A customer receives a real US address and shops as if they lived in the United States. The business then consolidates their packages, flies them over, clears them through customs and hands them over at home.
When the owner first came to us, the whole business ran on spreadsheets and a team of more than ten people. Staff tracked packages and ship requests in Excel. Costs, customers and invoices sat in sheets that referenced other sheets, and nobody saw the gaps until a customer called about a missing box. Each new customer added work.
We built them a system. Then we spent eight years removing manual jobs from it, one at a time. Staff no longer create each customer's US address and email it to them, import package lists from the US warehouse by spreadsheet, work out whose package it is when the name on the box doesn't match the account, or re-type ship requests into the partner's website. The client tells us an invoice that took one person a working day now takes under a minute.
In 2024 the US warehouse partner replaced its API with a different one and set a date to retire the old one. The new API organised data in a way our platform had never been built for. We rebuilt the platform's data model to match it while the warehouse took deliveries every working day, and no customer saw any interruption. Later, when the partner's records began to disagree with ours, we built checks that find the differences.
On the left, in grey, nested spreadsheets with broken links between them and a team of more than ten people around them. On the right, one screen showing a single package record with its photographs, items, customer, status and invoice. One arrow joins the two sides.
The challenges
Eight problems, and six of them were jobs people did by hand.
A shipping business run on spreadsheets
A team of more than ten people kept packages, ship requests, customers, costs and invoices in Excel. The sheets were nested, inconsistent and incomplete. A package could appear in one sheet and be missing from another, and nobody could say for certain where a given box was. Tracking packages and creating ship requests took a long time, and customers usually found the mistakes before staff did.
The business needed one record for each package, customer, ship request and invoice, shared by every screen.
Every US address made by hand
New customers started by waiting. For each one, staff opened an account with the US warehouse partner, copied the US address and suite number it issued, and emailed it to the customer. They did this by hand for every person who signed up.
Packages that arrive under the wrong name
The US warehouse partner first sent package lists as Excel sheets. Staff exported them, cleaned them and imported them into the system. When the partner started sending live notifications instead, the spreadsheets went away, but the hardest part of the job stayed.
Someone still had to decide which customer each package belonged to. Shoppers at big online retailers type any name they like at checkout, and it is often not the name on their account. Some packages arrived addressed to the business's shared suite instead of the customer's own. Staff read labels and guessed all day, and each wrong guess put one customer's package in another customer's account.
Customers couldn't see what they would pay
A customer who asked to ship their packages learned the cost only when the invoice arrived. The price depended on the shipping speed, the weight of each package, the number of packages consolidated, the declared value, express handling, insurance and customs on every item. The customer saw none of it while deciding.
Every ship request done twice
A customer's ship request arrived in the admin screens. A member of staff then opened the warehouse partner's website and entered the same request again so the shipment could start. If the partner had a problem with a package in that request, staff found out only by checking the partner's site. Staff typed every request twice, and mistakes appeared in the copying between the two systems.
One invoice, one working day
Staff built every customer invoice by hand, and the client tells us each one took one person a whole working day. They priced each package at the rate for the speed the customer chose. They worked out customs item by item, from value, quantity and the duty rate for that type of goods.
Then they added the fees: handling, consolidation, insurance, express handling, oversize, storage, special handling, lithium batteries, home delivery, the concierge service, membership and the use of the customer's own tax number. Then the discounts, anything the customer had bought from the business with the shipment, and the paperwork. All of it in two currencies.
Nobody had written the rules down. Before we could automate them, we had to work out what they were.
A deadline the business didn't choose
A few years into the integration, the warehouse partner announced a new API that worked differently from the old one, and a date when the old one would stop working.
The new API treated a package and the items inside it as separate records. Our platform had stored one row per item since the start. A box holding five things was five rows, and each row repeated the same tracking number, shelf, customs state and photographs. Every screen, report and invoice depended on that layout, across 69 parts of the codebase.
So we could not connect the new API as it was. The data model it needed did not exist yet, and there was no maintenance window to build one. The warehouse takes deliveries every working day, and customers pay for speed.
When the partner's records are wrong
Once the platform exchanged data with the partner in both directions, the partner's errors reached our data too. Some ship request submissions failed, and staff had to find them, fix them and send them again.
Later, problems in the partner's systems left a large number of packages in the wrong state. The partner showed them as shipped while our platform still showed them as ready to send. Some customers could see packages that had already left and tried to ship them again. Others had packages the partner showed as shipped that never arrived.
Staff could not sort this out by hand. The platform needed to find, on its own, every place where its records and the partner's disagreed.
2024 – 2025 · the partner's new API
The year the partner replaced its API
In 2024 the US warehouse partner told us it was replacing its API and would retire the old one at the end of the year.
For this business that was serious. The partner's API delivers every incoming package, links each package to its owner and sends every outgoing shipment. Without it, staff would have gone back to spreadsheets and re-typing for every package, every day, until a replacement was ready. That was the work the previous six years had removed.
A straight swap was impossible, because the new API stored packages and items as separate records and our platform stored one row per item.
The usual approach is a maintenance window, a large migration script and a weekend offline. The warehouse never stops, so that approach was not available.
We built the new data structure next to the old one.
We built it in the same live production database, and connected the new API to it while the old integration kept the business running. Then we converted eight years of records into the new structure, a thousand at a time, while staff and customers kept using the platform. Three safeguards made that safe.
Three safeguards
- Every old record tracked its own progress. The converter marked each one as converted, not yet converted, or set aside because something didn't reconcile. We could stop the converter, resume it, or run it again from the start without duplicating or losing a record.
- The converter never guessed. When it could not match a record to a customer, it set the record aside for a person to check instead of assigning it to the closest match. A person can recover a set-aside record. Nobody can easily find a record the converter matched to the wrong customer.
- Old and new stayed in step. Once the bulk conversion began, copy and reconciliation routines ran every minute until the switchover.
Then we moved the application over, one screen at a time.
Warehouse screens, the customer portal, ship requests, invoicing and reports each moved in turn. The old structure kept serving each screen until the new one matched it. The two structures ran side by side for about five months. We switched the last screen over in February 2025, and nothing visible happened. There was no announcement, no maintenance page and no rise in support requests.
A date rail with four markers: in 2024 the new API is announced and the old one is to retire at year end; in September and October 2024 the new integration and new data structure are built next to the old ones; from September 2024 to February 2025 both structures run together; in February 2025 the last screen is switched. One unbroken bar under the whole rail shows the warehouse worked every single day.
On the left, a tall table where a box holding five things is five rows, each repeating the same tracking number, shelf, customs state and photographs, with the repeated cells greyed out. On the right, the same box as one package card with five item cards beneath it.
Three results are still useful today
- Items became real records. Each item now carries its own duty rate, supplier invoice and problem history, and invoices are built from the items being shipped. None of that was possible with one row per item.
- The platform stores the partner's version of events separately from its own. What the warehouse says it received and what the customer owns are now two sets of records. That separation made the checks in challenge 8 possible a few months later.
- We have tested the method. The next time a partner changes its API, it will be planned work.
The warehouse took deliveries every working day of the rebuild, and no customer saw a maintenance page.
What we built
Eight years of building, one manual job at a time.
Chapter one · 2018 · One record per thing
Moving the business off spreadsheets
The first job was to put the whole business in one system. It held customers, their packages, the ship requests they raised, staff screens to track each package from arrival to pickup, shipping costs and invoices, with one record per thing that everyone could see.
A shipping-rate calculator followed in 2020, then memberships and an ocean-freight channel. In 2021 we added a paid no-wait option that lets a package keep moving while its paperwork catches up. The ten-person spreadsheet team now worked from the same screens.
Chapter two · No staff involved
US addresses without staff
We built a sign-up form that the client gave to its customers, connected to the warehouse partner's API. When a customer fills it in, the platform opens their account with the partner, receives their US address and suite number, and emails both to the customer within seconds. Staff no longer create or send addresses.
Chapter three · 2021 – 2023 · Three stages of intake
Matching packages to customers automatically
Package intake changed three times. In 2021 staff imported spreadsheets exported by the partner. In 2023 the partner's API began sending a notification the moment the US warehouse scanned a package. Automatic assignment then removed most of the remaining manual work.
Chapter two made it possible, because every customer now had their own suite, opened through the partner's API. The platform reads the suite number the partner reports for each package, finds the matching customer and assigns the package. Nobody reads a label. The platform holds back any package addressed to the shared suite, or any package it cannot match with confidence, for a person to check.
Chapter four · Data flows both ways
Live prices for customers, and ship requests sent to the partner automatically
As a customer builds a ship request, the platform calculates the price live. It prices every package at the chosen speed and adds handling, consolidation, insurance, express handling and customs on each item. It recalculates the moment the customer changes anything, and the customer can compare every speed side by side.
When the customer submits the request, the platform creates the shipment with the warehouse partner through the API, so nobody enters it on the partner's website. If the partner reports a problem with a package in the request, the platform records it, notifies staff, and tracks the problem until someone resolves it.
Chapter five · 2023 · No switch-over
A Laravel application on the same database
The original hand-built system had reached its limit. We did not build a replacement on the side and plan one risky switch-over. Instead we connected a Laravel application to the same live database and moved features across one at a time. Customers saw no switch-over because the old and new code shared the same data throughout.
This also meant that when the 2024 rebuild came, both applications already worked from one database, so it could run without downtime.
Chapter six · 2024 – 2025 · A new API, a new data structure
The partner replaces its API
The panel above describes this chapter. It covered a new API and a new data structure, took months, and ended when we switched the last screen in February 2025.
Chapter seven · 2025 – now · Both applications maintained · Ongoing
Partner errors, compliance and payments
The platform now records every ship request it submits to the partner. When a submission fails, it stores the reason, shows it to staff, sends an alert, and resubmits once staff fix the cause. When problems in the partner's systems left packages in the wrong state, we built the checks described in the integration section below.
In the same period we added the destination country's tax-registration process to the product, added online card payments, reconciled bank transfers and counter payments properly, and closed a rare duplicate-payment risk with a database rule instead of a code check. We still maintain both applications today.
Throughout
What stayed the same
The customer's view of their suite stayed the same through every rebuild. It shows a list of packages, each with photographs, a weight, an itemised contents list and a plain statement of what it needs before it can ship. Customers kept using that screen while we replaced the data model underneath it.
The invoice
The invoice, from a working day to under a minute.
The client noticed this change first. The rules that one person used to hold in their head are now in the system, and staff generate an invoice with one button.
Labelled lines converge on one invoice: package price by shipping speed, customs per item, handling, consolidation, insurance, products, extra fees, discounts and payments. One button builds it in under a minute, down from one person's working day, by the client's account. Out of it come a sequential number and a due date, a PDF emailed with a payment link, a total that stays current, and the same result whoever presses the button.
What the button does
- It prices every package at the speed the customer chose. It takes the actual weight, or the measured size for oversized boxes, rounds up to the next pound, caps it at the price list's maximum, and charges the rate for that speed. It does not guess. If an oversized box has no recorded dimensions, it stops the invoice instead of charging the wrong weight.
- It calculates customs item by item. It takes each item's value and quantity, adjusts for the shipping speed, and multiplies by the duty rate for that type of goods. It then saves the result on the item, so staff can trace every customs figure on the invoice back to the item it came from.
- It carries over what the customer chose. Express handling, consolidation, insurance, home delivery, the concierge service, use of their own tax number and the no-wait option all arrive on the invoice already priced, as selected when the customer requested the shipment.
- It collects everything owed. It attaches products the customer bought with the shipment, membership fees set to pay on the next invoice, charges already recorded against the customer's account, and delay fees.
- It lets staff add what the system can't know. Staff can add an extra fee with a description to any invoice, or record a charge against a customer in advance so it lands on their next invoice. Common surcharges such as special handling, lithium batteries, health inspection and wireless-device clearance are tick boxes.
- It adds everything up and keeps the total current. The invoice combines twenty-three charge, fee and discount lines, including partnership and pickup discounts, subtracts payments already made, and adds a card-payment fee where it applies. It handles two currencies, assigns a sequential invoice number and a due date, produces a PDF, and emails it to the customer with a payment link. If staff add a charge later, the total recalculates.
Preparing one invoice took a member of staff a working day. It now takes one button and under a minute.
Technical deep-dive · the US warehouse partner
Inside the two-way integration with the US warehouse partner
The warehouse the business depends on is in the United States, and a partner runs it. The partner has its own systems and its own data model, and every package the business earns money on passes through that warehouse twice, once on the way in and once on the way out.
The key decision was to keep our own copy of the partner's data.
The platform stores what the warehouse says it received, meaning received packages, received items and received shipments, as its own records. It keeps them separate from what the customer owns, and customers only see our records. Because the platform keeps the two apart, it can compare them, and most of what follows depends on that comparison.
On the left, the US warehouse partner, in grey, sends eight message types into the platform, each archived on arrival, and receives ship requests back, validated, submitted and retried on failure. On the right, the platform's own database is split into what the partner says happened and what the customer owns, with a two-way arrow between the halves: compared, and differences flagged before the customer finds them.
Messages in
Eight kinds of message, at any hour and in any order.
The partner reports a package received, package details corrected, a package status change, a service request update, shipment details, a shipment status, a customer screening result, and one older format the platform still accepts.
The platform validates each message before writing anything. It writes inside a database transaction, so a message is either fully processed or not processed at all. It also archives every raw message before reading it, so if the partner ever disputes what it sent, the original is on file.
Matching
Each package finds its owner by suite number.
The platform matches each new package to its customer using the suite number the partner reports. Every customer has their own suite, issued through the partner's API when they signed up. The platform then pulls in the package's photographs and item list, looks up each item's duty rate from its customs code, flags any problem, and notifies the customer, all within a minute. It holds back any package addressed to the shared suite, or any package it cannot match with confidence, for a person to check.
Ship requests out
Submitting a ship request takes several steps.
The platform checks that the partner will accept every package in it, chooses the carrier and service level from the speed the customer paid for, and sends the request. It then checks that the partner reports back the same number of packages that went out.
The platform records every submission. When one fails, it stores the reason, shows it to staff, sends an alert, and resubmits once staff fix the cause, so no shipment goes unsent without someone knowing.
When the partner's records disagreed with ours, we added three checks
- Arrived compared with requested. When a shipment lands, the platform compares the packages that arrived with the packages listed on the ship request. It flags an extra package nobody asked for, or a missing package a customer paid to ship, before the customer notices. A routine re-checks the last three months every half hour in case the platform missed a live message, and staff can dismiss a known exception so the routine stops flagging it.
- The partner's status first. Before a customer can add a package to a new ship request, the platform checks the partner's latest status for it, so nobody can ship a package the partner has already shipped.
- Clearing stale requests. The platform finds ship requests that the partner's problems left stuck and sets them aside, so staff work from a list of live requests.
We store what the partner says happened and what the customer owns as separate records, so the platform can compare them.
How we work
Nine rules, each put to work on this platform.
Remove one manual job at a time
Each year of this engagement removed a task people used to do by hand: creating addresses, importing package lists, matching packages, re-typing ship requests and building invoices. We pick the job that costs the client most, automate it completely, and move on to the next.
Rebuild underneath the running system
We connect the replacement to the same live database as the original, move features across in small steps, keep every saved link working, and let the old system serve each screen until the new one matches it. When the final switch happens, users should see no change.
Never guess with a customer's data
When a record won't convert cleanly, or the platform can't match a package to an owner with confidence, it sets that record or package aside for a person instead of picking the closest answer. A person can recover a set-aside record, and a record matched to the wrong customer is much harder to find.
Write the rules down before automating them
We worked out pricing, storage periods, membership logic and customs rates from how the business operated, wrote them down, checked them against real invoices, and only then built the invoice button. An undocumented rule, once automated, repeats its errors on every invoice.
Keep our own copy, and the partner's version too
When the product depends on a partner's data, the data lands in our database and the platform reads it from there. Storing the partner's version separately from ours lets the platform detect when the two disagree.
Plan for partners to change
Partners replace APIs, their systems have outages, and they set deadlines. We built the platform so that a partner change is planned work, as the 2024 API replacement showed.
Measure the automation
Every automated step reports what it did, and the team receives a daily count of what the system handled with no person involved. The client uses that count to check that the automation works.
Fix the cause
When a rare risk of recording a payment twice appeared, we removed the existing duplicates and added a database rule that rejects new ones. Unlike a check in code, the database rule applies to every code path, including ones written later.
We still run it
For eight years we have run both applications, with error logs, failed-job alerts, an archive of partner traffic and alerts in the team's chat, so we see most problems before a customer does. The client has one contact for any problem.
Technologies used
Nine layers, and what each one is for.
Application
Laravel runs the newer of the two applications. It is widely used, so other developers can pick it up.
- PHP 8
- Laravel 10
Interface
The ship request page recalculates the price without reloading. Staff screens also work on a phone.
- Blade
- Vue 3
- Inertia
- Tailwind
- Vite
Data
Both applications share one production database, which let the rebuild run without downtime. Redis runs the queues and the cache.
- MySQL
- Redis
Background work
Slow work runs in the background instead of making a user wait. The fastest routine runs every ten seconds.
- Laravel Horizon
- 15 queued jobs
- 29 scheduled routines
Warehouse and carriers
These cover accounts, US addresses, package and shipment notifications, outgoing ship requests and live tracking.
- The warehouse partner's API, both ways
- Two express carriers' tracking APIs
Payments
No single card processor serves the whole customer base in this market.
- Regional online payment gateway
- PIN card payments
- Bank transfer and cash reconciliation
- Two-currency conversion
Messaging and marketing
Notifications reach customers in the apps they read, as well as by email. Alerts about exceptions go to the staff who can fix them, in the chat tool they already use. Customer groups, such as customers who never shipped or stopped ordering, sync to the marketing platform automatically.
- Business messaging API
- Chat-app bot
- Transactional email
- Real-time browser push
- Two team-chat platforms
- Email-marketing platform
Documents and storage
These hold invoices, package photographs, archived partner messages and spreadsheet exports.
- PDF invoices
- Two cloud object stores
- CSV import and export
Engineering
The platform archives every partner request and response, and we test every release on staging first.
- Git
- Staging environment
- Structured API logging
- Laravel Pint
- Clockwork
- Bot protection on public forms
Automated testing
Automated tests check each change before it reaches production.
Integrations, by what they do
- US warehouse partner's API
- Express carrier tracking API
- A second express carrier's tracking API
- Regional online payment gateway
- Business messaging API
- Chat-app bot
- Team-chat platform
- A second team-chat platform
- Email-marketing platform
- Real-time push service
- Cloud object store
- A second cloud object store
- Bot protection
The platform archives every partner request and response before processing it, and posts failures to the team's chat. A broken integration shows up as an alert to a person instead of as a customer whose account has gone quiet.
How the platform works
Seven steps, from a sign-up form to a collected shipment.
Step 1
Sign up and get a US address
The customer fills in the sign-up form. The platform opens their account with the US warehouse partner through the API, receives a real US address and suite number, and emails both to the customer. Nobody in the office is involved.
Step 2
Shop, and each package finds its owner
When the US warehouse scans a box, the partner notifies the platform. The platform matches the box to the right customer by suite number, even when the name on the box differs from the name on the account. It pulls in the photographs and itemised contents, looks up each item's duty rate, flags any problem and tells the customer, all within a minute. A package it cannot match with confidence waits for a person.
Step 3
Clear the paperwork, or keep moving
The platform knows what each item is missing, such as a supplier invoice, a duty category, a tax number or an ID document, and reminds the customer until the package can ship. Customers on the no-wait option don't have to wait. Their package keeps moving, the platform sends reminders at 36 hours, and a small delay charge applies after 48 hours if the paperwork still hasn't arrived.
Step 4
See the price change live
The customer picks which packages to send and how fast. Shipping, handling, consolidation, insurance, express fees and customs recalculate each time they change something. They can compare all four shipping speeds side by side, each with an estimated total and an estimated arrival date. The customer knows the price before any invoice exists.
Step 5
The request goes to the partner
The platform saves the ship request and sends it to the warehouse partner through the API. It validates the request first, chooses the carrier and service level, and confirms the package count afterwards. If the submission fails, staff see the reason and resubmit once they fix it. If the partner reports a problem with a package, the platform records it and tells staff.
Step 6
Check, shelve and invoice
The shipment flies. The platform compares what arrived with what the customer requested and flags any difference before the customer finds it. After customs clearance, it picks the shelf with the closest weight range that still has a free slot. Then one button builds the invoice: every package priced at the chosen speed, customs item by item, twenty-three charge, fee and discount lines in two currencies, and a PDF emailed with a payment link, in under a minute.
Step 7
Collect, with accurate updates
The platform tells the customer the shipment is ready. If customs held part of it, the customer sees which tracking numbers are still waiting and why. If a box stays past its free storage period, the platform sends a warning and later applies a charge.
Routines that run with nobody watching
- Memberships. When a customer cancels a membership, it drops back to free once the paid month ends, and the platform downgrades expired memberships automatically.
- Storage. The platform tracks storage days for packages in two separate queues.
- The concierge service. Concierge-service customers appear on a daily worklist, so staff deal with every one of them, not only the ones who complain.
- Customers who never shipped. The platform tracks customers who signed up but never shipped and syncs them to the email-marketing platform.
- Support. Support staff can open the platform as a specific customer, see what that customer sees, fix the problem and switch back.
- Reports. Eight standing reports cover packages, payments, shipments, ship requests, top clients, open balances and prepaid customs.
Three businesses on one platform.
The same platform runs the business's ocean-freight channel and a second forwarding brand alongside air cargo. Each has its own customers and its own database.
Results
What the business can do now that it couldn't in 2018.
Work without spreadsheets
Packages, customers, ship requests, costs and invoices are one set of records that everyone can see. A team of more than ten no longer maintains nested sheets.
Give every customer a US address without anyone typing it
The customer fills in a form and receives a US address and suite number by email within seconds.
Match packages to customers automatically
Staff no longer import spreadsheets or read labels to guess whose box it is, even when the name on the box doesn't match the account.
Show customers the price before they commit
The price updates live for every shipping speed, includes customs, and shows cost and arrival date side by side.
Send ship requests to the warehouse without re-typing them
The platform creates each request once, submits it through the API, tracks failures, and records problems the partner reports.
Invoice in under a minute instead of a working day
The invoice button prices packages at the chosen speed, calculates customs item by item, combines twenty-three charge, fee and discount lines, accepts extra fees, handles two currencies, and generates and sends a PDF. The result is the same whoever presses the button; the time saved is the client's own figure.
Keep trading when its biggest partner replaced its API
The partner introduced a new API with a different data model and a deadline. We rebuilt the data model to match, and the business lost no working days.
Find the partner's errors before customers do
The platform compares arrived packages with requested ones, checks the partner's live status before a package can ship, and clears stuck requests.
Collect fees that used to go uncharged
The platform tracks storage charges, no-wait delay fees, concierge-service fees and membership fees, notifies customers and adds the fees to invoices. Staff don't have to remember them.
Run three businesses on one platform
Air cargo, ocean freight and a second forwarding brand each have their own customers and data.
The engineering, in numbers
- 8 Years of continuous development and operation
- 0 Minutes of downtime during the data-model rebuild
- ~5 Months the old and new data structures ran side by side
- 69 Parts of the codebase rebuilt for the new data structure
- 3 Stages of package intake: spreadsheet, live notification, automatic assignment
- 8 Types of incoming partner message handled
- 23 Charge, fee and discount lines combined by the invoice button
- Under 1 min Time to prepare one invoice, down from a working day · client's figure
- 81 Types of record the system stores
- 165 Database changes applied without interrupting service
- 29 / 15 Scheduled routines / queued jobs
- 13 External services integrated
- 39 Transactional email templates
- 1,722 Tracked changes across both codebases
- ~127,000 Lines of application code
Honest status
Both applications are in production today. The original PHP system and the Laravel platform share one database, and we still maintain both. The original application received 139 tracked changes in 2025, and both received changes in 2026, the original most recently in May and the Laravel platform in August.
We fully replaced the data model and switched it over in February 2025. We are still moving the application itself across, screen by screen, using the same method that made the data rebuild safe.
The automation, in what it replaced
Eight jobs people did by hand, and what does them now.
- Before: Creating US addresses. Staff opened an account with the partner and emailed each customer by hand. — Now: A sign-up form creates the account and sends the email.
- Before: Getting package lists into the system. Staff exported spreadsheets from the partner and imported them. — Now: The partner sends a notification the moment it scans a box.
- Before: Working out whose package it is. Staff read labels and guessed all day. — Now: The platform matches packages automatically and holds back the ones it isn't sure of.
- Before: Creating ship requests with the partner. Staff re-typed each request on the partner's website. — Now: The platform submits it through the API, tracks failures and retries them.
- Before: Finding out what the partner had done. Staff checked the partner's site. — Now: Problems, status changes and mismatches arrive in the platform automatically.
- Before: Building invoices. One staff member spent a working day on each invoice. — Now: One button produces it in under a minute.
- Before: Choosing a shelf for each shipment. A supervisor decided. — Now: The platform picks the shelf with the closest weight range that still has a free slot, and staff can override it.
- Before: Chasing missing paperwork and storage fees. Staff sent individual emails and had to remember. — Now: The platform sends reminders, warnings and charges on a fixed schedule and logs each one.
Each of these was a person's job before it became a scheduled routine.
Related work
Forwarding Warehouse & Resale Platform
Logistics · 2021 · Design and build
330 — Server endpoints: 66 screens, 264 single-purpose operations
Air Cargo & Customs Platform
Logistics · 2025 – 2026 · Specify, build, deploy
7 days — Empty repository to a live, documented API. Both sides signed the contract on it
Next step
Is part of your business still running on spreadsheets?
Work with a team that removes the manual jobs one at a time, keeps you trading when a partner changes its API, and still runs what it builds.
Client anonymised · every figure counted from the source repositories, except the three marked as the client's · Hesham has worked as a full-stack developer since 2016 and founded CompertMe in 2020 · October 2026