Automation · Warehouse systems

Automating a system that has no API

The advice for a system with no API is to replace it. That advice assumes you own the system. When the platform belongs to a customer and the contract runs for years, the real question is what you can build against the interface you actually have, which makes this 3PL software built beside a customer-owned warehouse system rather than a replacement for it. Here, deciding whether to extend, replace or leave a system alone came down on extend.

An estimated $29,044 a year in labour time saved, by the site's own tracker. Where the figure comes from

5 concurrent automation sessions
3 cancel-safe restart attempts
2 independent claim guards
Confidential client & operation

What it saves

$29,044 a year, estimated

The site's efficiency-savings tracker timed this task by hand and again with the software. The labour time saved comes to an estimated $29,044 a year.

TaskSaved a year
Picking orders on the handheld screens$29,044

Estimated by the site's own efficiency-savings tracker: each task timed by hand and again with the software, the time saved valued at a fully loaded labour rate over a 252-day working year. Labour time only, to the whole dollar.

Built for a contract-logistics operation running a customer-owned warehouse management platform, where no integration interface was available.

Automating a warehouse platform with no API by driving its screensA job queue in the database feeds an automation service that runs a bounded pool of five browser sessions. Each job is claimed in the database before it starts, cancellation is checked before every step, a stalled session restarts only if nothing was picked yet, and cases the service should not handle alone stop and escalate with the item, location and reason attached. The sessions type into the handheld pick screen and the upload portal's one-time-passcode login. The service fails closed: a status field is believed over an HTTP 200, an unreadable confirmation page counts as a failure, and inbound requests are marked handled before an upload runs, so a crash leaves a visible flag rather than a duplicate order.JOB QUEUE · DATABASEJob queueone row per run, claimed firstJOBSTATEJOB-0412runningJOB-0413claimedJOB-0409escalatedJOB-0407returned to queueJOB-0405cancelledClaimed here before it starts:2 independent claim guardsA job stuck mid-run when the serverrestarts returns here, with a log lineWHAT WE BUILTAutomation serviceA bounded pool: 5 concurrent sessionsClaims the job in the database first, sotwo workers can never pick the same orderChecks for cancellation before every step;a stop keeps and reports the partial workA stalled session restarts only if nothingwas picked yet: 3 cancel-safe attemptsCases it should not handle alone stop andescalate with item, location and reasonSESSION POOL · 5 SLOTSa sixth job waits its turnDRIVEN IN A BROWSER SESSION · NO APIUpload portalONE-TIME PASSCODE• • • • • •one sheet at a timeHTTP 200status: failedPICK UNITORDERUNITLOCQTY0412SN-0931A-07-31ENTER next unitkeys each unitHandheld pick screensame screens, every unitFails closed: a portal that answers HTTP 200 with the real outcome in a status field is read as the status field says; an unreadable confirmation page isa failure, not a success; retries wait past the one-time-passcode window; and inbound requests are marked handled before an upload runs, so a crashleaves a visible flag to retry rather than a duplicate order.
A queued job is claimed in the database, then one of five bounded browser sessions keys it into the handheld pick screen and the upload portal as an operator would. Job ids and screen values are illustrative.

Can you automate a warehouse system that has no write API?

Yes, by driving its screens the way an experienced operator would. In this build a queue-driven Selenium service runs up to five concurrent sessions against the handheld pick screens and the upload portal; each job is claimed in the database before it starts, cancellation is checked before every step, and a stalled session restarts only if nothing was picked yet.

The problem

Several jobs a day were pure data entry against a web interface. Picking an order meant walking a handheld terminal through the same sequence of screens for every single unit, and orders could run to thousands of units. Bulk uploads meant logging into a portal, keying a one-time passcode, and uploading a workbook one sheet at a time. Wave planning meant logging into a third system, running its planner, waiting for it, running its scheduler, waiting again, then filtering a grid and exporting it — a sequence somebody had to stay logged in for. That last job grew into the wave-planning pipeline built on the same approach.

It was slow, but the real cost was that it was boring. Boring work done by hand produces errors, and errors against live inventory are expensive to unwind. Microsoft's answer to the same problem is desktop flows for systems with no API; here the screens were web pages, so a coded service drove them directly.

What we built

A queue-driven automation service that drives the real interfaces the way an experienced operator would, with a bounded pool of concurrent sessions and a job record for every run. It is screen-level access when there is no other way in, written for live inventory.

Everything in the design assumes it will fail at the worst possible moment, because eventually it does.

  • Jobs are claimed in the database before they start, so two workers can never pick the same order
  • Jobs stuck mid-run when a server restarts are returned to the queue on boot, with a log line explaining why
  • Cancellation is checked before every step, so stopping a job preserves and reports the partial work already done
  • A stalled session restarts only if nothing was picked yet — never after real work, where a retry would double-count
  • Cases the automation shouldn't handle alone stop and escalate with the specific item, location and reason attached

