PlatformContainers

Per diem and demurrage tracking, on the box itself

Two clocks, two creditors, and only one of them running at any moment. DrayBase models both on the container record — where the last free day carries the source that supplied it and the time it was last refreshed — and counts the days the way a terminal and a steamship line count them: by calendar day, in the time zone of the port.

What is running todayThe data model and the risk math are built and under test: the container is a first-class record, and demurrage and per diem are computed in whole cents per calendar day of the port. What is not built: nothing pulls a last free day out of a terminal portal on its own, no alert goes out, no charge gets billed or disputed, and the dispatcher board that shows all of this is read-only. Every line below marked Planned is a design, not a feature.

The two clocksDifferent pockets

Demurrage is not per diem

They get used as synonyms in conversation and they are not the same charge, not the same contract and not the same company sending the invoice. A system that keeps one "free time" field is quietly wrong twice.

Demurrage
The terminal's charge for ground. It runs while the box is still inside the gate with its last free day behind it, and it stops the moment the container leaves. The free time is the terminal's to give and the bill is the terminal's to send.
Per diem
The steamship line's charge for their equipment. It starts once you have gated out and burned the free days written into the line's contract, and it runs until the empty is returned. Different creditor, different rate sheet, different argument when you dispute it.
Gate out
The switch between them. Demurrage returns zero the instant the container has a gate out; per diem returns zero until it does. One box, one clock — never both at once, and the free days that sit between them are the line's to give. That rule is in the calculation, not in a policy document someone has to remember.
Free time
Two fields, because they are two different grants and they rarely line up: the last free day belongs to the terminal, the free days belong to the line. The line record carries its own default free days, and each container carries the date its own per diem free time runs out — which is the date the math charges from.
Chassis
A third clock again, billed by the pool per day the unit is out. Chassis and pool reconciliation have their own page — the point here is that the box and the bogie are never the same meter.

The cheapest per diem day is the one that never starts.

Which is why the empty return is a routing decision, not a chore — street turns and dual transactions exist to stop the clock without a trip back to the gate.

The recordBuilt

The date that costs the money says where it came from

A last free day is worth exactly as much as your confidence in it. So it does not travel alone: the field carries the source that supplied it and the timestamp of the last refresh, and the vocabulary of sources is already fixed — manual, terminal portal, steamship line, email, EDI, API, driver app, inferred.

TGHU 772104820 GP · Import
Sample
Steamship lineMeridian Line · 4 free days by contract
TerminalElizabeth marine terminal
DischargedJul 24 · 18:40
HoldsNone · customs and freight clear
Last free dayJul 29 · manual entry, 2 h ago
Gate outJul 28 · 11:06 · demurrage stopped here
Demurrage accrued$0 · never went past the free day
Per diem free untilAug 01 · line's clock, not the terminal's
Empty returned · clock still running
Per diem accrued$0 · line's daily rate from Aug 02
Exposure if not returned PlannedToday the number is what has accrued as of now. Projecting it forward is next.

Illustration of the data model — sample values, not live data. The steamship line is fictional.

The fields that decide whether a move made money.

Vessel ETA, discharge, availability, customs and freight holds, last free day, free return date, cut-off, per diem free time, gate out and empty return all live on the container — not buried inside the load, because the terminal keeps moving this box whether or not a driver has touched it.

Two rates, one precedenceThe daily rate comes from the steamship line record, and a single container can override it when the deal was different. Only when neither carries one does the math fall back to a named constant — so no invented number quietly outranks a rate you negotiated. Showing on the row which of the three produced the charge is Planned.
Holds belong to the boxCustoms and freight holds sit on the container, not on the load, and they stop blocking the moment there is a gate out. A stale hold on a box already on the street should never paint red on anyone's board.
Event history Modeled, not fedAn append-only log with the raw payload from whatever supplied each event — the evidence you hand a line during a per diem dispute. The table is part of the schema; nothing writes to it yet, because there is no automatic feed to write it.
One carrier, one set of boxesEvery container row carries its company, and every read of one runs inside a transaction with the tenant pinned — Postgres returns nothing at all if it is not. Read more on how the isolation works.

The mathBuilt · tested

A day is a day in the port, not on the server

Terminals and lines bill by calendar day. A last free day of July 29 expires at the end of July 29 in Newark — and by then it has been July 30 in UTC for four hours. Count that in the server's clock and you flag a box as blown a day early, or you miss the day it actually blew. Both mistakes cost money and one of them costs trust.

Calendar days

