The archive does what it was built to do, unattended, on both machines: four providers, incremental and resumable, Markdown plus Joplin, on a schedule, with results pushed to a phone and failures that announce themselves. The one item left on the roadmap — a StartOS service that consolidates both machines into one corpus — is an optional convenience, not a gap. Hence 1.0.0 rather than another 0.x. The code in this release shipped across b6ce636..8c3e00a: the claude.ai 403 session-invalid fix, the fork-subagent recursion fix, the FAILED-push backstops on both platforms, and asset-download retries with cross-run recovery, a circuit breaker and media counts in the notification. This commit cuts the release and pays a documentation debt. The README had reached 939 lines / 6,490 words / 48 KB / 96 headings, doing four unrelated jobs at once, and FUTURE.md asked for the split to ride along with the next substantial README edit rather than break every existing anchor for its own sake. A release is that edit. README.md is now 303 lines and keeps only what you need to install and configure the tool; the rest moved verbatim into docs/providers.md, docs/cli.md, docs/scheduling.md and docs/troubleshooting.md. No section was rewritten or dropped and every original heading still exists, so each anchor still resolves somewhere — but bookmarks into moved sections now need the docs/ path, which is the cost this deferral was protecting against. .gitignore needed the fix FUTURE.md predicted: the blanket *.md rule (exported conversations are Markdown and may be private) matched all four new pages, and git check-ignore confirmed every one would have been silently ignored. !docs/*.md is added with a comment saying why. Gitea's rendering of relative docs/ links cannot be checked before pushing, so it still needs confirming after. Also corrects two stale claims in the changelog: the Windows backstop is no longer "not yet run under Windows PowerShell 5.1 or Task Scheduler" (confirmed on winmini today, LastTaskResult 0 — only the FAILED push itself remains unexercised there), and the test count is 414, not 409. 414 tests passing. No code changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012gCWSknsWvhLnMFwa7qnGp
13 KiB
AI Chat Exporter
A personal backup tool for ChatGPT and Claude conversation history. Exports your chats to Markdown files and syncs them to Joplin as notes. Each conversation becomes a single .md file with YAML frontmatter, organised into folders that map directly to Joplin notebooks.
Supports incremental sync — only new or updated conversations are exported on each run. Every run is resumable: if interrupted, re-running picks up exactly where it left off.
Contents
This page — what it is, installing it, the first run, configuration and where files land.
- Terms of Service Warning
- Installation
- First Run: Run Doctor
.envSetup- Output Structure
- Documentation
- Future Work
- Security Notes
Detail lives in docs/:
| Document | Covers |
|---|---|
| docs/providers.md | Session tokens and how to refresh them, the auth wizard, ChatGPT Projects, Claude Code sessions, Codex sessions |
| docs/cli.md | CLI reference — every command and flag |
| docs/scheduling.md | The daily run on Linux and Windows, and push notifications |
| docs/troubleshooting.md | How the cache works, what each failure means, filing a bug report |
Terms of Service Warning
Read this before using this tool.
This tool works by accessing unofficial, undocumented internal web API endpoints used by the ChatGPT and Claude web apps. These endpoints are not publicly supported by OpenAI or Anthropic and are subject to change or removal without notice.
Use of this tool may conflict with their Terms of Service:
- OpenAI: https://openai.com/policies/terms-of-use
- Anthropic: https://www.anthropic.com/legal/consumer-terms
By using this tool, you accept that:
- You are using it entirely at your own risk
- Your account could potentially be suspended for automated or scripted access
- The internal APIs this tool relies on may break at any time without notice
- This tool is for personal archival use only — not commercial use
This tool is designed for a single user backing up their own conversations. Do not use it to scrape data at scale or for any commercial purpose.
Installation
Linux / macOS
git clone <repo-url>
cd AIChatExporter
./ai-chat-exporter doctor
That's the whole install. The ai-chat-exporter wrapper creates .venv and
installs dependencies on first run, and reinstalls whenever pyproject.toml
changes — so there is no python3 -m venv / source .venv/bin/activate to
remember, on this machine or the next one you clone onto. Every command in this
README works the same way:
./ai-chat-exporter export --provider all
./ai-chat-exporter sync
The wrapper does not change directory: .env, cache/ and exports/ all
resolve against your current directory, which is what lets one checkout archive
different machines into different places. Run it from the repo. (It warns if you
don't, because a different working directory means a different cache/manifest.json
— which would re-export everything and orphan your existing Joplin notes.)
Prefer the traditional route, or want the test dependencies? That still works:
python3 -m venv .venv && source .venv/bin/activate && pip install -e ".[dev]"
Windows
No admin access required. Run these in Command Prompt (cmd.exe) — it's the simplest option on Windows because it doesn't have PowerShell's script execution policy restrictions.
git clone <repo-url>
cd AIChatExporter
ai-chat-exporter doctor
ai-chat-exporter.cmd does the same bootstrap as the POSIX wrapper — it creates
.venv and installs dependencies on first run. No python -m venv, no
.venv\Scripts\activate.
How you invoke it differs between the two Windows shells:
| Shell | Command |
|---|---|
Command Prompt (cmd.exe) |
ai-chat-exporter export --provider all |
| PowerShell | .\ai-chat-exporter.cmd export --provider all |
cmd.exe searches the current directory before PATH and resolves the bare name
through PATHEXT, so it finds ai-chat-exporter.cmd with no prefix and no
extension. PowerShell deliberately does not search the current directory, and
.\ai-chat-exporter there would resolve to the extensionless POSIX script, which
PowerShell cannot execute — so name the .cmd explicitly. Command Prompt is the
simpler of the two here.
All ai-chat-exporter commands work identically in Command Prompt.
Using PowerShell instead? If you prefer PowerShell, you may need to allow script execution first (one-time, current user only):
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Then activate the venv and run commands the same way.
Prerequisites:
- Python 3.11 or later — install from python.org. During installation, tick "Add Python to PATH".
- Git — install from git-scm.com if not already present.
Notes:
- The cache manifest and logs are stored in
cache\inside the install directory — the same as on Linux. - File permission hardening (
chmod 600) is silently ignored on Windows — not a concern for single-user desktop use. - Joplin Web Clipper runs on
localhost:41184on all platforms; no configuration changes needed.
First Run: Run Doctor
Before anything else, validate your setup:
ai-chat-exporter doctor
This checks token presence, format, token health (via the /api/auth/session error field), directory permissions, disk space, and live API connectivity. Fix any failures before proceeding.
Checking for API drift
The exporter reads ChatGPT's and Claude's undocumented internal web APIs, which can change shape without notice. The worst failure for a backup tool is silent — a response change that makes the exporter skip or mis-parse content without erroring. Run the canary to catch that early:
ai-chat-exporter canary
It probes one conversation per provider and checks only the fields the parser depends on (a renamed retrieval-tool author that would bypass the content-collapse, a new content type, drifted message fields, ignored attachments). OK means the live schema still matches; WARN/ERROR tells you exactly what changed. Worth running before a large export or on a schedule.
.env Setup
Copy .env.example to .env and fill in your values:
cp .env.example .env
Provider tokens
| Variable | Description |
|---|---|
CHATGPT_SESSION_TOKEN |
ChatGPT session token chunk .0 (starts with eyJ…) |
CHATGPT_SESSION_TOKEN_1 |
ChatGPT session token chunk .1 (the remainder) |
CHATGPT_PROJECT_IDS |
Comma-separated ChatGPT project IDs (see below) |
CLAUDE_SESSION_KEY |
Your Claude session key |
Output
| Variable | Default | Description |
|---|---|---|
EXPORT_DIR |
./exports |
Where to write exported Markdown files |
OUTPUT_STRUCTURE |
provider/project/year |
Folder structure (see below) |
EXPORTER_HIDDEN_CONTENT |
placeholder |
What to do with content invisible in the provider's web UI (file-retrieval tool dumps, Custom Instructions): placeholder collapses to a one-line note with size, full keeps everything, omit drops it. Dumps can be 90% of a conversation's bytes. |
EXPORTER_DOWNLOAD_MEDIA |
images |
Download conversation assets into a media/ folder beside each export and inline them: images (images only), all (also audio/voice clips), off (text placeholders only). Downloaded media is uploaded to Joplin as resources on the next joplin run. |
MAX_CONVERSATIONS_PER_RUN |
unlimited | Cap downloads per export run (per provider). Runs are resumable, so re-running continues where the cap stopped — useful to spread a large first export across several sessions. |
REQUEST_DELAY |
1.0 |
Seconds between consecutive API requests, with small random jitter, so traffic stays human-paced. Set 0 to disable. |
Joplin
| Variable | Default | Description |
|---|---|---|
JOPLIN_API_TOKEN |
— | Authorization token from Joplin Web Clipper settings |
JOPLIN_API_URL |
http://localhost:41184 |
Joplin API URL (change only if you've customised the port) |
JOPLIN_REQUEST_TIMEOUT |
30 |
Seconds before an API call times out. Increase for very large conversations. |
Notifications
| Variable | Default | Description |
|---|---|---|
NTFY_TOPIC |
— | ntfy topic to push run results to. Unset disables notifications entirely. |
NTFY_SERVER |
https://ntfy.sh |
Point at your own host if self-hosting. |
NTFY_TOKEN |
— | Bearer token, for access-controlled topics. |
NTFY_NOTIFY |
always |
always notifies on every run, failure only when something failed, off never. |
A topic on public ntfy.sh is readable by anyone who knows its name, so notifications carry per-provider counts and a machine name only — never conversation titles. See Getting notified.
Cache & logging
| Variable | Default | Description |
|---|---|---|
CACHE_DIR |
./cache |
Where to store the sync manifest |
LOG_FILE |
./cache/logs/exporter.log |
Log file path (none to disable) |
AI_CHAT_EXPORTER_QUIET_CWD |
— | Set to 1 to silence the launcher's warning when run from outside the repo. Read by the ai-chat-exporter wrapper scripts, not by Python; the scheduler installers set it, since they always set the correct working directory. |
Output Structure
All exported files go under EXPORT_DIR. The folder structure maps directly to Joplin notebooks.
Default: provider/project/year
exports/
├── chatgpt/
│ ├── no-project/
│ │ └── 2024/
│ │ └── 2024-03-15_my-conversation_abc12345.md
│ └── learning-python/
│ └── 2024/
│ └── 2024-03-15_async-tutorial_def67890.md
└── claude/
├── no-project/
│ └── 2024/
│ └── 2024-06-01_docker-explained_ghi11111.md
└── startos-packaging/
└── 2024/
└── 2024-06-10_manifest-setup_jkl22222.md
Joplin Notebook Mapping
Each provider+project combination maps to a flat Joplin notebook created automatically by the joplin command:
| Export folder | Joplin notebook |
|---|---|
exports/chatgpt/learning-python/ |
ChatGPT - Learning Python |
exports/claude/startos-packaging/ |
Claude - Startos Packaging |
exports/chatgpt/no-project/ |
ChatGPT - No Project |
exports/claude/no-project/ |
Claude - No Project |
Other OUTPUT_STRUCTURE options
| Value | Result |
|---|---|
provider/project/year (default) |
exports/claude/my-project/2024/file.md |
provider/project |
exports/claude/my-project/file.md |
provider/year |
exports/claude/2024/file.md (projects ignored) |
Filename format
YYYY-MM-DD_{title-slug}_{id[:8]}.md — e.g. 2024-06-10_manifest-setup_jkl22222.md
Documentation
This page covers installing and configuring the exporter. Everything else is in
docs/:
- Providers and Tokens — getting a session token out of
DevTools, what each one's lifetime is and how expiry is detected, the
authwizard, and the setup each provider needs: ChatGPT Projects (which are not in the default conversation listing), Claude Code sessions, Codex sessions. - CLI Reference —
auth,doctor,export,list,joplin,sync,notify,prune,projects,canary,cache, with the flags and examples for each. - Running on a Schedule — the systemd user timer, the Windows scheduled task, what to know before relying on either, and how run results reach your phone.
- Troubleshooting — how the cache decides what to re-export, every error message the tool produces and what it means, media download failures, and what to include in a bug report.
Future Work
See FUTURE.md for the full roadmap. Current priority:
- A StartOS service that centralises every machine's conversations into one
corpus and owns the Joplin connection, so each machine only has to upload
(
FUTURE.md§1)
Security Notes
- All exported data is stored locally only — nothing is sent anywhere except to your local Joplin instance
- Exported files and the cache manifest are created with
600permissions (owner read/write only) .envis in.gitignore— never commit it- Session tokens are never logged, printed, or included in error messages
- The Joplin API token is only ever sent to
localhost— it never leaves your machine - If you accidentally commit
.env: immediately log out and back in to invalidate the token, then remove it from git history using BFG Repo Cleaner orgit filter-branch