Krokanti Notes
REST API

Entwickler-API-Referenz

Verwende die REST-API von Krokanti Notes, um Notizen programmgesteuert zu lesen und zu schreiben, Workflows zu automatisieren oder Integrationen zu erstellen. Alle Endpoints akzeptieren und geben JSON zurück.

Basis-URL:https://notes.krokanti.com/api
Ratenlimit:100 req/min per token

Pro-Abonnement erforderlich

Der Zugriff per API-Token erfordert ein aktives Pro-Abonnement. Generiere deinen Token in Einstellungen → Verbindungen nach dem Upgrade.

Auf Pro upgraden →

Authentifizierung

Alle API-Anfragen müssen einen persönlichen API-Token im Header Authorizationenthalten. Tokens generieren in Einstellungen → Verbindungen (Pro-Plan erforderlich). Tokens beginnen mit kn_ und werden nur einmal bei der Erstellung angezeigt — bewahre sie sicher auf.

curl https://notes.krokanti.com/api/notes \
  -H "Authorization: Bearer kn_your_token_here" \
  -H "Content-Type: application/json"
Hinweis: Die Token-Verwaltung (erstellen, auflisten, widerrufen) ist nur über den Browser verfügbar — du kannst Tokens nicht über die API selbst verwalten.

Notizen

GET/api/notes

Listet alle Notizen des authentifizierten Benutzers auf. Unterstützt Filterung, Sortierung und Paginierung.

ParameterTypBeschreibung
searchstringVolltextsuche in Titel und Inhalt
folderIdstringNach Ordner-ID filtern
tagstringNach Tag-Name filtern (exakte Übereinstimmung)
trashedbooleanAuf "true" setzen, um Notizen im Papierkorb aufzulisten
sortByupdatedAt | createdAt | titleSortierfeld (Standard: "updatedAt")
sortOrderasc | descSortierrichtung (Standard: "desc")
limitnumberErgebnisse pro Seite — 1 bis 100 (Standard: 50)
offsetnumberAnzahl der zu überspringenden Ergebnisse (Standard: 0)
  • Antwort: { notes: Note[], hasMore: boolean }
  • Angeheftete Notizen werden immer zuerst zurückgegeben, dann nach dem gewählten Feld sortiert
  • Der Inhalt sicherer Notizen wird als verschlüsselter Blob zurückgegeben (JSON-String beginnend mit {"v":1)
POST/api/notes

Erstellt eine neue leere Notiz. Gibt das erstellte Notizobjekt zurück.

  • Die neue Notiz hat leeren Titel und Inhalt — aktualisiere sie sofort mit PATCH
  • Antwortstatus: 201 Created
GET/api/notes/:id

Ruft eine einzelne Notiz per ID ab.

ParameterTypBeschreibung
iderforderlichstringNotiz-UUID
  • Gibt 404 zurück, wenn die Notiz nicht existiert oder einem anderen Benutzer gehört
  • Der Inhalt sicherer Notizen wird als verschlüsselter Blob zurückgegeben
PATCH/api/notes/:id

Aktualisiert Titel, Inhalt, Tags, Ordner oder Metadaten einer Notiz. Unterstützt Konflikterkennung.

ParameterTypBeschreibung
iderforderlichstringNotiz-UUID (Pfadparameter)
titlestringNeuer Titel
contentstringNeuer HTML-Inhalt
tagsstring[]Ersetzt das vollständige Tags-Array
folderIdstring | nullIn Ordner verschieben (null um aus Ordner zu entfernen)
isPinnedbooleanNotiz anheften oder lösen (nur Eigentümer)
isPublicbooleanNotiz veröffentlichen oder verbergen (nur Eigentümer)
actiontrash | restoreIn Papierkorb verschieben oder wiederherstellen (nur Eigentümer)
clientUpdatedAtstring (ISO 8601)Zuletzt bekanntes updatedAt — für Konflikterkennung verwendet
forcebooleanKonflikterkennung überspringen und Serverstatus überschreiben
  • Gibt 409 mit { conflict: true, serverNote } zurück, wenn clientUpdatedAt älter als die Serverversion ist
  • Sichere Notizen können nicht über die API bearbeitet werden (gibt 403 zurück)
  • Mitarbeiter mit Bearbeitungsberechtigung können nur Titel und Inhalt aktualisieren — Tags und Ordner sind nur für den Eigentümer
DELETE/api/notes/:id

Löscht eine Notiz dauerhaft. Dies ist irreversibel — verwende action: 'trash' via PATCH für weiches Löschen.

ParameterTypBeschreibung
iderforderlichstringNotiz-UUID

Ordner

GET/api/folders

Listet alle Ordner des authentifizierten Benutzers auf.

  • Antwort: { folders: Folder[] }
POST/api/folders

Erstellt einen neuen Ordner.

ParameterTypBeschreibung
nameerforderlichstringOrdnername
  • Antwortstatus: 201 Created
PATCH/api/folders/:id

Benennt einen Ordner um.

ParameterTypBeschreibung
iderforderlichstringOrdner-UUID (Pfadparameter)
nameerforderlichstringNeuer Ordnername
DELETE/api/folders/:id

Löscht einen Ordner. Notizen darin werden nicht gelöscht — sie werden ordnerlos.

ParameterTypBeschreibung
iderforderlichstringOrdner-UUID

Paginierung

Der GET /api/notes-Endpoint verwendet offset-basierte Paginierung. Steuere sie mit zwei Abfrageparametern:

ParameterStandardBeschreibung
limit50Ergebnisse pro Seite (1–100)
offset0Anzahl der zu überspringenden Ergebnisse

Die Antwort enthält einen hasMore -Boolean. Rufe die nächste Seite ab, indem du offset erhöhst:

// Fetch all notes (auto-pagination)
async function fetchAllNotes(token) {
  const notes = [];
  let offset = 0;
  while (true) {
    const res = await fetch(
      `https://notes.krokanti.com/api/notes?limit=100&offset=${offset}`,
      { headers: { Authorization: `Bearer ${token}` } }
    );
    const { notes: page, hasMore } = await res.json();
    notes.push(...page);
    if (!hasMore) break;
    offset += 100;
  }
  return notes;
}

Konflikterkennung

Die API verwendet optimistische Nebenläufigkeitskontrolle, um verlorene Aktualisierungen zu verhindern, wenn mehrere Clients dieselbe Notiz gleichzeitig bearbeiten.

Füge das Feld clientUpdatedAt in jede PATCH-Anfrage ein — setze es auf den Wert updatedAt der zuletzt abgerufenen Notiz. Wenn ein anderer Client eine neuere Version gespeichert hat, gibt die API 409 Conflict mit der aktuellen Serverversion zurück, damit du zusammenführen kannst:

// PATCH with conflict detection
const res = await fetch(`https://notes.krokanti.com/api/notes/${id}`, {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    title: "Updated title",
    content: "<p>New content</p>",
    clientUpdatedAt: note.updatedAt, // ISO 8601 string
  }),
});

