How do I move a folder of documents into a vault without retyping everything?
How do I move a folder of documents into a vault without retyping everything?
Put the files in a folder and write one sidecar file beside them — a CSV or JSON list with one row per file giving its name, title, category and dates. Anything that can export a spreadsheet can produce it, and an importer that reads it can file hundreds of documents with their details intact instead of asking you to type each one.
The expensive part of moving documents between products is never the files. It is the metadata: the titles you chose, the categories you sorted into, the expiry dates you looked up once and would have to look up again. Most products will hand you the files. Rather fewer will hand you the rest in a form another program can read.
The general fix is a sidecar: one small text file sitting beside the documents that says what each one is. This page documents the format Keepsake reads, but there is nothing proprietary about the idea, and the format is deliberately plain enough that you could write an importer for it yourself in an afternoon.
The shape: a folder and one extra file
A folder of documents, with a single sidecar at its root:
my-documents/
keepsake-import.csv
passport-ali.pdf
car-insurance-2026.pdf
Insurance/
home-policy.pdfSubfolders are fine; the sidecar refers to files by their path inside the folder, using forward slashes. The sidecar may be CSV or JSON — CSV because a spreadsheet can save it, JSON because a script can write it. Both carry exactly the same nine columns.
The nine columns
| Column | What it holds |
|---|---|
file | The file's path inside the folder. The only required column — a row naming a file that is not there is reported, not guessed at. |
title | What the document should be called. Left out, the file name is cleaned up and used. |
category | A heading in your own words. Recognised ones are mapped; unrecognised ones are kept as a tag and the document is filed under Other. |
issue_date | When it was issued. |
expiry_date | When it expires. This is the column worth the effort — it is what a reminder is built from. |
doc_number | Passport number, policy number, account number. |
issuing_authority | Who issued it: HM Passport Office, an insurer, a council. |
tags | Comma-separated words to find it by later. |
notes | Anything else worth keeping with the document. |
Every column but file may be blank, and a sidecar that only lists file names is still worth writing: it is the difference between an import that knows which files you meant to bring and one that takes whatever it finds.
Dates, and why an unreadable one stays empty
Dates are read in these forms, and no others:
2026-04-12 2026/04/12 12/04/2026 12-04-2026 12.04.2026
Note what is missing: 04/12/2026 read as an American month-first date. A day-month-year format and a month-day-year format are indistinguishable for the first twelve days of every month, and a vault that guesses gets it right about two thirds of the time and silently wrong the rest. So a value that cannot be read unambiguously is left empty, and the import tells you how many documents that happened to, rather than inventing a date and then reminding you about it.
If you are producing the sidecar from a spreadsheet, format the date columns as YYYY-MM-DD text before exporting. Spreadsheets are enthusiastic about reformatting dates on the way out, and that one setting removes the entire question.
A worked CSV
The first row names the columns; order does not matter, and columns you have nothing for can be left out entirely.
file,title,category,expiry_date,doc_number,issuing_authority,tags
passport-ali.pdf,Ali passport,Passport,2031-04-11,123456789,HM Passport Office,"identity,travel"
car-insurance-2026.pdf,Car insurance 2026,Insurance,2027-03-31,POL-88213,Aviva,car
Insurance/home-policy.pdf,Home insurance,Insurance,2027-01-05,,Direct Line,houseThe same list as JSON, under a documents key, if a script is writing it:
{
"documents": [
{ "file": "passport-ali.pdf", "title": "Ali passport",
"category": "Passport", "expiry_date": "2031-04-11" }
]
}What will not come in, and why you are told
Only documents are imported: PDFs and images (pdf, jpg, jpeg, png, bmp, tif, tiff, gif, webp). Everything else is refused with the reason next to the file name, which matters more than the list does — an import that drops nine of ninety in silence leaves you believing all ninety arrived, and finding out on the day you need one.
- Programs are refused as programs. A vault is not a place to store an installer.
- Archives — zip, rar, 7z — are refused rather than opened. An importer that cannot see inside a container cannot tell you what it filed. Unpack it and import the folder.
- Office documents that can carry macros are refused. Nothing in filing a document needs code to run.
- Files whose name disagrees with their contents are refused as disguised.
- Files already in the vault, byte for byte, are skipped and said to be skipped — which is what makes it safe to re-run an import after adding thirty more documents.
Step by step
- Put the documents in one folder. Subfolders are fine. Keep it to documents — a folder that also holds a video library will spend a long time refusing the videos.
- Produce the list. In a spreadsheet, one row per file with the column names above in the first row. Save as CSV, into the same folder, named keepsake-import.csv.
- Format the date columns as YYYY-MM-DD text. Do this before exporting. It is the single change that prevents most of the dates arriving empty.
- Import the folder. On Windows, Add Document then Import a Folder. On Android, the import button on the upload screen. In the web app, Import in the sidebar. Nothing is uploaded in any of the three: the folder is read on the device you are sitting at.
- Read the summary, then fix the few. The end of the import names what was skipped and why, and how many documents came in without a readable date. That short list is the whole job that is left.
Questions
Do I have to write a sidecar at all?
No. A folder with no sidecar imports perfectly well — the file names become the titles and the top-level folder names become categories and tags. The sidecar exists for the case where you already hold better metadata than the file names carry, which is usually because you are leaving a product that had it.
Can I generate the sidecar from another product's export?
That is exactly what it is for. Most products can export a CSV of some kind; renaming its columns to the nine above is a spreadsheet job of a few minutes, and it turns a folder of anonymous files into a filed archive. If the product exports JSON instead, a short script will do it.
What happens to a category Keepsake does not recognise?
The document is filed under Other and your heading is kept as a tag. This is deliberate and it is the rule the whole importer is built on: a document filed under a category the importer invented is a document you cannot find, because you will not think to look for it there. Under Other with your own word attached, it is still one search away.
Is any of this uploaded while it is being read?
No. The import reads a folder on the device you are using, against a vault that is already unlocked there. There is no sign-in to any other service, no OAuth prompt and no server of ours in the path.
Can I run the same import twice?
Yes, and that is the intended way to use it. Every file is matched against what the vault already holds, so a second run of the same folder adds nothing and tells you it added nothing. Add thirty documents to the folder and re-run it, and thirty documents arrive.
Where Keepsake fits
Keepsake is our product, so read this part with that in mind. Everything above is true whether or not you use it, and most of it you can do with a folder and an afternoon.
Keepsake reads this format on Windows, Android and the web app, from the same shared definition of the columns — so the same folder produces the same result on all three, and a file refused on one is refused on the others in the same words. The importers for Trustworthy, Everplans, paperless-ngx and Drive or OneDrive folders are the same machinery with the metadata read from somewhere else.
The reason we document the format rather than just shipping the button: an import path you can produce by hand is one you can also use to leave. Keepsake's own export writes your documents back out as files with their details beside them, for the same reason.