This page is generated from the importer itself — every field, type and default below is the rule the app actually applies.
The smallest file that works
{ "watches": [ { "brand": "Tudor", "model": "Black Bay 58" } ] }
Brand is the only thing every watch must have. Everything else is optional and takes the default in the tables below.
A fuller one
{
"watches": [
{ "id": "w1", "brand": "Tudor", "model": "Black Bay 58", "status": "owned",
"purchasePrice": 3100, "dateAcquired": "2024-03-02", "serial": "SN-12345" }
],
"wearLogs": [ { "watchId": "w1", "date": "2026-01-14", "hours": 9 } ],
"straps": [ { "name": "Oxblood shell cordovan", "material": "leather", "width": 20 } ],
"serviceLogs": [ { "watchId": "w1", "date": "2025-06-01", "description": "Full service", "cost": 420 } ]
}
Rules that will reject a file
- Numbers must be numbers.
3100, not"£3,100"and not"3100". A price that arrives as text is rejected rather than quietly turned into nothing. - Dates are
YYYY-MM-DD."2024-03-02", not"March 2024". - A
watchIdmust match anidin the same file. An entry pointing at a watch that is not there would exist against nothing, so it is named rather than dropped. - Ids must be unique within their array. Two rows sharing one id would collapse into a single row on the way in.
- Nothing is applied unless the whole file passes. A file with an error leaves your collection exactly as it was.
Photos
Photos cannot be imported. They would have to arrive as base64 data URLs, which a spreadsheet cannot produce and which would make the file enormous. Add photos in the app after importing.
What happens to what you already have
An import replaces the collection on the device. If you already have watches, you are asked to confirm first, and the answer names how many will be replaced. Merging was considered and rejected: two rows both saying "Tudor Black Bay 58" may be one watch or two, and there is no way to tell from the outside — guessing either way loses something.
If you have an account, the import lands locally and the ordinary backup carries it to the server a few seconds later. Import never uploads anything itself.
watches
Required. An array of watches.
| field | type | required | notes |
|---|---|---|---|
brand | string | yes | e.g. "Tudor" |
model | string | no | e.g. "Black Bay 58" |
status | owned / wishlist / previouslyOwned | no | defaults to owned |
purchasePrice | number | no | a number, not "£3,100" |
marketValue | number | no | |
salePrice | number | no | only meaningful for previouslyOwned |
currency | USD / EUR / GBP / CHF / JPY / CAD / AUD / SGD / HKD | no | every price on this watch is in it; USD if absent |
serial | string | no | |
reference | string | no | |
dateAcquired | date | no | YYYY-MM-DD |
yearMade | number | no | |
country | string | no | |
lugWidth | number | no | |
notes | string | no | |
favorite | boolean | no | defaults to false |
private | boolean | no | never leaves the device for friends |
id | string | no | generated if absent |
wearLogs
Optional. An array of wear log entries.
| field | type | required | notes |
|---|---|---|---|
watchId | string | yes | must match a watch id in this file |
date | date | yes | YYYY-MM-DD |
hours | number or null | no | defaults to 0; null means worn, hours not recorded, and is kept as null |
id | string | no |
straps
Optional. An array of straps.
| field | type | required | notes |
|---|---|---|---|
name | string | yes | |
material | string | no | |
color | string | no | |
width | number | no | |
id | string | no |
serviceLogs
Optional. An array of service entries.
| field | type | required | notes |
|---|---|---|---|
watchId | string | yes | |
date | date | yes | YYYY-MM-DD |
description | string | no | |
cost | number | no | |
id | string | no |