if (res.status === 409) {
  const { serverNote } = await res.json();
  // Merge your changes with serverNote, then retry with force: true
  await fetch(`https://notes.krokanti.com/api/notes/${id}`, {
    method: "PATCH",
    headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
    body: JSON.stringify({ content: merged, force: true }),
  });
}

Übergebe force: true um die Konflikterkennung zu überspringen und den Serverstatus bedingungslos zu überschreiben.

Sichere Notizen

Sichere Notizen werden vor der Speicherung clientseitig mit AES-256-GCM verschlüsselt. Die API gibt den rohen verschlüsselten Blob zurück — sie kann ihn nicht entschlüsseln (der PIN verlässt nie den Browser).

  • GET /api/notes/:id — gibt Inhalt als verschlüsselten JSON-Blob zurück, der mit {"v":1,"alg":"AES-256-GCM",...} beginnt
  • PATCH /api/notes/:id — blockiert für sichere Notizen (gibt 403 zurück). Verschlüsselung kann nur über die Web-App erfolgen.
  • Sichere Notizen können nicht öffentlich gemacht werden (gibt 400 zurück, wenn isPublic: true gesendet wird).

Fehlercodes

Alle Fehlerantworten enthalten einen JSON-Body mit einem error -String und manchmal ein code -Feld für maschinenlesbare Untercodes.

StatusBedeutungHäufige Ursache
400Fehlerhafte AnfrageUngültiger Anfrage-Body (z.B. sichere Notiz öffentlich machen)
401Nicht autorisiertAPI-Token fehlt oder ist ungültig
402Zahlung erforderlichFunktion erfordert Pro-Abonnement (code: pro_required)
403VerbotenNotiz gehört einem anderen Benutzer oder Bearbeitung einer sicheren Notiz via API
404Nicht gefundenNotiz oder Ordner existiert nicht
409KonfliktclientUpdatedAt ist veraltet — Antwort enthält serverNote zum Zusammenführen
429Zu viele AnfragenRatenlimit überschritten — überprüfe den Retry-After-Antwortheader
// Error response body
{ "error": "Rate limit exceeded" }

