Overview
The Movements API provides endpoints to query and synchronize shipment movements (loads) from McLeod TMS. Each movement represents a shipment with one or more stops (pickup and delivery locations). Database Tables:movements- Main movement data (id, status, tractor_id, driver_ids, etc.)movement_stops- Stop-level data (city, address, ETA, scheduled times, etc.)
A- Available/AssignedP- Progress (active/in-transit)D- Delivered/Completed
Get Movements
GET /api/movements
Get movements with nested stops from the database.Filter by movement status (e.g.,
P for Progress/active movements)Maximum number of movements to return (1-1000)
-
Query Movements: Calls
get_movements()which queries themovementstable with:- LEFT JOIN to
tractorstable to get Terminalvehicle_id - Filter to exclude brokered movements (
brokerage != True) - Ordered by
id DESC(newest first)
- LEFT JOIN to
-
Fetch Stops: For each movement, calls
get_movement_stops_by_movement_id()to get associated stops frommovement_stopstable, ordered bymovement_sequence -
Driver Name Enrichment: Uses
DriverMcLeodMapperto fetch driver names from McLeod API:- Parses comma-separated
driver_ids(McLeod IDs like “M72323”) - Calls McLeod API
/v1/driver?id={driver_id}for each unique ID - Caches results in memory to avoid repeated API calls
- Returns comma-separated
driver_names(e.g., “John Smith, Jane Doe”)
- Parses comma-separated
-
Timing Status Calculation: For each stop, calculates
timing_statusandtiming_delta_minutes:- Compares
etatosched_arrive_early - Returns
early,on_time,late, orunknown - Returns delta in minutes (negative = early, positive = late)
- Compares
| Response Field | Database Column | Source |
|---|---|---|
tractor_id | movements.__tractorid | McLeod TMS |
vehicle_id | tractors.vehicle_id | Terminal API (via JOIN) |
driver_ids | movements.__driverids | McLeod TMS |
driver_names | - | McLeod API (runtime lookup) |
trailer_ids | movements.__trailerids | McLeod TMS |
Sync Movements
POST /api/movements/sync
Sync in-progress movements from McLeod API to the database. Uses pagination to fetch all matching movements.Ignored - McLeod filters by company based on API credentials
Movement status filter (e.g.,
P for Progress). Defaults to P if not specified.Maximum movements to fetch per status (1-1000)
-
Pagination Loop: Fetches movements from McLeod API in batches of 50 (
McLeodClient.get_movements())- Increments
offsetby 50 each batch - Continues until batch returns fewer than 50 movements (end of data)
- Increments
-
Parse & Validate: For each batch:
- Parses raw JSON into
MovementPydantic models - Logs parsing errors but continues processing
- Parses raw JSON into
-
UPSERT to Database: Calls
upsert_movements_batch():- Inserts or updates
movementstable usingON CONFLICT (id) DO UPDATE - For each movement, upserts associated stops to
movement_stopstable - Parses McLeod timestamp format (
YYYYMMDDHHmmss-timezone) to datetime
- Inserts or updates
-
Tractor Location Fallback: After sync, calls
TractorLocationService.update_for_movements_batch():- Uses stop coordinates as fallback location when Terminal GPS is unavailable
- Updates
tractors.latitude,tractors.longitude,tractors.last_location_provider='movement_stop'
Sync Delivered Movements
POST /api/movements/sync-delivered
Sync delivered movements (status=“D”) from McLeod API. Only updates movements that already exist in the database with status=“P” - prevents inserting new delivered movements that were never tracked.Ignored - McLeod filters by company based on API credentials
Maximum movements to fetch (1-1000)
-
Fetch Delivered Movements: Calls McLeod API with
status=D: -
Get In-Progress IDs: Queries
movementstable for all movement IDs withstatus='P' -
Filter: Only keeps fetched movements whose ID exists in the in-progress set
filteredcount shows how many matched- This prevents inserting delivered movements we never tracked as in-progress
-
UPSERT: Updates matching movements from
status='P'tostatus='D' - No Location Update: Skips tractor location fallback since delivered movements are no longer active
Sync All Movements
POST /api/movements/sync-all
Convenience endpoint that syncs both in-progress (P) and delivered (D) movements in a single call.
Ignored - McLeod filters by company based on API credentials
Maximum movements to fetch per status (1-1000)
- Calls
sync_movements(status='P')to sync in-progress movements - Calls
sync_delivered_movements()to sync delivered movements - Returns combined statistics from both operations
Health Check
GET /api/movements/health
Health check endpoint for the movements module. Response:Background Sync Services
In addition to manual sync endpoints, movements are also synced automatically by background services:MovementChangeSyncService
- Interval: Every hour (configurable via
MOVEMENT_CHANGE_SYNC_INTERVAL_SECONDS) - Behavior: Uses McLeod’s
changedAfterDateparameter to fetch only movements changed since last sync - Initial Run: Syncs movements from past 24 hours
- Config:
MOVEMENT_CHANGE_SYNC_ENABLED=true(default)
MovementFullSyncService
- Interval: Every 30 minutes (configurable via
MOVEMENT_FULL_SYNC_INTERVAL_SECONDS) - Behavior: Syncs all statuses (A, P, D) in full
- Config:
MOVEMENT_FULL_SYNC_ENABLED=true(default)

