Exports
Exports Endpoints
Section titled “Exports Endpoints”Export endpoints manage encrypted bookmark backups stored in R2.
List Exports
Section titled “List Exports”GET /api/v1/exports
Returns all exports for the authenticated user.
Authentication
Section titled “Authentication”Required (Cookie)
curl https://app.bitmarks.sh/api/v1/exports \ -b "bitmarks_session=YOUR_TOKEN"const response = await fetch('https://app.bitmarks.sh/api/v1/exports', { credentials: 'include'});const { exports } = await response.json();Response
Section titled “Response”Status: 200 OK
{ "exports": [ { "id": "exp_abc123", "created_at": 1735430400000, "status": "completed", "size_bytes": 1048576, "bookmark_count": 500, "expires_at": 1736035200000 } ]}Export Object Fields
Section titled “Export Object Fields”| Field | Type | Description |
|---|---|---|
id | string | Unique export identifier |
created_at | integer | Creation timestamp (Unix ms) |
status | string | pending, processing, completed, failed |
size_bytes | integer | Size of export file in bytes |
bookmark_count | integer | Number of bookmarks in export |
expires_at | integer | When download link expires (Unix ms) |
Create Export
Section titled “Create Export”POST /api/v1/exports
Creates a new encrypted export of all bookmarks.
Authentication
Section titled “Authentication”Required (Cookie)
curl -X POST https://app.bitmarks.sh/api/v1/exports \ -b "bitmarks_session=YOUR_TOKEN"const response = await fetch('https://app.bitmarks.sh/api/v1/exports', { method: 'POST', credentials: 'include'});const exportInfo = await response.json();Response
Section titled “Response”Status: 201 Created
{ "id": "exp_new123", "created_at": 1735430400000, "status": "processing", "size_bytes": null, "bookmark_count": null, "expires_at": null}Export Process
Section titled “Export Process”- pending - Export request received
- processing - Collecting and encrypting bookmarks
- completed - Ready for download
- failed - Error during processing
Download Export
Section titled “Download Export”GET /api/v1/exports/:id
Downloads a specific export file.
Authentication
Section titled “Authentication”Required (Cookie)
Path Parameters
Section titled “Path Parameters”| Param | Type | Description |
|---|---|---|
id | string | Export ID |
curl https://app.bitmarks.sh/api/v1/exports/exp_abc123 \ -b "bitmarks_session=YOUR_TOKEN" \ -o backup.datconst response = await fetch('https://app.bitmarks.sh/api/v1/exports/exp_abc123', { credentials: 'include'});const blob = await response.blob();
// Trigger downloadconst url = URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = 'bitmarks-backup.dat';a.click();Response
Section titled “Response”Status: 200 OK
Content-Type: application/octet-streamContent-Disposition: attachment; filename="bitmarks-export-2025-01-15.dat"Binary encrypted data.
Error Responses
Section titled “Error Responses”Status: 404 Not Found
{ "error": "Export not found"}Status: 400 Bad Request (if export not completed)
{ "error": "Export not ready for download"}Delete Export
Section titled “Delete Export”DELETE /api/v1/exports/:id
Deletes an export from R2 storage.
Authentication
Section titled “Authentication”Required (Cookie)
Path Parameters
Section titled “Path Parameters”| Param | Type | Description |
|---|---|---|
id | string | Export ID |
curl -X DELETE https://app.bitmarks.sh/api/v1/exports/exp_abc123 \ -b "bitmarks_session=YOUR_TOKEN"const response = await fetch('https://app.bitmarks.sh/api/v1/exports/exp_abc123', { method: 'DELETE', credentials: 'include'});Response
Section titled “Response”Status: 200 OK
{ "success": true}Error Response
Section titled “Error Response”Status: 404 Not Found
{ "error": "Export not found"}Working with Exports
Section titled “Working with Exports”Creating a Backup
Section titled “Creating a Backup”async function createBackup() { // 1. Create export const createResponse = await fetch('/api/v1/exports', { method: 'POST', credentials: 'include' }); const { id } = await createResponse.json();
// 2. Poll for completion let status = 'processing'; while (status === 'processing' || status === 'pending') { await new Promise(resolve => setTimeout(resolve, 1000));
const listResponse = await fetch('/api/v1/exports', { credentials: 'include' }); const { exports } = await listResponse.json(); const exportInfo = exports.find(e => e.id === id); status = exportInfo.status; }
if (status === 'completed') { // 3. Download const downloadResponse = await fetch(`/api/v1/exports/${id}`, { credentials: 'include' }); return downloadResponse.blob(); } else { throw new Error('Export failed'); }}Restoring from Backup
Section titled “Restoring from Backup”async function restoreBackup(blob, encryptionKey) { // 1. Read encrypted data const arrayBuffer = await blob.arrayBuffer(); const encryptedData = new Uint8Array(arrayBuffer);
// 2. Decrypt const { downloadEncrypted } = await import('@bitmarks.sh/core'); const decrypted = await decrypt(encryptedData, encryptionKey);
// 3. Parse bookmarks const bookmarks = JSON.parse(new TextDecoder().decode(decrypted));
// 4. Import via sync/import endpoint const importResponse = await fetch('/api/v1/sync/import', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ bookmarks }) });
return importResponse.json();}Export File Format
Section titled “Export File Format”Structure (after decryption)
Section titled “Structure (after decryption)”{ "version": "1.0", "exported_at": "2025-01-15T12:00:00.000Z", "bookmark_count": 500, "bookmarks": [ { "id": "bm_abc123", "title": "Example", "url": "https://example.com", "source": "bookmarks", "dateAdded": "2024-01-01T00:00:00.000Z", "path": "Bookmarks/Work" } ]}Encryption Details
Section titled “Encryption Details”- Algorithm: XChaCha20-Poly1305
- Key: User’s encryption key
- Storage: R2 with Server-Side Encryption with Customer-Provided Keys (SSEC)