API reference¶
This summarizes the REST API. The machine-readable contract is the committed
OpenAPI schema
(packages/contract/openapi.json), from which the TypeScript client types are
generated; CI fails on drift between the server and that schema.
All endpoints are under /api/v1 and require Authorization: Bearer <token>
(except /healthz). Responses are JSON.
| Method & path | Purpose |
|---|---|
POST /notes |
Create a note. Idempotent via idempotency_key. |
GET /notes |
List notes, newest first (limit, cursor, tag). |
GET /notes/{id} |
Read a note in full. |
DELETE /notes/{id} |
Soft-delete (move to .trash/). |
POST /notes/{id}/restore |
Restore a trashed note. |
GET /search |
Full-text search (q required). |
GET /trash |
List trashed notes. |
GET /healthz |
Liveness/readiness (no auth). |
Errors use a consistent shape:
{ "error": { "code": "not_found", "message": "No note with that id." } }
Create a note¶
curl -X POST https://your-host/api/v1/notes \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Meeting notes",
"body": "# Sync\n- shipped the thing",
"tags": ["work"],
"idempotency_key": "2026-05-23-sync"
}'
Repeating the same request with the same idempotency_key returns the existing
note instead of creating a duplicate.