Overview
The Sync endpoint is the primary method for retrieving all vault data in a single request. It returns:
- User profile information
- All ciphers (vault items)
- Folders
- Collections
- Organizations
- Policies
- Sends
- Encryption keys
Clients should call this endpoint periodically to keep vault data synchronized. Use the revision date to detect changes.
Sync Vault Data
Retrieve all vault data for the authenticated user.
GET /sync?excludeDomains={excludeDomains}
Exclude equivalent domain data from response to reduce payload size
curl -X GET "https://api.bitwarden.com/sync" \
-H "Authorization: Bearer {access_token}"
Response Structure
User Profile
User’s organization memberships
User’s provider memberships
Vault Data
User’s folders with data array
Organization collections accessible to user
All vault items (passwords, notes, cards, identities)
Organization Data
Organization policies that apply to user
Encryption
Equivalent domain data (excluded if excludeDomains=true)
Each data section follows this structure:
{
"object": "list",
"data": [
// Array of items
],
"continuationToken": null
}
Full Response Example
{
"profile": {
"id": "user-guid",
"name": "John Doe",
"email": "[email protected]",
"emailVerified": true,
"premium": true,
"twoFactorEnabled": true,
"key": "encrypted-key-data",
"privateKey": "encrypted-private-key",
"securityStamp": "stamp-value",
"organizations": [
{
"id": "org-guid",
"name": "My Organization",
"useGroups": true,
"useDirectory": true,
"useTotp": true,
"seats": 10,
"type": 0,
"status": 2,
"permissions": {}
}
]
},
"folders": {
"object": "list",
"data": [
{
"id": "folder-guid",
"name": "2.encrypted_name",
"revisionDate": "2024-01-01T00:00:00Z"
}
]
},
"collections": {
"object": "list",
"data": [
{
"id": "collection-guid",
"organizationId": "org-guid",
"name": "2.encrypted_name",
"externalId": null,
"readOnly": false,
"hidePasswords": false,
"manage": false
}
]
},
"ciphers": {
"object": "list",
"data": [
{
"id": "cipher-guid",
"type": 1,
"name": "2.encrypted_name",
"notes": "2.encrypted_notes",
"login": {
"username": "2.encrypted_username",
"password": "2.encrypted_password",
"totp": "2.encrypted_totp",
"uris": [
{"uri": "2.encrypted_uri"}
]
},
"favorite": false,
"organizationId": null,
"collectionIds": [],
"revisionDate": "2024-01-01T00:00:00Z"
}
]
},
"sends": {
"object": "list",
"data": []
},
"policies": {
"object": "list",
"data": []
},
"domains": {
"equivalentDomains": [],
"globalEquivalentDomains": []
}
}
Sync Strategy
Full Sync
Perform a full sync when:
- User logs in
- App starts after being closed
- User manually triggers refresh
Incremental Sync
Use revision dates to detect changes:
- Store last sync
revisionDate locally
- Call
GET /accounts/revision-date to check for updates
- If revision date changed, perform full sync
- Update stored revision date
const lastSync = localStorage.getItem('lastSyncDate');
const revisionDate = await fetch('/accounts/revision-date', {
headers: { 'Authorization': `Bearer ${token}` }
}).then(r => r.json());
if (revisionDate > lastSync) {
// Perform full sync
const syncData = await fetch('/sync');
localStorage.setItem('lastSyncDate', revisionDate);
}
Background Sync
Recommended intervals:
- Active use: Every 5-10 minutes
- Background: Every 30 minutes
- On network change: Immediate sync
Reduce Payload Size
- Use
excludeDomains=true if you don’t need equivalent domains
- Filter SSH keys on older clients using client version checks
- Compress responses with gzip/brotli encoding
Caching Strategy
// Cache sync data with revision tracking
const syncCache = {
data: null,
revisionDate: null,
async fetch(token) {
const currentRevision = await this.getRevisionDate(token);
if (this.data && this.revisionDate === currentRevision) {
return this.data; // Return cached data
}
// Fetch fresh data
this.data = await fetch('/sync', {
headers: { 'Authorization': `Bearer ${token}` }
}).then(r => r.json());
this.revisionDate = currentRevision;
return this.data;
},
async getRevisionDate(token) {
return fetch('/accounts/revision-date', {
headers: { 'Authorization': `Bearer ${token}` }
}).then(r => r.json());
}
};
Error Handling
Access token expired or invalid - trigger re-authentication
User account locked or disabled
Server error - implement exponential backoff retry
Retry Strategy
async function syncWithRetry(token, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fetch('/sync', {
headers: { 'Authorization': `Bearer ${token}` }
}).then(r => r.json());
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
}
}
}
WebSocket Events
For real-time sync, subscribe to WebSocket notifications:
const ws = new WebSocket('wss://notifications.bitwarden.com');
ws.on('message', async (event) => {
const data = JSON.parse(event.data);
if (data.type === 'SyncCipherUpdate') {
// Trigger sync
await performSync();
}
});
WebSocket notifications are recommended for active sessions to receive instant updates when vault data changes.