Data portability
Garage Export Format
Version 1 · Format identifier: quiver-garage
Overview
Quiver can export your entire garage — bikes, components, rides, and service history — to a single JSON file. You own your data and can take it with you at any time.
To export: open Settings → Export garage. To import on another device or after reinstalling: open Settings → Import garage.
Bike photos are embedded in the export file as base64-encoded image data so the file is fully self-contained — no sidecar folders needed.
Top-level structure
{
"format": "quiver-garage",
"version": 1,
"exported_at": "2026-05-06T14:30:00.000Z",
"bikes": [ ... ],
"rides": [ ... ]
}formatstringrequiredAlways
quiver-garage. Identifies this as a Quiver export file.versionnumberrequiredSchema version. Currently
1. Future versions will be backward-compatible where possible; Quiver will reject files with a version higher than it understands.exported_atstring (ISO 8601)requiredUTC timestamp of when the export was generated.
bikesarray<Bike>requiredAll bikes in the garage. Each bike includes its components and service history nested inline.
ridesarray<Ride>requiredAll rides, stored flat at the top level rather than nested per bike. Each ride references its bike via
bike_id.Bike object
{
"id": "abc123",
"name": "Sunday Tarmac",
"brand": "Specialized",
"model": "Tarmac SL7",
"year": 2023,
"type": "road",
"catalog_id": 42,
"frame_material": "carbon",
"color": "Gloss Tarmac Black",
"size": "56",
"purchased_at": "2023-04-01",
"starting_km": 0,
"archived": false,
"image_data": "<base64>",
"image_mime": "image/jpeg",
"created_at": "2023-04-01T10:00:00.000Z",
"modified_at": "2026-05-01T08:22:10.000Z",
"components": [ ... ]
}idstringrequiredUnique identifier (nanoid). Stable across imports — re-importing the same bike will update it in place rather than creating a duplicate.
typestringrequiredOne of:
road, gravel, mountain, cyclocross, commuter, tt, track, bmx, other.starting_kmnumberrequiredKilometres already on the bike when it was added to Quiver. Total bike km =
starting_km + sum of all ride distances.image_datastring | nullBase64-encoded image data.
null if the bike has no photo.image_mimestring | nullMIME type of
image_data. Either image/jpeg or image/png.componentsarray<Component>requiredAll components ever installed on this bike, including retired ones (those with
replaced_at set). Active components have replaced_at: null.Component object
{
"id": "def456",
"type_id": "chain",
"brand": "Shimano",
"model": "CN-LG500",
"notes": null,
"installed_at": "2025-01-15",
"installed_at_km": 12500,
"interval_km": 3000,
"design_life_km_override": null,
"replaced_at": null,
"replaced_at_km": null,
"created_at": "2025-01-15T09:00:00.000Z",
"modified_at": "2025-01-15T09:00:00.000Z",
"service_log": [ ... ]
}type_idstringrequiredComponent category identifier:
chain, cassette, chainring, tire_front, tire_rear, brake_pads_front, brake_pads_rear, derailleur_rear, derailleur_front, bottom_bracket, and others.installed_at_kmnumberrequiredTotal bike km at the time this component was installed.
interval_kmnumber | nullPer-component service interval override. When null, the type default is used.
replaced_atstring | nullISO date when this component was retired. Null means still installed.
service_logarray<ServiceLog>requiredAll service events recorded for this component.
Service log entry
{
"id": "ghi789",
"date": "2025-03-20",
"km_at_service": 15200,
"action": "service",
"action_kind": "chain_hotwax",
"recurrence_km": 500,
"notes": "Waxed in Molten Speed Wax",
"created_at": "2025-03-20T18:00:00.000Z",
"modified_at": "2025-03-20T18:00:00.000Z"
}actionstringrequiredservice, inspect, or note.action_kindstring | nullSpecific action subtype (e.g.
chain_hotwax). Null for free-text entries.recurrence_kmnumber | nullHow many km until this action should repeat. Null means one-off.
Ride object
{
"id": "jkl012",
"bike_id": "abc123",
"date": "2026-04-28",
"distance_km": 87.4,
"duration_min": 195,
"notes": null,
"source": "strava",
"external_id": "12345678901",
"created_at": "2026-04-28T14:00:00.000Z",
"modified_at": "2026-04-28T14:00:00.000Z"
}sourcestringrequiredmanual for rides entered by hand; strava for rides imported via the Strava integration.external_idstring | nullActivity ID on the source platform (Strava activity ID). Used to deduplicate on re-import.
Import behaviour
Importing offers two modes:
- Merge — upserts records. Existing records with the same
idare updated; records not present in the file are left untouched. Safe for syncing between devices without losing new data added on the destination. - Replace all — wipes local data first, then inserts everything from the file. Use when restoring from a backup.
After a successful import, Quiver pushes the imported records to iCloud so they appear on all your devices automatically.