Bitwarden's five export files

Leaving Bitwarden? Skip the account-restricted JSON. Only the Bitwarden account that made it can decrypt it.

Checked against Bitwarden web vault Sources: bitwarden/clients, bitwarden/sdk-internal

The export dialog offers four file formats. The encrypted JSON comes in two kinds, password-protected and account-restricted, so there are five different files you can end up with. Which one you pick decides whether your attachments, cards and second factors come with you. We exported one vault in every form Buddy can open.

Form File Encrypted
Plaintext JSON Highest fidelity of the five. The reference form the others are measured against. bitwarden_export_*.json No
Password-protected JSON Decrypts to exactly the plaintext form, so nothing downstream needs to know it was encrypted. bitwarden_encrypted_export_*.json Yes
Account-restricted JSON Shares a filename prefix with the password-protected form. Told apart by the passwordProtected key. Buddy cannot read this file. The file carries no salt, kdfType or kdfIterations, because it was never re-encrypted for export. It is a verbatim dump of ciphertext held under a server-side account key, so opening it means authenticating to Bitwarden. bitwarden_encrypted_export_*.json Yes
CSV Cards and identities never reach the file. This is Bitwarden's own limitation, not an importer one. bitwarden_export_*.csv No
ZIP data.json plus an attachments directory. The only form that carries files. Always plaintext; there is no encrypted zip form. bitwarden_export_*.zip No
What survives Plaintext JSON bitwarden_export_*.json Password-protected JSON bitwarden_encrypted_export_*.json CSV bitwarden_export_*.csv ZIP bitwarden_export_*.zip
Logins
Plaintext JSON Logins: Fully supported.
Password-protected JSON Logins: Fully supported.
CSV Logins: Fully supported.
ZIP Logins: Fully supported.
TOTP secrets
Plaintext JSON TOTP secrets: Fully supported.
Password-protected JSON TOTP secrets: Fully supported.
CSV TOTP secrets: Fully supported.
ZIP TOTP secrets: Fully supported.
Attachments
Plaintext JSON Attachments: N/A.
Password-protected JSON Attachments: N/A.
CSV Attachments: N/A.
ZIP Attachments: Fully supported.
Custom fields
Plaintext JSON Custom fields: Fully supported.
Password-protected JSON Custom fields: Fully supported.
CSV Custom fields: Limited.
ZIP Custom fields: Fully supported.
Folders
Plaintext JSON Folders: Fully supported.
Password-protected JSON Folders: Fully supported.
CSV Folders: Fully supported.
ZIP Folders: Fully supported.
Payment cards
Plaintext JSON Payment cards: Fully supported.
Password-protected JSON Payment cards: Fully supported.
CSV Payment cards: N/A.
ZIP Payment cards: Fully supported.
Password history
Plaintext JSON Password history: Fully supported.
Password-protected JSON Password history: Fully supported.
CSV Password history: N/A.
ZIP Password history: Fully supported.
Passkeys
Plaintext JSON Passkeys: Not yet supported.
Password-protected JSON Passkeys: Not yet supported.
CSV Passkeys: N/A.
ZIP Passkeys: Not yet supported.
Fully supported
Limited
Not yet supported
N/A

For a move, the plaintext JSON is enough, or the ZIP if you have attachments. Use the password-protected JSON if the file will sit somewhere you do not control.

How to export

In the web vault, open Tools in the left sidebar, then Export. Pick a file format and choose Export. You will be asked for your master password.

.json
.csv
.json (Encrypted)
.zip (with attachments)

This exports your individual vault only. Items that belong to an organization are not in the file; an organization owner or admin exports those separately from the Admin Console.

Only .json (Encrypted) produces an encrypted file.

JSON format

This is the highest-fidelity form, and the reference the other four are measured against. The ZIP's data.json is byte-identical to it for the same vault, and the password-protected form is this document encrypted.

Document layout

A plaintext export is one object. Folders are listed once, and items point at them by id.

