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": [ ... ]
}
formatstringrequired
Always quiver-garage. Identifies this as a Quiver export file.
versionnumberrequired
Schema 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)required
UTC timestamp of when the export was generated.
bikesarray<Bike>required
All bikes in the garage. Each bike includes its components and service history nested inline.
ridesarray<Ride>required
All 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": [ ... ]
}
idstringrequired
Unique identifier (nanoid). Stable across imports — re-importing the same bike will update it in place rather than creating a duplicate.
typestringrequired
One of: road, gravel, mountain, cyclocross, commuter, tt, track, bmx, other.
starting_kmnumberrequired
Kilometres already on the bike when it was added to Quiver. Total bike km = starting_km + sum of all ride distances.
image_datastring | null
Base64-encoded image data. null if the bike has no photo.
image_mimestring | null
MIME type of image_data. Either image/jpeg or image/png.
componentsarray<Component>required
All 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_idstringrequired
Component 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_kmnumberrequired
Total bike km at the time this component was installed.
interval_kmnumber | null
Per-component service interval override. When null, the type default is used.
replaced_atstring | null
ISO date when this component was retired. Null means still installed.
service_logarray<ServiceLog>required
All 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"
}
actionstringrequired
service, inspect, or note.
action_kindstring | null
Specific action subtype (e.g. chain_hotwax). Null for free-text entries.
recurrence_kmnumber | null
How 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"
}
sourcestringrequired
manual for rides entered by hand; strava for rides imported via the Strava integration.
external_idstring | null
Activity 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 id are 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.