Cloud sync API
Carrot Notes can sync to your own HTTP server. There is no official Carrot cloud — you host the endpoint and store the backup JSON yourself.
Local folder sync writes the same note JSON to carrotnotes_backup.json plus .md files. Cloud sync sends only the JSON array.
Overview
| Operation | Method | URL | Body |
|---|---|---|---|
| Sync (push) | POST | {endpoint} | Notes JSON array |
| Fetch backup | GET | {endpoint} | — |
| Clean trash | DELETE | {endpoint}/deleted | — |
| Remove all | POST | {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: trueare 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
| Field | Type | Description |
|---|---|---|
id | string | Unique ID (e.g. note_1718891234_abc123) |
title | string | Display title |
content | string | Note body as Markdown |
theme | string | Color theme ID (e.g. theme-orange) |
isTemporary | boolean | If true, excluded from sync |
isOpen | boolean | Whether the note window is open |
pinned | boolean | Pinned in tray / dashboard |
alwaysOnTop | boolean | Window always-on-top |
readOnly | boolean | Per-note lock |
fontFamily | string | e.g. Caveat, Inter |
fontSize | string | e.g. 20px |
width | number | Window width (px) |
height | number | Window height (px) |
x | number | Window X position |
y | number | Window Y position |
rotation | number / string | Card 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)
| Mode | Behavior |
|---|---|
| Manual | Sync only when you click Sync Cloud Now |
| On save | Sync after each note save |
| Scheduled | Sync 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