Overview

OperationMethodURLBody
Sync (push)POST{endpoint}Notes JSON array
Fetch backupGET{endpoint}
Clean trashDELETE{endpoint}/deleted
Remove allPOST{endpoint}[]

All requests use Content-Type: application/json where a body is sent.

Authentication

If the app has a token configured, every request includes:

Authorization: Bearer <token>

Use a strong token in production. The app stores it in local settings.

Sync — POST {endpoint}

Pushes the current note list to your server.

  • Permanent notes only — entries with isTemporary: true are stripped before upload.
  • The body is a JSON array of note objects (not wrapped).
  • 2xx — success. Non-2xx — sync fails; body text is shown in the app.
POST https://your-server.example.com/carrotnotes HTTP/1.1
Authorization: Bearer your-secret-token
Content-Type: application/json

[{ ...note objects... }]

Example server (Node.js / Express)

const express = require('express');
const fs = require('fs');
const app = express();

const TOKEN = process.env.CARROT_TOKEN || 'change-me';
const DATA_FILE = './carrotnotes_backup.json';

app.use(express.json({ limit: '10mb' }));

function auth(req, res, next) {
  const header = req.headers.authorization || '';
  const token = header.startsWith('Bearer ') ? header.slice(7) : '';
  if (token !== TOKEN) return res.status(401).json({ error: 'Unauthorized' });
  next();
}

app.get('/carrotnotes', auth, (req, res) => {
  if (!fs.existsSync(DATA_FILE)) return res.json([]);
  res.type('json').send(fs.readFileSync(DATA_FILE, 'utf8'));
});

app.post('/carrotnotes', auth, (req, res) => {
  fs.writeFileSync(DATA_FILE, JSON.stringify(req.body, null, 2));
  res.json({ ok: true });
});

app.delete('/carrotnotes/deleted', auth, (req, res) => {
  res.json({ ok: true });
});

app.listen(3000, () => console.log('Carrot sync on :3000'));

Configure in Carrot Notes: Sync & Cloud → Cloud Server Sync. Set endpoint to https://your-server.example.com/carrotnotes and token to match CARROT_TOKEN.

Fetch backup — GET {endpoint}

Used when Find Restorable Notes runs with sync source Cloud Server. Returns a JSON array of note objects; empty backup is [].

Clean trash — DELETE {endpoint}/deleted

Triggered from Danger Zone → Cloud Server → Clean Trash. Unlike local sync, cloud sync does not automatically maintain a deleted/ archive on the server — implement this route only if you store deleted note archives server-side.

Remove everything — POST with []

Triggered from Danger Zone → Cloud Server → Remove Everything. Your server should replace the stored backup with an empty array.

Note object schema

FieldTypeDescription
idstringUnique ID (e.g. note_1718891234_abc123)
titlestringDisplay title
contentstringNote body as Markdown
themestringColor theme ID (e.g. theme-orange)
isTemporarybooleanIf true, excluded from sync
isOpenbooleanWhether the note window is open
pinnedbooleanPinned in tray / dashboard
alwaysOnTopbooleanWindow always-on-top
readOnlybooleanPer-note lock
fontFamilystringe.g. Caveat, Inter
fontSizestringe.g. 20px
widthnumberWindow width (px)
heightnumberWindow height (px)
xnumberWindow X position
ynumberWindow Y position
rotationnumber / stringCard rotation for preview

Additional fields are preserved if present but are not required for sync.

{
  "id": "note_1718891234567_x9k2m",
  "title": "Shopping list",
  "content": "- [ ] Milk\n- [x] Bread",
  "theme": "theme-yellow",
  "isTemporary": false,
  "isOpen": false,
  "pinned": true,
  "alwaysOnTop": false,
  "readOnly": false,
  "fontFamily": "Caveat",
  "fontSize": "20px",
  "width": 280,
  "height": 300,
  "x": 120,
  "y": 80,
  "rotation": "0.5"
}

Sync modes (in the app)

ModeBehavior
ManualSync only when you click Sync Cloud Now
On saveSync after each note save
ScheduledSync every N seconds (minimum 5)

These modes control when the app calls POST. Your server always receives the full current list (last-write-wins).

Conflict handling

There is no merge or conflict resolution — each sync replaces the remote backup with local non-temporary notes. Restore pulls a note from the last synced backup if you deleted or changed it locally and have not synced since. Treat the server as a backup snapshot, not a real-time collaborative store.

Security recommendations

  • Always use HTTPS in production.
  • Use a strong Bearer token.
  • Validate that the POST body is a JSON array before writing.
  • Rate-limit your endpoint if exposed to the internet.
  • Do not expose the endpoint without authentication.

Local folder sync (reference)

sync-folder/
├── carrotnotes_backup.json    # Same JSON array as cloud POST body
├── My Note {id}.md            # One markdown file per note
└── deleted/                   # Archived notes after delete + sync
    ├── {id}.json
    └── ...

Source

Full upstream documentation: docs/CLOUD-SYNC.md on GitHub

Install guide