Skip to content

Sync vehicles with your ERP

Most organizations keep vehicles in an ERP or asset register (Odoo, SAP, Dynamics, Sage) as well as in Fleet. This recipe keeps them aligned: the ERP owns purchase data, Fleet owns operational status and meters.

Scopes: vehicles:write, branches:read.

Use the ERP’s asset number as Fleet’s vehicle_number, which must be unique. Look the vehicle up by it, then update it or create it.

const API = 'https://api.get-fleet.com/v1'
const headers = { Authorization: `Bearer ${process.env.FLEET_API_KEY}`, 'Content-Type': 'application/json' }
async function upsertVehicle(asset, retried = false) {
const fields = {
vehicle_number: asset.number,
name: asset.description,
make: asset.make,
model: asset.model,
year: asset.year,
vin: asset.chassis_number,
license_plate: asset.registration,
purchase_date: asset.acquired_on, // "2024-03-15"
purchase_price: asset.cost, // "4250000.00"
purchase_currency: asset.currency,
}
const found = await (await fetch(`${API}/vehicles?vehicle_number=${encodeURIComponent(asset.number)}`, { headers })).json()
if (found.data.length === 0) {
const res = await fetch(`${API}/vehicles`, {
method: 'POST', headers: { ...headers, 'Idempotency-Key': `asset-${asset.number}-create` }, body: JSON.stringify(fields),
})
// 409: the number was taken between the lookup and the create. Look it up once more and update instead.
if (res.status === 409 && !retried) return upsertVehicle(asset, true)
if (!res.ok) throw new Error(`${asset.number}: ${(await res.json()).code}`)
return res.json()
}
const { vehicle_number, ...changes } = fields
const res = await fetch(`${API}/vehicles/${found.data[0].id}`, { method: 'PATCH', headers, body: JSON.stringify(changes) })
return res.json()
}

Map branches by listing GET /v1/branches once and matching on code, then send branch_id.

To retire a vehicle, set "status": "disposed". Fleet keeps its history; vehicles are not deleted through the API.

Pull changes since your last run and write the fields Fleet owns (status, meters) back to the ERP:

let since = await loadCheckpoint() // e.g. "2026-11-01T00:00:00.000Z"
let cursor
do {
const url = new URL(`${API}/vehicles`)
url.search = new URLSearchParams({ updated_since: since, sort: 'updated_at', limit: '100', ...(cursor && { cursor }) })
const page = await (await fetch(url, { headers })).json()
for (const v of page.data) {
await erp.updateAsset(v.vehicle_number, { status: v.status, odometer: v.meters.primary.reading })
since = v.updated_at
}
cursor = page.next_cursor
} while (cursor)
await saveCheckpoint(since)

If people edit vehicles in Fleet and the ERP pushes changes at the same time, send the ETag you read as If-Match on your PATCH. A 412 means someone changed the vehicle in between: read it again and reapply.