{
  "encrypted": false,
  "folders": [
    { "id": "11111111-1111-1111-1111-111111111111", "name": "Work/Dev" }
  ],
  "items": [ ... ]
}

Item types

Every item has a numeric type, and one object named after that type holding its own fields:

1  Login        login
2  Secure note  secureNote
3  Card         card
4  Identity     identity
5  SSH key      sshKey

A login:

{
  "id": "aaaaaaaa-0000-0000-0000-000000000001",
  "folderId": "11111111-1111-1111-1111-111111111111",
  "type": 1,
  "reprompt": 0,
  "name": "GitHub",
  "notes": "Personal account",
  "favorite": true,
  "fields": [],
  "login": {
    "uris": [
      { "match": null, "uri": "https://github.com/login" }
    ],
    "username": "octocat",
    "password": "correct-horse-battery",
    "totp": "otpauth://totp/GitHub:octocat?secret=JBSWY3DPEHPK3PXP&issuer=GitHub"
  },
  "passwordHistory": [
    { "lastUsedDate": "2026-03-01T10:00:00.000Z", "password": "previous-password" }
  ]
}

A secure note is only its name and notes. Its secureNote object holds a single type, which is always 0. Cards import into card fields and identities into custom fields. SSH key items import with their name, notes and custom fields, but not the key.

Custom fields

Each entry in fields has a numeric type of its own:

"fields": [
  { "name": "Employee ID",   "value": "12345",          "type": 0 },
  { "name": "Recovery code", "value": "X7K2-9QPL-M4TR", "type": 1 },
  { "name": "Admin",         "value": "true",           "type": 2 },
  { "name": "Login alias",   "type": 3, "linkedId": 100 }
]
  • 0 text imports as a text field.
  • 1 hidden imports as a secret field.
  • 2 boolean is the string true or false, and imports as text.
  • 3 linked has no value. It points at one of the item's own fields through linkedId, here 100 for the login's username, and is skipped.

URI match

match on each URI is its autofill rule, or null when the item follows the account default:

0  Base domain   3  Exact
1  Host          4  Regular expression
2  Starts with   5  Never

Buddy does not read it.

Folders

There is no parent id anywhere in a Bitwarden export. A nested folder is literally named parent/child, and the interface renders the slash as a tree. Personal exports use folders and organization exports use collections, but both are just a map of id to name.

The practical consequence for anything converting these to tags is that only the slash separates. Folder names contain spaces all the time, so a splitter that treats whitespace as a separator turns one folder called "child folder" into two unrelated tags.

An item in several collections keeps the first one as a tag. The rest are not recorded.

TOTP

Bitwarden does not validate what goes in the TOTP field. Real exports hold full otpauth:// URIs, bare base32 seeds, and outright junk, sometimes all three in one vault.

Keep anything you cannot parse. A lost second factor looks fine until the day you are locked out of the account. Buddy imports an unreadable value as a secret field for you to sort out by hand.

Passkeys

A login's passkeys are written into login.fido2Credentials, private key included, in the plaintext JSON, the password-protected JSON and the ZIP. The CSV has no column for them. That makes a plaintext JSON export more sensitive than it looks: anyone holding the file can sign in with those passkeys, not only read your passwords.

Encrypted JSON formats

Two of the forms are encrypted, they share a filename prefix, and they look nearly identical. They are told apart by a single key in the file, passwordProtected.

The account-restricted form was never re-encrypted for export. It is a verbatim dump of the ciphertext already sitting in your synced vault, encrypted under an account key that Bitwarden holds server side. There is nothing in the file to derive a key from: no salt, no kdfType, no kdfIterations. Opening it means authenticating to Bitwarden.

Two details in the file confirm it is raw synced state, not something generated for the export. Each item carries its own wrapped key, and each saved URI carries a uriChecksum. That checksum exists so a compromised Bitwarden server cannot swap a saved URI and trick autofill into posting your credentials somewhere else. It is a defense that only makes sense inside the sync relationship, so finding it in a file tells you the file is a copy of your synced vault.

