Export an Outlook or Exchange mailbox via Microsoft Graph into a date-organized tree of Markdown files with frontmatter and saved attachments.
pks email export [options]$ pks ms-graph registerRequired one-time sign-in before the first export
$ pks email exportExport the inbox into ./.emails
$ pks email export --after 2026-01-01 --folder inboxExport inbox messages received on or after a date
$ pks email export -o ./my-emails --max 100Export the 100 most recent messages into a custom folder
pks email exports messages from a Microsoft Graph mail folder into local Markdown files, one file per message, with attachments saved alongside them.
pks email has a single subcommand, export. It pages through a Microsoft Graph mail folder — inbox by default — applying any date, sender, or subject filters you give it, and writes each message as a Markdown file with YAML frontmatter under a date-based folder tree. HTML bodies are converted to Markdown; attachments land in a sibling attachments/ folder next to each message.
pks email is not a sync tool. It never diffs or updates content for a message it has already exported, and it does not detect edits or deletes made on the server — only --overwrite forces a message to be re-fetched and rewritten.
--no-attachments.pks email export does not authenticate itself — it checks whether you are already signed in and, if not, prints Not authenticated. Run pks ms-graph register first. and exits 1 without ever calling the mail-export Graph endpoints. (If a stored token is more than an hour old, this check itself makes one Graph /me validation call before deciding.) Run pks ms-graph register first.--folder, if you are not exporting the default inbox — for example sentitems or archive. An invalid folder name surfaces as an HTTP error from Graph.Note. A Graph-layer guard elsewhere in the export path throws a different message —
"Not authenticated. Please sign in first using 'pks graph auth'"— if a token becomes invalid mid-export. That command name is stale; the real command ispks ms-graph register.
Fetches messages from one mail folder, converts each to Markdown, and writes it under {output}/raw/{yyyy}/{MM}/{dd}/{HHmmss}-{subject-slug}/{subject-slug}.md. Subject slugs are lowercased, non-alphanumeric characters collapsed to -, trimmed, and capped at 60 characters; an empty subject becomes untitled. When a message has attachments and --no-attachments was not passed, they are decoded and written into a sibling attachments/ folder next to the message file.
Messages are fetched newest first (receivedDateTime desc) in pages of 50, following Graph's @odata.nextLink automatically. --max does not change the page size — it truncates the accumulated list client-side once the running total reaches your requested count, so even --max 1 can trigger a full 50-message page fetch before truncating. --after, --before, --from, and --subject are translated into a Graph $filter clause and applied server-side.
A progress spinner reports Exporting emails... {current}/{total} - Fetching and Exporting emails... {current}/{total} - Exporting. When the run finishes, a table reports Exported, Skipped, and Error counts, followed by the absolute output directory path.
pks email export [options]
| Flag | Default | Description |
|---|---|---|
-o, --output <PATH> | .emails | Output directory for the exported tree. |
--after <DATE> | — | Only messages received on or after this date (yyyy-MM-dd). |
--before <DATE> | — | Only messages received on or before this date (yyyy-MM-dd). |
--from <EMAIL> | — | Filter by sender email address. |
--subject <TEXT> | — | Filter to subjects containing this text (Graph contains(...)). |
--folder <NAME> | inbox | Mail folder id or well-known name to export from. |
--max <COUNT> | — | Maximum number of messages to export, applied after fetching. |
--no-attachments | false | Skip downloading attachments. |
--overwrite | false | Re-export and overwrite messages whose output file already exists. |
-v, --verbose | false | Show detailed output. |
pks email export
Exports the default inbox, all pages, into ./.emails, skipping any message whose output file already exists.
pks email export --after 2026-01-01 --folder inbox
Exports only inbox messages received on or after 2026-01-01.
pks email export -o ./my-emails --max 100
Exports into a custom directory, capped at the 100 most recent messages.
Availability.
--afterand--beforeaccept onlyyyyy-MM-dd. Any other format, including a full ISO datetime, is rejected with anInvalid --after/--before date format. Use yyyy-MM-dd.error and exit code1before any Graph call is made.
--overwrite, a message whose deterministic output path already exists is skipped, not re-fetched or diffed. Pass --overwrite to force a message to be rewritten — note this is not scoped to what changed, so combined with a broad filter it re-downloads every matching message and its attachments again.1 but most messages exported fine. The command exits 1 whenever any message failed (ErrorCount > 0), even if hundreds of others succeeded. Check the Errors count in the summary table rather than relying on exit code alone to mean "total failure".--subject behaves unexpectedly with quotes in it. The value is inserted directly into a Graph contains(subject, '...') filter with no escaping of embedded single quotes, which can break or alter the query.--folder fails with an HTTP error. The value must be a real Graph mail-folder id or well-known name (inbox, sentitems, archive, and so on) — an unknown name surfaces as a Graph HTTP error, not a local validation message.