// With machine-readable subcode
{ "error": "Secure notes require Pro", "code": "pro_required" }

// Conflict response
{ "conflict": true, "serverNote": { "id": "...", "title": "...", "updatedAt": "..." } }

Ratenlimits

API-Token-Anfragen sind auf 100 Anfragen pro Minute pro Token begrenzt (festes Fenster). Sitzungsbasierte Anfragen (Browser) sind nicht begrenzt.

Wenn das Limit überschritten wird, gibt die API 429 Too Many Requests mit einem Retry-After -Header zurück, der angibt, wie viele Sekunden vor einem erneuten Versuch gewartet werden soll.

# 429 response headers
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/json

{ "error": "Rate limit exceeded" }

Code-Beispiele

Ersetze kn_your_token_here durch deinen echten API-Token aus Einstellungen → Verbindungen.

cURL

# List your 10 most recently updated notes
curl "https://notes.krokanti.com/api/notes?limit=10" \
  -H "Authorization: Bearer kn_your_token_here"

# Create a new note
curl -X POST "https://notes.krokanti.com/api/notes" \
  -H "Authorization: Bearer kn_your_token_here"

# Update a note's title and content
curl -X PATCH "https://notes.krokanti.com/api/notes/NOTE_ID" \
  -H "Authorization: Bearer kn_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{ "title": "My Note", "content": "<p>Hello world</p>", "clientUpdatedAt": "2026-01-01T00:00:00.000Z" }'

# Search notes
curl "https://notes.krokanti.com/api/notes?search=meeting&limit=20" \
  -H "Authorization: Bearer kn_your_token_here"

# Move a note to trash
curl -X PATCH "https://notes.krokanti.com/api/notes/NOTE_ID" \
  -H "Authorization: Bearer kn_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{ "action": "trash" }'

JavaScript (fetch)

const BASE = "https://notes.krokanti.com/api";
const TOKEN = "kn_your_token_here";

const headers = {
  Authorization: `Bearer ${TOKEN}`,
  "Content-Type": "application/json",
};

// List notes
const { notes, hasMore } = await fetch(`${BASE}/notes?limit=50`, { headers }).then(r => r.json());

// Create a note
const note = await fetch(`${BASE}/notes`, { method: "POST", headers }).then(r => r.json());

// Update title + content
await fetch(`${BASE}/notes/${note.id}`, {
  method: "PATCH",
  headers,
  body: JSON.stringify({
    title: "Shopping list",
    content: "<ul><li>Milk</li><li>Eggs</li></ul>",
    clientUpdatedAt: note.updatedAt,
  }),
});

// Add a tag
await fetch(`${BASE}/notes/${note.id}`, {
  method: "PATCH",
  headers,
  body: JSON.stringify({ tags: ["shopping", "weekly"] }),
});

// Pin the note
await fetch(`${BASE}/notes/${note.id}`, {
  method: "PATCH",
  headers,
  body: JSON.stringify({ isPinned: true }),
});

// List folders
const { folders } = await fetch(`${BASE}/folders`, { headers }).then(r => r.json());

// Create a folder and move the note to it
const folder = await fetch(`${BASE}/folders`, {
  method: "POST",
  headers,
  body: JSON.stringify({ name: "Groceries" }),
}).then(r => r.json());

await fetch(`${BASE}/notes/${note.id}`, {
  method: "PATCH",
  headers,
  body: JSON.stringify({ folderId: folder.id }),
});

Python (requests)

import requests

BASE = "https://notes.krokanti.com/api"
TOKEN = "kn_your_token_here"
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}

# List notes
resp = requests.get(f"{BASE}/notes", headers=HEADERS, params={"limit": 50})
data = resp.json()
notes, has_more = data["notes"], data["hasMore"]

# Create a note
note = requests.post(f"{BASE}/notes", headers=HEADERS).json()

# Update it
requests.patch(
    f"{BASE}/notes/{note['id']}",
    headers=HEADERS,
    json={
        "title": "Meeting notes",
        "content": "<p>Action items:</p><ul><li>Follow up with Alice</li></ul>",
        "clientUpdatedAt": note["updatedAt"],
    },
)

# Search and print titles
resp = requests.get(f"{BASE}/notes", headers=HEADERS, params={"search": "meeting"})
for n in resp.json()["notes"]:
    print(n["title"])

# Delete a note permanently
requests.delete(f"{BASE}/notes/{note['id']}", headers=HEADERS)