Migrating Claude Projects to Open WebUI: what the export leaves out, and how to rebuild it
Claude's export doesn't link chats to projects. Here's how I migrated two years of Claude history and rebuilt Projects as Open WebUI Knowledge + Models.
A few weeks ago I wrote about keeping your own data — the personal data sovereignty stack, capturing your own activity on hardware you control so your data lands in your infrastructure before it lands in anyone else’s. That post was about the capture layer, building the copy of your data that’s yours first. This one is about the data you’ve already generated somewhere else: two years of Claude history, and the Projects built on top of it, moved onto an Open WebUI instance you own.
Here’s the punchline first. Claude’s data export gives you every chat and every project document, but it does not give you the one thing that ties them together — which chats belonged to which project. And “a Project” isn’t a folder anyway. It’s instructions plus a knowledge base, and in Open WebUI those map to a custom Model plus a Knowledge collection, not to a folder in your sidebar. Get those two facts straight up front and the migration is a couple of hours of mostly-waiting. Miss them and you’ll do what I did first: build the wrong thing, then rebuild it.
This is a solo-dev guide. One account, your own instance, your own hardware. If you’re moving a team, most of this still applies but the folder-permission and RBAC parts change, and that’s a different post.
Step 0: get your data out of Claude
Before anything touches Open WebUI, pull your export. In Claude — desktop app or web — go to Settings → Privacy → Export data and confirm. Claude doesn’t hand you a file on the spot; it emails you a download link when the export is ready. For a large account that can take a little while. Download the archive from that email and unzip it.
What you get:
conversations.json— every chat, every message. This is the big one.projects/— one JSON per project, carrying the project’s instructions and the actual content of its knowledge docs.users.json— your account record.
Two things about what’s preserved are worth knowing before you build on top of this, because they cut in opposite directions. The good news: your project knowledge survives intact. The documents that made up each project — the files you attached to give it context — are sitting right there in that project’s JSON as full content, not summaries. That’s the substance of the project, and it comes across whole. Now the catch. First, chat attachments are a different story from project docs: the export preserves only their extracted text, not the original binaries — if a chat had a PDF or an image, the text Claude pulled out of it is in the export, but the file itself is not. You’re migrating text, not files. Second, and this is the one that shapes the rest of this guide: the project JSON carries a project’s docs, but it does not carry any link to the chats that belonged to that project. The knowledge is there; the connection between a project and its conversations is not. That’s normal for Claude exports, but it’s the kind of thing you want to know now rather than discover three steps in.
Step 1: import the chats into Open WebUI
Open WebUI has native import under Settings → Data Controls → Import Chats. Point it at a JSON file and it restores conversations.
The catch is the format. Open WebUI auto-detects and converts ChatGPT exports — you can hand it the raw file and it figures out the shape. Claude is not on that list. Claude’s export falls under Open WebUI’s “custom JSON” path, which means you have to reshape conversations.json into the structure Open WebUI expects before it’ll take it. The official docs put it flatly: There is no built-in converter for these platforms.
That structure is a JSON array of chat objects, each using a message tree — every message points to its parent by parentId and its children by childrenIds, and a currentId marks the tip of the active branch. This is the part that’s actually pleasant: Claude already stores a parent_message_uuid on every message, which maps directly onto that tree. So the edited-message branches in your Claude history survive the move instead of getting flattened into a single line. The linkage carried over cleanly for me — no dangling parents across 17,000-odd messages.
A few things the conversion should handle, because Open WebUI won’t do them for you: map human → user and assistant → assistant roles, carry the original timestamps (or every chat imports dated “now”), and preserve the thinking blocks and tool calls as inline content so nothing gets silently dropped. Tag every imported chat with something like claude-import so you can find or bulk-manage them later.
Import once. Re-importing the same file makes duplicates — Open WebUI assigns fresh IDs each time, so a second import is a second copy, not an update.
If you want the conversion script I used, email me and I’ll send it over.
Step 2: the thing the export doesn’t tell you — which chat went where
Here’s where the first version went wrong, so you don’t have to repeat it.
In the Claude UI, a project chat shows a breadcrumb: Computer Setup Project → Docker WebUI. That tells you the chat belongs to the project. You’d reasonably assume that relationship is in the export. It isn’t. Every chat object in conversations.json carries uuid, name, summary, timestamps, account, and chat_messages — and nothing that names its project. I confirmed it on a chat I knew was a project chat: zero project fields. Claude reconstructs that breadcrumb server-side from data it doesn’t include in the download.
So if you import flat, everything lands in one undifferentiated list and the project structure is gone. There’s no project_id to sort on. This is a real, current limitation, not something I misconfigured. There’s an open feature request on the Open WebUI repo asking for a native Claude importer precisely because this mapping is missing from the source data — the request frames migrating users as locked in by the history they can’t easily move. A maintainer’s reply is encouraging but non-committal: Would be great as a side project!
Until then, the only turnkey tool is a community importer that writes directly to Open WebUI’s SQLite database — powerful, but riskier than reshaping JSON and importing through the UI.
I recovered the mapping two ways.
Save the project page as HTML. This is the one that actually works, and it’s dead simple. Open each project in Claude, and use your browser’s Save Page As → Web Page, Complete. The saved HTML lists that project’s chats as title + /chat/{uuid} links — which is exactly the project-to-chat mapping the JSON omits. Feed those saved pages back in, pull the UUIDs out, and cross-reference them against conversations.json. For my most active projects this recovered membership exactly: 167 of 168 chats matched, and the single miss was a chat created after the export’s cutoff — not in the data yet, not a failure of the method.
Content-match the rest. For smaller projects I didn’t bother saving, I matched the leftover unmapped chats by content. Each project’s JSON carries its name, description, and creation date, so I looked at unmapped chats created in the window around each project’s creation date and scored their titles and summaries against the project’s topic. Some of those are unmistakable — “Agentic AI Workflow Project Plan” clearly belongs to the “Agentic AI Project Plan” project. Plenty are not, and this is where you have to be honest with yourself: a date window pulls in every standalone chat from that week, most of which have nothing to do with the project. I kept only the high-confidence topical hits and left the ambiguous ones untagged rather than guessing. Guessing wrong is worse than leaving a chat in the flat list.
The takeaway: the HTML save is reliable, content-matching is a best-effort supplement, and for the projects that matter, spend the two minutes to save the page. It beats any heuristic I could write.
Step 3: stop thinking in folders
This is the correction that reframes the whole job, and it’s worth stating plainly because it’s the mistake I made: I spent real effort organizing the imported chats into folders, one per project, and folders are not what a Claude Project is.
A Claude Project is two things — the instructions that condition every chat in it, and the knowledge it can draw on. The chats are downstream of that. They’re the byproduct, not the project. Organizing them into folders gives you a tidy archive of past conversations and nothing else: no instructions, no retrieval, none of the thing that made the project useful.
Open WebUI’s own docs point you here without quite saying it. Their “self-hosted Claude alternative” page lists Projects as a reason to keep using claude.ai, and lists Knowledge and custom Models as the Open WebUI features for persistent context. Those last two are the project. You just have to assemble them yourself.
So: skip folders for this. Build Knowledge collections and custom Models instead.
Step 4: rebuild each project as an Open WebUI Knowledge base and custom Model
This is the part that recreates the actual capability. Two pieces per project.
The Knowledge collection holds the docs. In Open WebUI, go to Workspace → Knowledge → + New Knowledge, name it after the project, and upload that project’s documents. The content is already in your export — each projects/*.json has a docs array with the real text of every file the project carried. Open WebUI chunks and embeds each doc so the model can retrieve against it at chat time. Embedding takes a moment per document; larger collections take longer. Let it finish before you judge whether retrieval works.
The custom Model holds the instructions and binds the knowledge. Go to Workspace → Models, create a new model on top of whatever base model you run, and do two things: paste the project’s instructions into the System Prompt field (they’re in the project JSON, in the prompt_template), and attach the Knowledge collection you just built. Save.
That’s the whole equivalence:
| Claude Project | Open WebUI |
|---|---|
| Project instructions | Model system prompt |
| Attached files / docs | Knowledge collection (chunked + embedded for RAG) |
| “Being in the project” | Selecting that custom model |
| The chats | Conversations with that model (already archived in Step 1) |
Now, selecting that model in a new chat is being in the project. It has the instructions and it retrieves from the docs. If you’d rather not make a full model for a one-off, you can also pull a collection into any chat inline by typing # and the collection name — but for something you’ll use repeatedly, the custom model is the real replacement.
One verification worth doing before you trust it: ask the new model something answerable only from a doc, not from general knowledge. If it answers from the document, retrieval is wired correctly end to end. If it says it doesn’t know, the knowledge isn’t reaching the model — and the likeliest reason isn’t a bad upload. In current Open WebUI, native function calling is the default, and with it attached knowledge is not auto-injected: the model has to call a knowledge tool to retrieve, and a weaker model sometimes just doesn’t. If that’s happening, you have three fixes — add a line to the system prompt telling it to search its knowledge, turn native function calling off for that model to restore automatic RAG injection, or switch the attachment to Full Context so the docs are always in the prompt. Worth checking that before you assume the embedding failed.
This kind of migration — getting a team or an individual off a managed AI tool and onto infrastructure they control, without losing the context they’ve built — is a chunk of what I do as a fractional CTO. If you’re partway through one and the structure isn’t mapping cleanly, reach out.
What you actually end up with
A local Open WebUI instance holding your full Claude history as importable, searchable chats, and your projects rebuilt as selectable models — each one carrying its own instructions and grounded on its own documents. Pick a project’s model, start a chat, and you’re working the way you were in Claude, except the whole thing runs on hardware you own.
It is not a perfect one-to-one copy, and I’d rather be straight about the gaps than pretend the export is cleaner than it is. Original attachment binaries don’t come across — only their extracted text. The project-to-chat mapping has to be rebuilt by hand, and for projects you don’t save as HTML, some chats will stay in the flat list. Anything created after your export’s cutoff simply isn’t in the data. None of that is fatal; all of it is worth knowing before you start, because knowing it up front is the difference between a clean afternoon and a rebuild.
The reason to do this at all is the same reason I run the rest of my stack locally. The context you’ve spent two years building — the instructions, the reference docs, the history — is yours, and it can live somewhere you control instead of somewhere you rent. Claude’s export, gaps and all, is enough to get it there.
If you’re moving off a managed AI tool and want the migration mapped out before you commit to it — what transfers cleanly, what has to be rebuilt, what’s genuinely lost — that’s the kind of thing I help work through. And if you’re a company with years of accumulated context sitting inside someone else’s product, that’s the same problem at a different scale. The value of owning your own data and context doesn’t shrink when there’s more of it. It grows.
FAQ
Does Open WebUI import Claude exports directly?
Not automatically. Open WebUI auto-detects and converts ChatGPT exports, but Claude’s export isn’t recognized — it goes through the “custom JSON” path, so you have to reshape conversations.json into Open WebUI’s message-tree format first. The good news is Claude’s export already stores parent-message references, which map cleanly onto that tree, so the conversion preserves branching instead of flattening it.
How do I export my data from Claude? In the Claude desktop app or web, go to Settings → Privacy → Export data and confirm. Claude emails you a download link when the export is ready rather than giving you the file immediately; for a large account it can take a while. Download and unzip the archive from that email.
Why don’t my imported chats keep their projects?
Because the project-to-chat link isn’t in the export. Claude’s conversations.json stores no project field on chats — the breadcrumb you see in the Claude UI is reconstructed server-side. To rebuild the mapping, save each project’s page as HTML (which lists its chats’ UUIDs) and cross-reference those against the export.
What’s the Open WebUI equivalent of a Claude Project? A custom Model plus a Knowledge collection. The project’s instructions become the model’s system prompt; the project’s documents become a Knowledge collection attached to that model. Selecting the model is the equivalent of “being in the project.” Folders are not the equivalent — they only organize chat history, not instructions or retrieval.
Do my uploaded files and images come across? Only their extracted text. Claude’s export preserves the text content it pulled from attachments, not the original PDFs or images. If you need the original files, keep the source copies — they’re not in the export bundle.
What about chats created after I exported?
They’re not in the data, full stop. If a chat was created after your export’s cutoff, it won’t be in conversations.json and there’s no way to recover it from that export. If it matters, run a fresh export once those chats exist.
Can I do this without writing any code? Partly. Building Knowledge collections and custom Models is entirely UI-driven in Open WebUI — no code needed. The chat import is the part that needs a conversion step to reshape Claude’s JSON, since Open WebUI won’t auto-convert it. If you want the script I used for that, email me.