Delivery
The
{entity} prefix is how the pipeline decides which file it is looking at, so it must be
exactly one of the eight documented names. The timestamp portion only has to make the
filename unique, but it must not introduce underscore-separated words that change how the
prefix reads.
There are no network prerequisites — no static egress IP to register, no firewall rule.
Authorisation is the SAS token issued at project onboarding, which grants create, write and
list but deliberately not read or delete.
Rules that apply to every file
Validation is all-or-nothing
If any row fails, the entire file is rejected. It is moved toerror/ with a message
naming every failing row, and no production table is touched. A file that passes is moved to
archive/.
Row numbers in error messages are 1-based over data rows, excluding the header:
external_system_id as the key
external_system_id is the source system’s own identifier for the row. It is both the
deduplication key and the update key: re-sending the same identifier updates the existing
record rather than creating a second one. It must be stable over time.
The one exception is vehicle_readings, which is keyed on the vehicle and reading date
instead — see that page.
last_updated decides whether an update applies
last_updated is the source system’s last-modified timestamp for the row. It is stored as
external_updated_at, and an incoming row is applied only when its last_updated is
strictly newer than the stored value. Equal or older rows are silently skipped — not
reported as errors, just counted.
Two details worth knowing:
- A row whose stored
external_updated_atis empty is always overwritten by an import, regardless of timestamps. - If the incoming
last_updatedis empty but the stored value is not, the row is skipped.
last_updated whenever the row changes, or your correction will be silently
discarded.
Duplicates within one file are allowed
If the sameexternal_system_id appears more than once in a file, the row with the highest
last_updated wins. Ties are broken by position — the last occurrence in the file wins.
A row with an empty last_updated loses to any row that has one.
References are sent as human-readable codes
Rows refer to other data by code, never by database identifier:vehicle_number,
component_serial_number, product group and slot codes, task_external_system_id. Every
code must already exist in the project the container belongs to.
An unresolvable code rejects the file, with two exceptions: product_group_code and
slot_position on defects, which import as empty with a warning.
Note that free-text classification fields — action_code, cause_code, severity,
category, fault_indication_code and similar — are not validated against any list.
They pass through as written.
Correcting and closing records
Two entities close an open period by re-sending the original row rather than sending a new one —vehicle_configurations (a dismount closes a mount) and vehicle_out_of_service
(returning to service closes a period). In both cases you re-send the same
external_system_id with the closing fields filled in and a newer last_updated.
The files
defects
Faults recorded against a vehicle or component.
vehicle_readings
Daily absolute odometer snapshots.
vehicle_configurations
Mount and dismount events. Load this one first.
maintenance_records
Maintenance work that was performed.
measurements
Condition measurements. The most strictly validated file.
vehicle_out_of_service
Periods where a vehicle was unavailable.
cleaning_records
Cleaning and toilet emptying events.
maintenance_plans
Maintenance programmes. Not a daily file — read the warnings.
maintenance_plans is sent only when a plan revision
changes, and it behaves differently enough to deserve careful reading.