Three jobs, by hand and from the queue

JobBeforeFrom the queue (after)
Picking an orderA handheld terminal walked through the same screens for every unitClaimed in the database, run by one of five bounded sessions
Bulk uploadsLog in, key a one-time passcode, upload one sheet at a timeRun as a job, with retries timed past the passcode window
Wave planningRun the planner, wait, run the scheduler, wait, then filter a grid and download itRun as a job with its own record
A failed uploadAn earlier automated version reported success anywayFails closed and says so

Failing closed on live inventory

The most valuable work on this project was unglamorous. An earlier version reported success when uploads had actually failed — the error was raised inside a block that caught and discarded it, so the caller always saw a green result. Rewriting those paths to fail closed, and to treat an unreadable confirmation page as a failure rather than an assumption, mattered more than any speed gain.

One portal returned HTTP 200 on failure, with the real outcome buried in a status field. One vendor template had a spelling error in a column header that the system matched on, so the typo had to be preserved deliberately and documented. Retries wait past the one-time-passcode window, because retrying inside it just re-sends the same rejected code.

Where an ordering decision could cause damage, the safe choice was taken explicitly. Inbound requests are marked handled before any upload runs: a crash mid-upload leaves a visible flag someone can retry, whereas the alternative risks creating duplicate orders in the customer's system. A stuck flag is recoverable. A duplicate order is not.

The result

Work that consumed hours of attention runs from a queue with a full audit trail. More importantly, the failure modes are now visible: when something doesn't work, it says so, in a place someone is looking. The same approach carries over to receiving, pick and ship workflows on scanners when the screens belong to someone else.

Questions about this build

How do you stop screen automation from double-counting a pick?

By refusing the retry that would do it. A stalled session restarts only if nothing has been picked yet, never after real work; jobs are claimed in the database before they start, so two workers cannot pick the same order; and cancellation is checked before every step, so stopping a job preserves and reports the partial work already done.

How does the automation handle the portal's one-time passcode?

It waits the window out. Retries wait past the one-time-passcode window, because retrying inside it just re-sends the same rejected code. Confirmations get the same suspicion: one portal returned HTTP 200 on failure, with the real outcome buried in a status field, and an unreadable confirmation page is treated as a failure rather than an assumption.

Why mark a request handled before the upload runs?

Because a stuck flag is recoverable and a duplicate order is not. Inbound requests are marked handled before any upload runs, so a crash mid-upload leaves a visible flag someone can retry, where the other order of steps risks creating duplicate orders in the customer's system. Wherever an ordering decision could cause damage, the safe choice was taken explicitly.

Built with

  • Python
  • Selenium WebDriver
  • SQL Server
  • Thread pools & work queues
  • TOTP
  • openpyxl

On the details we left out

Client, location, system names and volumes are withheld deliberately; the savings above are the tracker's dollar estimates, with the minutes, rates and headcount behind them left out. We're happy to go deeper on architecture and approach on a call.

Services this draws on

Workload Optimization

Squeeze more throughput out of the hardware and licenses you already pay for.

See Workload Optimization →

System Integration

Connect aging systems to modern APIs, data platforms, and SaaS tools.

See System Integration →

More projects

Integration · Inbound logistics

Knowing where inbound freight actually is

Inbound shipments were tracked by opening carrier websites one tab at a time. We replaced the tabs with live carrier integrations and a workflow that routes exceptions to the person who can fix them.

Read the story →
Automation · Inventory transfers

The order builds itself from the attachment

Inventory transfer requests arrived as spreadsheets attached to email, in as many shapes as there were requesters, and were keyed in line by line. Now the attachment becomes a validated, reference-numbered order on its own.

Read the story →
Planning · Outbound fulfilment

Turning a flat order backlog into a plan the floor can pick

A daily backlog feed said what had been ordered — not what could be built, released or picked. We built the pipeline that turns it into a wave plan, a queued pick order, and a screen that says where each one got to.

Read the story →
Get in touch

Got a system with no API that someone keys by hand?

Name the system, the screens someone keys by hand and how often. Talk to an engineer.

Prefer to talk?

Call 832-598-8234 or email msco@stoneagesoftware.com. Houston, Texas — serving Houston, The Woodlands, Conroe, Sugar Land, Katy, Pearland, and the Greater Houston metro.

What happens next

We read your message, an engineer replies with questions or a straight answer, and if a call would help, we book one. From the first reply on, you're talking to the people who'd do the work.

No obligation — we reply within one business day. This form sends your details to Stone Age Software LLC so we can respond to you. See our privacy policy for how we handle them. This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.