Bitwarden's own interface calls this “account restricted” and warns that it can only be imported back into the same account. Nothing outside Bitwarden can read it. No importer is missing a feature here; the format is built that way. Re-export as password-protected instead.

ZIP format

The ZIP export is data.json plus an attachments/ directory, and it is always plaintext, with no encrypted option. It is also the only Bitwarden export that carries the attached files themselves.

The attachment paths carry neither the item id nor the attachment id:

attachments/<sanitized item name>[_N]/<sanitized filename stem>[_N].<ext>

Files are matched back to items by reproducing Bitwarden's own sanitizing and de-duplication rules, which means the order items are walked in is load-bearing. Two items with the same name, or two attachments with the same filename, are told apart only by a positional counter.

CSV format

Cards and identities have no rows in the CSV at all. Every item of those two types is absent from the file, and nothing in the file records that they were left out.

Also gone: attachments, password history, and the distinction between a hidden custom field and a plain text one, since every custom field flattens to a name: value line.

Column layouts

Bitwarden added an archivedDate column as the eighth column, which pushed every login column one position to the right.

folder,favorite,type,name,notes,fields,reprompt,archivedDate,
  login_uri,login_username,login_password,login_totp        (12 columns, current)

folder,favorite,type,name,notes,fields,reprompt,
  login_uri,login_username,login_password,login_totp        (11 columns, legacy)

Nothing errors when this goes wrong. Anything reading these files by column position rather than by header will read archivedDate as the URL, the URL as the username, and the username as the password, on every row, without raising an error. If you have imported a Bitwarden CSV somewhere and the fields look shifted, this is why.

Buddy field mapping

JSON and ZIP

The plaintext JSON, the password-protected JSON once decrypted, and the ZIP's data.json share one layout.

Source field Serialized Target field Notes
name Always Title
login.username Conditional Username
login.password Conditional Password
login.uris[] Conditional URL First URI becomes the entry URL, the rest become custom URL fields.
login.totp Conditional TOTP Unparseable values land as a secret custom field.
notes Conditional Note
folders[].name Conditional Tags (derived) Split on slash only. Folder names freely contain spaces, so a generic space splitter would turn one folder into two tags.
fields[] Conditional Custom fields Hidden fields import as secrets and booleans as text. Linked fields are skipped.
card.expMonth Conditional Expiry (derived) Month is unpadded, year is sometimes 4 digits and sometimes 2.
identity.title Conditional Prefix An honorific (Mr), not the item's name.
reprompt Always Dropped Requires-master-password has no equivalent concept.
favorite Always Dropped
archivedDate Conditional Tags (derived) Archived is not deleted, so the item imports live carrying an archived tag.
passwordHistory[] Conditional Password history Newest first, and lastUsedDate is the moment that password stopped being current, which is the same thing our own history records. An unparseable date keeps the value without one rather than dropping an old password.
login.fido2Credentials Conditional Dropped The login's passkeys, private key included. Not imported until Buddy can store passkeys.
login.uris[].uriChecksum Conditional Dropped A MAC'd hash that defends the sync path against a compromised server. Its presence in an export is an artifact.
login.uris[].match Conditional Dropped The autofill match rule.
sshKey Conditional Dropped The item imports with its name, notes and custom fields, but not the key.

CSV

Source field Target field Notes
folder Tags (derived) Split on slash only.
favorite Dropped
type Dropped
name Title
notes Note
fields Custom fields (derived) One name: value per line. Hidden fields import as plain text. Multi-line values keep only their first line.
reprompt Dropped
archivedDate Tags (derived) Any value marks the item archived. Absent from the legacy header.
login_uri URL Comma-separated. The first becomes the entry URL, the rest become custom URL fields.
login_username Username
login_password Password
login_totp TOTP Unparseable values land as a secret custom field.

Every CSV export loses folders and metadata to some degree, and that is survivable. The fields that have to come through are username, password, URI, TOTP and note. Check those after any migration, from any manager, into anything.

Buddy is a desktop password manager for macOS and Windows that imports these files. If your export looks different from what this page describes, tell us. Other managers are on the export formats page.