Referência da API para desenvolvedores
Usa a API REST do Krokanti Notes para ler e escrever notas programaticamente, automatizar fluxos de trabalho ou criar integrações. Todos os endpoints aceitam e retornam JSON.
https://notes.krokanti.com/api100 req/min per tokenSubscrição Pro necessária
O acesso por token de API requer uma subscrição Pro ativa. Gere seu token em Configurações → Conexões após a atualização.
Atualizar para Pro →Autenticação
Todos os pedidos de API devem incluir um token de API pessoal no cabeçalho Authorization. Gere tokens em Configurações → Conexões (plano Pro necessário). Os tokens começam com kn_ e são mostrados apenas uma vez na criação — guarde-os em segurança.
curl https://notes.krokanti.com/api/notes \
-H "Authorization: Bearer kn_your_token_here" \
-H "Content-Type: application/json"Notas
/api/notesLista todas as notas do utilizador autenticado. Suporta filtragem, ordenação e paginação.
| Parâmetro | Tipo | Descrição |
|---|---|---|
search | string | Pesquisa de texto completo no título e conteúdo |
folderId | string | Filtrar por ID de pasta |
tag | string | Filtrar por nome de etiqueta (correspondência exata) |
trashed | boolean | Define como "true" para listar notas no lixo |
sortBy | updatedAt | createdAt | title | Campo de ordenação (padrão: "updatedAt") |
sortOrder | asc | desc | Direção de ordenação (padrão: "desc") |
limit | number | Resultados por página — 1 a 100 (padrão: 50) |
offset | number | Número de resultados a ignorar (padrão: 0) |
- →Resposta: { notes: Note[], hasMore: boolean }
- →As notas fixadas são sempre retornadas primeiro, depois ordenadas pelo campo escolhido
- →O conteúdo de notas seguras é retornado como um blob cifrado (string JSON começando com {"v":1)
/api/notesCria uma nova nota vazia. Retorna o objeto de nota criado.
- →A nova nota tem título e conteúdo vazios — atualize-a imediatamente com PATCH
- →Estado de resposta: 201 Created
/api/notes/:idObtém uma única nota por ID.
| Parâmetro | Tipo | Descrição |
|---|---|---|
idobrigatório | string | UUID da nota |
- →Retorna 404 se a nota não existe ou pertence a outro utilizador
- →O conteúdo de notas seguras é retornado como um blob cifrado
/api/notes/:idAtualize o título, conteúdo, etiquetas, pasta ou metadados de uma nota. Suporta deteção de conflitos.
| Parâmetro | Tipo | Descrição |
|---|---|---|
idobrigatório | string | UUID da nota (parâmetro de caminho) |
title | string | Novo título |
content | string | Novo conteúdo HTML |
tags | string[] | Substitua o array completo de etiquetas |
folderId | string | null | Mover para pasta (null para remover da pasta) |
isPinned | boolean | Fixar ou desafixar a nota (apenas proprietário) |
isPublic | boolean | Publicar ou despublicar a nota (apenas proprietário) |
action | trash | restore | Mover para o lixo ou restaurar (apenas proprietário) |
clientUpdatedAt | string (ISO 8601) | Último updatedAt conhecido — usado para deteção de conflitos |
force | boolean | Ignorar a deteção de conflitos e sobrescrever o estado do servidor |
- →Retorna 409 com { conflict: true, serverNote } se clientUpdatedAt for mais antigo que a versão do servidor
- →Notas seguras não podem ser editadas via API (retorna 403)
- →Colaboradores com permissão de edição só podem atualizar título e conteúdo — etiquetas e pasta são apenas do proprietário
/api/notes/:idElimina permanentemente uma nota. É irreversível — usa action: 'trash' via PATCH para eliminação suave.
| Parâmetro | Tipo | Descrição |
|---|---|---|
idobrigatório | string | UUID da nota |
Pastas
/api/foldersLista todas as pastas do utilizador autenticado.
- →Resposta: { folders: Folder[] }
/api/foldersCria uma nova pasta.
| Parâmetro | Tipo | Descrição |
|---|---|---|
nameobrigatório | string | Nome da pasta |
- →Estado de resposta: 201 Created
/api/folders/:idRenomeia uma pasta.
| Parâmetro | Tipo | Descrição |
|---|---|---|
idobrigatório | string | UUID da pasta (parâmetro de caminho) |
nameobrigatório | string | Novo nome da pasta |
/api/folders/:idElimina uma pasta. As notas dentro não são eliminadas — ficam sem pasta.
| Parâmetro | Tipo | Descrição |
|---|---|---|
idobrigatório | string | UUID da pasta |
Paginação
O endpoint GET /api/notes usa paginação baseada em offset. Controla-a com dois parâmetros de consulta:
| Parâmetro | Padrão | Descrição |
|---|---|---|
limit | 50 | Resultados por página (1–100) |
offset | 0 | Número de resultados a ignorar |
A resposta inclui um booleano hasMore . Obtém a próxima página incrementando offset:
// 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;
}Deteção de conflitos
A API usa controlo de concorrência otimista para evitar atualizações perdidas quando múltiplos clientes editam a mesma nota simultaneamente.
Inclui o campo clientUpdatedAt em cada pedido PATCH — define-o com o valor updatedAt da nota que obtiveste. Se outro cliente guardou uma versão mais recente, a API retorna 409 Conflict com a versão atual do servidor para que possa fundir:
// 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 }),
});
}Passa force: true para ignorar a deteção de conflitos e sobrescrever o estado do servidor incondicionalmente.
Notas seguras
As notas seguras são cifradas do lado do cliente com AES-256-GCM antes de serem armazenadas. A API retorna o blob cifrado bruto — não o pode decifrar (o PIN nunca sai do browser).
- →GET /api/notes/:id — retorna conteúdo como um blob JSON cifrado começando com {"v":1,"alg":"AES-256-GCM",...}
- →PATCH /api/notes/:id — bloqueado para notas seguras (retorna 403). A cifragem só pode ser feita através da app web.
- →Notas seguras não podem ser tornadas públicas (retorna 400 se isPublic: true for enviado).
Códigos de erro
Todas as respostas de erro incluem um corpo JSON com uma string error e às vezes um campo code para subcódigos legíveis por máquina.
| Estado | Significado | Causa comum |
|---|---|---|
400 | Pedido inválido | Corpo do pedido inválido (ex. tornar uma nota segura pública) |
401 | Não autorizado | Token de API em falta ou inválido |
402 | Pagamento necessário | A funcionalidade requer uma subscrição Pro (code: pro_required) |
403 | Proibido | A nota pertence a outro utilizador, ou edição de nota segura via API |
404 | Não encontrado | A nota ou pasta não existe |
409 | Conflito | clientUpdatedAt está desatualizado — a resposta inclui serverNote para fundir |
429 | Pedidos a mais | Limite de taxa excedido — verifica o cabeçalho de resposta Retry-After |
// 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": "..." } }Limites de taxa
Os pedidos com token de API estão limitados a 100 pedidos por minuto por token (janela fixa). Os pedidos baseados em sessão (browser) não têm limite.
Quando o limite é excedido, a API retorna 429 Too Many Requests com um cabeçalho Retry-After indicando quantos segundos aguardar antes de tentar novamente.
# 429 response headers
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/json
{ "error": "Rate limit exceeded" }Exemplos de código
Substitua kn_your_token_here pelo seu token de API real de Configurações → Conexões.
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)