API
39 endpoints, 61 operations. JSON in, JSON out. The reference data is open; anything about your containers needs a key.
This page is generated from the route registry and checked against the source in CI, so it cannot drift from what the server actually does.
Authentication
Send your key as Authorization: Bearer ds_live_….
Issue one under Developer in the app.
A key is shown once at creation and stored only as a hash. We cannot recover a lost key, only replace it.
- Public
- No credentials.
- Signed in
- Any key or session. Scoped to your organisation.
- Staff
- Platform operators only.
- Root
- Platform owner only.
- Signature
- Authenticated by request signature, not by a key.
Rate limits
Fixed window, keyed by your account when signed in and by IP otherwise. Every API response carries the remaining budget, not just the 429 — a client that can only discover the limit by hitting it has no way to back off politely.
| Budget | Per minute |
|---|---|
| catalogue Reference reads. | 240 |
| rates Pricing, which is the expensive path. | 60 |
| write Anything that changes state. | 20 |
| auth Deliberately tight: this is the surface a credential-stuffer hits. | 10 |
Limits are counted per server instance. Behind more than one instance the effective
ceiling is higher — the response says scope: per-instance so
you are not guessing.
Read the source field
Chassis, appointment and empty-return responses say whether the figures came from a terminal feed or from fixtures. The fixtures are deterministic and realistic, which is exactly why they must not be dispatched against. Empty-return acceptance is fixtures everywhere today: no provider publishes it through an API — it lives on per-terminal notice boards.
Reference
/api/ports
Rate budget catalogue — 240/min.
/api/ports/{code}
- code (path, required)
- UN/LOCODE, e.g. USLAX.
Rate budget catalogue — 240/min.
/api/carriers
Rate budget catalogue — 240/min.
/api/carriers/{scac}
- scac (path, required)
- Standard Carrier Alpha Code.
Rate budget catalogue — 240/min.
/api/lanes
Rate budget catalogue — 240/min.
/api/chassis
- port (query, required)
- UN/LOCODE.
- equipment (query)
- 20ft, 40ft, 40ft tri-axle, Genset or 45ft.
Rate budget catalogue — 240/min.
Read `source`: `fixtures` means no provider is connected and the numbers are generated. Do not dispatch against them.
/api/chassis/search
- equipment (query)
- Narrow to one equipment type.
- port (query)
- Comma-separated UN/LOCODEs. Omit to search every port.
- limit (query)
- Page size, 1–100. Defaults to 20.
Rate budget catalogue — 240/min.
`count` is the total match; `results` is the page. An unknown port code is a 400, never a silent omission.
/api/empty-returns
- port (query, required)
- UN/LOCODE.
- line (query)
- Ocean line SCAC.
- date (query)
- YYYY-MM-DD. Defaults to today.
Rate budget catalogue — 240/min.
Always fixtures today: no surveyed provider publishes acceptance through an API. Acceptance also changes same-day — confirm before dispatch.
/api/rates
Rate budget rates — 60/min.
Per diem
/api/per-diem/{container}
- container (path, required)
- ISO 6346 container number.
- horizonDays (query)
- How far to project, 0–90. Defaults to 14.
Rate budget catalogue — 240/min.
Each day carries `billed`. Days after `asOf` are a projection, not an invoice line. `billedBy.attributed` is false for leased or unrecognised boxes — do not bill off those figures.
/api/per-diem/calendar
- year (query)
- 2000–2100. Defaults to the current year.
- coast (query)
- west, east, gulf or inland. West adds the ILWU contract days.
Rate budget catalogue — 240/min.
Published so a dispute can rest on something checkable. Holidays are computed from the rules that define them, not from a table that goes stale.
Operations
/api/moves
Rate budget catalogue — 240/min.
An illegal status jump is refused rather than corrupting the per-diem clocks downstream.
/api/tracking
Rate budget catalogue — 240/min.
A container belonging to another organisation answers 404, byte-identical to one that does not exist.
/api/bookings
Rate budget write — 20/min.
/api/quotes
Rate budget write — 20/min.
/api/exceptions
/api/alerts
Account
/api/account
/api/preferences
/api/team
An invite link works once and expires after an hour. It is emailed to the invitee; only when no email could be sent does the POST response carry it, for the inviter to pass on. It is never stored — only its hash.
/api/developer
A key is shown once at creation and stored only as a hash. It cannot be recovered, only replaced.
/api/feedback
/api/feedback/{id}
- id (path, required)
- Thread id.
Billing
/api/billing
A checkout can only become `active` on a gateway confirmation. With no gateway configured it stays `pending` and never counts towards MRR.
/api/billing/webhook
Rate budget write — 20/min.
HMAC-SHA256 over `<timestamp>.<raw body>`, five-minute tolerance, idempotent by event id. With no secret configured it answers 503 and refuses everything rather than trusting it.
Authentication
/api/auth/signup
Rate budget auth — 10/min.
Answers 503 when Basefyio auth is not configured. Without tokens from Basefyio (email verification pending) it points to /verify instead of signing in.
/api/auth/invite
Rate budget auth — 10/min.
The verified token’s email must match the invite. An address that already has an account in any organisation is refused; accounts are never moved between organisations.
/api/auth/login
Rate budget auth — 10/min.
Every access token is verified (RS256, pinned realm issuer, JWKS) on every request. Answers 503, never "wrong password", when auth is not configured.
/api/auth/logout
/api/auth/forgot-password
Rate budget auth — 10/min.
Answers identically whether or not the account exists, so it cannot be used to discover which addresses are registered.
/api/auth/reset-password
Rate budget auth — 10/min.
Basefyio checks the code; it is single-use and expires.
/api/auth/verify
Rate budget auth — 10/min.
/api/auth/resend-verification
Administration
/api/admin/audit
/api/admin/users
/api/admin/preflight
Rate budget catalogue — 240/min.
Answers 200 whether or not it is ready — a report, not a probe. Staff-only because the checks name which secrets are unset.
/api/admin/pricing
System
/api/health
/api/cron/per-diem-scan
Safe to run hourly: the alert ledger de-duplicates per container, per severity, per day. Also accepts a CRON_SECRET bearer token instead of a staff session.
Webhooks
Register an endpoint under Developer to receive move events. Deliveries are signed so you can tell ours from anyone else's POST:
X-DrayageScanner-Event: move.gated_out
X-DrayageScanner-Timestamp: 1789000000000
X-DrayageScanner-Signature: sha256=<hmac>
The HMAC is SHA-256 over <timestamp>.<raw body> with your
endpoint's secret. Verify it over the bytes you received — parsing the JSON and
re-serialising it changes them and the signature will not match.
Endpoints must be https. One that fails five times in a row is disabled automatically; a single success clears the streak.
Errors
Errors are JSON with an error string.
| 400 | The request is wrong in a way retrying will not fix. |
| 401 | No credentials, or they did not verify. |
| 403 | Signed in, but not allowed. |
| 404 | No such record — including one that belongs to another organisation. We do not distinguish the two, because doing so would confirm the id exists. |
| 429 | Rate limited. Honour Retry-After. |
| 503 | A dependency we need is not configured or not answering. |