The count is the distance between two civil dates, resolved in the port's zone — not elapsed hours. Twenty-three hours across a boundary is one day; twenty-five hours inside one is zero.

The zone is an argument

Never read from the environment, so moving the servers cannot move anybody's last free day. Every call today counts in the NY/NJ zone, and the same code counts a Los Angeles box correctly. Each carrier's own zone is already a field on its company record; feeding it into the math is wiring still to do Planned.

Daylight saving

The March shift produces a twenty-three hour day. It is still one day, and there is a test that says so, because this is exactly the kind of bug that only shows up twice a year and always on a Monday.

Whole cents

Exposure is integer cents multiplied by whole days, never a float. Money that has to survive a dispute does not get rounded by a binary fraction on the way to the screen.

This is the least glamorous part of the product and the one that decides whether the number on the board is worth anything. The calculation lives in one tested package the API calls once per row, and the screens render what came back instead of doing their own arithmetic — so when the free time watcher gets built it will call that same function rather than a second copy of it.

HonestlyLine by line

What the record does, and what it does not

CapabilityDrayBaseWithout it
Last free dayA field on the container with the source that supplied it and the time it was last refreshedA column in a spreadsheet retyped every morning
Demurrage exposureWhole cents per calendar day past the last free day, stopping at gate outA number nobody computes until the terminal invoice lands
Per diem exposureStarts after gate out and the free time on the box, stops the day the empty is returnedDiscovered on the line's statement weeks later
Daily ratesFrom the steamship line record, overridable per container, falling back to a named constant only when neither carries oneOne rate assumed for every line
HoldsCustoms and freight on the box, outranking everything else until there is a gate outA note in the load someone has to reread
Refresh from the terminalAutomatic pull from terminal portals and line APIs, with per-field freshness PlannedA dispatcher with eight browser tabs and a login that expired
Warnings before the dayA free time watcher that raises the box before the clock starts, ranked by dollars at stake PlannedFinding out on the invoice
Editing the containerWriting dates and holds back from the screen PlannedYour current system, which is where the data still lives
Billing the charge backTurning an accrual into a customer charge with the evidence attached PlannedAbsorbing it, mostly

The right column is what carriers describe living with today, not a benchmark we ran. The watcher in row seven is one of the six operating agents — none of them is running yet, and that page says so in the same words.

QuestionsStraight answers

What dispatchers ask about the clocks

What is the difference between demurrage and per diem?

Demurrage is the terminal charging you for occupying its ground after the last free day, and it only runs while the container is still inside the gate. Per diem is the steamship line charging you for holding its equipment after the free days in your contract, and it only runs once the box is out. Gate out is the handoff: demurrage stops, per diem starts. A container cannot generate both on the same day, and in DrayBase it cannot, because the two calculations exclude each other on that field.

When exactly does the per diem clock start?

At gate out, but the meter only charges once the free days are gone. Each container carries the date its own free time runs out, and the calculation charges from the day after it — counted in calendar days at the port, not in hours from the moment the gate scanned it. The line record keeps its default free days too; deriving the container's date from the line's contract instead of taking it as given is still to be built Planned.

Does DrayBase pull last free day from the terminal automatically?

Not yet, and we are not going to imply otherwise. The record is built to hold a machine-supplied date — it stores which source gave it and when it was last refreshed — but no scraper or line API is wired up today, so the value comes from a person. Automatic refresh is the next thing worth building on this page, and when it exists the freshness will be visible on the row instead of assumed.

Which time zone do you count days in?

The port's. The calculation takes the zone as an argument rather than reading the machine it happens to be running on, and today every count runs in NY/NJ — the market this is being built alongside. The same code counts a West Coast box correctly and the daylight saving boundary is covered by a test rather than by hope. Each carrier's zone is already stored on its company record; making the math read it instead of the default is wiring still to do Planned.

Can I use this to dispute a per diem invoice?

That is what the event history is designed for: an append-only record of what happened and when, keeping the raw payload of whatever reported it. Today the table is modeled but nothing fills it, and there is no billing or dispute workflow — so it is the design, not a promise you can hold us to. Say what your line disputes look like on the call and it will shape the order this gets built in.

Is container tracking priced separately?

No. There is one rate per active power unit per month and the containers those trucks move are part of it — see pricing, which also says plainly that the number itself is not set yet.

Early accessNY / NJ first

Bring us a week of your worst boxes

The fastest way to know whether this models your operation is to walk one bad container through it — the one with the hold, the moved return location and the line that counts free days differently. Tell us where you run and we will do exactly that.

Related: the dispatcher board · street turns · chassis and pools · the agents · what gets built next