GET /openapi.json, committed
at contracts/openapi/elanotes.v1.json, copied to this site’s
openapi.json. Generated reference pages sit under API reference.
Style authority is Google AIP. Recorded
deviations: envelope outer shape, vault-implicit paths, Idempotency-Key
header, receipts instead of AIP-151 Operations.
There is no /v1/daily, /v1/tasks, or folder_id. Daily notes are
filter=kind = "daily". Tasks are checklist items in note bodies.
Folders are vault-relative path strings.
Host path
/healthz returns {"status":"ok"}. /openapi.json returns 200.
Unauthenticated /v1/vault is 401 AUTH_MISSING, not 503. The API
reference server list is production, then local.
/v1 is the stable path. GET /openapi.json and GET /healthz are
unauthenticated. Auth protects only /v1 and /mcp.
Auth and WWW-Authenticate
Send Authorization: Bearer <token>. Open locally when no auth is
configured. A library (vault_id in the API) is implicit: the bearer binds vault_id. Resource
bodies may carry AIP-122 name (vaults/{vault_id}/notes/{slug}); paths
stay /v1/notes/{note}.
401 / 403 on /v1 and /mcp add RFC 6750
WWW-Authenticate: Bearer realm="elanotes":
- missing token → no
error=pair (challenge only) - presented but invalid →
error="invalid_token" - forbidden →
error="insufficient_scope"plusscopenaming the missing permission (notes.write/notes.review)
WORKOS_CLIENT_ID or OAUTH_TEST_HMAC_SECRET is set, the challenge
includes resource_metadata and
GET /.well-known/oauth-protected-resource serves RFC 9728 metadata.
Otherwise that well-known path is 404 with a hint.
Personal access tokens and permissions
Create a personal access token in signed-in web Settings → Access tokens. Read is included. Authenticated note mutations requirenotes.write across HTTP, SDKs, CLI, and Cloud MCP; requests without it now return 403 PERMISSION_MISSING instead of queueing a write. Supplying agent_id does not grant permission. Ordinary static read bearers remain read-only.
Authenticated Cloud MCP exposes write_note and edit_note only with notes.write and connected write storage. Inspect tools/list; a valid read-only connection need not expose those tools. Open local mode skips the permission check.
Personal-token note writes use actor pat:<prefix> and follow agent write policy, budget, and review. Personal tokens cannot manage tokens, mutate device replicas, or rename/delete folders; use a suitable signed-in session for those operations. Folder creation remains available with notes.write.
POST /v1/access-tokens, GET /v1/access-tokens, and DELETE /v1/access-tokens/{id} require a signed-in session. Omitting expires_at stores no expiry. The token guide links their reference pages and explains expiry, the 25-live-token cap, and one-time secret disclosure.
GET /v1/sessions, DELETE /v1/sessions/{id}, and POST /v1/sessions/revoke-all also require a signed-in session. Personal access tokens and static tokens receive 403 CREDENTIAL_FORBIDDEN. Revoking a session invalidates refresh tokens issued under it.
Errors, reasons, hints
Envelope:error, message, statusCode, requestId keep their historic meaning.
reason is UPPER_SNAKE. hint is one imperative sentence naming the next
request. help is this site’s errors page with a
lowercase-reason anchor. details is the AIP-193 list (ErrorInfo,
BadRequest, RetryInfo, QuotaFailure, Help). Follow hint.
Filters
AIP-160filter + AIP-132 order_by on GET /v1/notes, /v1/search,
/v1/grep, /v1/writes, and /v1/notes/{note}:listRevisions.
GET /v1/tags rolls up frontmatter tags (body #hashtags are not tags).
=, !=, <, <=, >, >=; : (has) for repeated
fields; AND, OR, NOT / -; parentheses; double-quoted strings;
RFC 3339 timestamps; * wildcard inside string literals.
Note fields: slug, path, folder, title, tags, kind
(regular | daily), status (active | archived), visibility,
daily_date, created_at, updated_at, observed_at.
Pending-write fields: status, kind, agent_id, created_at,
decided_at.
Unknown field or operator → 400 INVALID_FILTER with a hint listing
allowed fields. Coverage echoes filter.
POST /v1/query stays for property aggregation. The list path is
GET /v1/notes?filter=.
Field masks
title, content, folder, tags, status,
frontmatter.<key>. Body is the partial resource. Missing or unknown
mask → 400 INVALID_UPDATE_MASK. Writes still need Idempotency-Key
and honor review / budget.
Pagination
Canonical AIP-158:page_size, page_token, next_page_token.
Legacy limit / cursor / next_cursor / prefix / archived /
status stay accepted and emitted through v1 and are marked deprecated
in the spec. Default order stays deterministic.
order_by examples: updated_at desc, created_at, observed_at,
title, daily_date desc.
Rate limits
Abuse protection, not cost metering. Reads are never metered.0 disables a policy. Responses carry IETF draft RateLimit-Limit /
RateLimit-Remaining / RateLimit-Reset. Every 429 includes
Retry-After. Envelope reason is RATE_LIMITED (abuse) or
WRITE_BUDGET_EXCEEDED (per-agent budget, separate quota).
Idempotency
Writes requireIdempotency-Key. Replay with the same key and the same
body returns the original receipt (idempotency-replayed: true). Reuse a
key only for that identical retry. A different body for a key that already
has a receipt is 409 IDEMPOTENCY_KEY_REUSED. A failed VERSION_MOVED
does not store the key: re-read, then submit the changed operation with a
new key. SDKs and the CLI mint a UUID unless you supply one.
Custom methods
Canonical AIP-136 form (colon verb):
Revisions expose AIP-162
revision_id / revision_create_time and keep
version_id as a v1 alias.
Create / PATCH receipts embed note when status = "applied", with
committed_seq and unchanged. A queued receipt has not changed the
note. Record and report pending_id. In authenticated mode, polling GET /v1/writes/{id} also needs notes.review; with a write-only token, hand the ID to a reviewer. :approve and :reject require notes.review, including when the credential is a personal token. Agents must leave approval to a human and must not request review permission merely to finish a task; that is a workflow rule, not a human-only credential check. Open local mode skips the permission check.
Deprecations
Legacy slash forms (/edit, /move, /rename, /restore, /approve,
/reject, /versions) and PUT /v1/vault/write-settings remain through
v1. They answer with Deprecation: true and
Link: <canonical>; rel="successor-version". Prefer the colon verbs and
PATCH + update_mask.