> ## Documentation Index
> Fetch the complete documentation index at: https://docs.elanotes.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Search and list return handles, provenance, and coverage. Fetch a body only with GET /v1/notes/{note} (line slice) or /raw.
> Read coverage before treating a result set as complete. Zero hits with coverage is a valid answer.
> Follow error hint. Writes need an Idempotency-Key header and agent_id.
> The user creates personal access tokens in web Settings → Access tokens. Read is included; authenticated note mutations require notes.write. Supplying agent_id does not grant permission.
> Reuse an Idempotency-Key only for an identical retry. After VERSION_MOVED, re-read and submit the changed operation with a new key.
> Local MCP is read-only. Authenticated Cloud MCP adds write_note and edit_note only with notes.write and connected write storage; open local mode skips the permission check. Inspect tools/list before calling conditional tools.
> A queued write has not changed the note. Record pending_id. Authenticated GET /v1/writes/{id} also requires notes.review; write-only tokens must hand the ID to a reviewer.
> Leave approval to a human. Agents must not approve their own proposals or request review permission merely to finish a task. The API permits review-scoped personal tokens; this is a workflow rule.
> Cloud search and note reads do not return Private plaintext. Coverage says Private notes not searched. Sync may still carry ciphertext.
> Production origin is https://api.elanotes.com. baseUrl has no /v1 suffix.

# Store wrapped private-note keys

> Replace the passphrase wrap of the vault data key. Optional recovery wrap is replaced when present and cleared when null. Write permission when auth is on. Personal access tokens receive 403 CREDENTIAL_FORBIDDEN. The server stores ciphertext only.



## OpenAPI

````yaml /openapi.json put /v1/sync/e2ee/keys
openapi: 3.1.0
info:
  description: >-
    HTTP contract for the current Fastify `/v1` surface. The vault is implicit:
    `Authorization: Bearer` binds `vault_id`; open locally when no auth is
    configured. Search and list return handles, provenance, and coverage — never
    full note bodies (use `GET /v1/notes/{note}` or `/raw`). Writes require
    `Idempotency-Key` and return a receipt (`applied` or `queued`). Create and
    PATCH embed `note` (GET shape without `lines`) when `status = applied`.
    Follow a queued receipt at `GET /v1/writes/{id}`. Errors are `{ error,
    message, statusCode, requestId, reason, hint, help, details[] }`. `reason`
    is UPPER_SNAKE (AIP-193 ErrorInfo.reason); `hint` is one imperative sentence
    naming the next request; `help` is a docs URL; `details` is the AIP-193 list
    (ErrorInfo, BadRequest, RetryInfo, QuotaFailure, Help). 401/403 on `/v1` and
    `/mcp` send RFC 6750 `WWW-Authenticate: Bearer realm="elanotes"`
    (`error=invalid_token` when a token was presented;
    `error=insufficient_scope` plus `scope` on 403). RFC 9728
    `/.well-known/oauth-protected-resource` is served when an issuer is
    configured. Abuse-protection rate limits (not cost metering) apply to `/v1`
    and `/mcp`. GET `/v1/*` and `/mcp` use `RATE_LIMIT_READ_PER_MINUTE` (default
    600). POST/PATCH/PUT/DELETE under `/v1` use `RATE_LIMIT_WRITE_PER_MINUTE`
    (default 60). `0` disables a policy. Responses carry IETF draft
    `RateLimit-Limit` / `RateLimit-Remaining` / `RateLimit-Reset`; every 429
    also has `Retry-After`. Reads are never metered for cost. This document does
    not invent `/v1/daily`, `/v1/tasks`, or `folder_id`; those are not
    registered routes. `GET /v1/tags` rolls up frontmatter tags. List filtering
    uses AIP-160 `filter` on GET `/v1/notes`, `/v1/search`, `/v1/grep`,
    `/v1/writes`, and `/v1/notes/{note}:listRevisions`. `PATCH
    /v1/notes/{note}?update_mask=` and `PATCH
    /v1/vault/write-settings?update_mask=` are AIP-161 field masks. Missing or
    unknown mask is `INVALID_UPDATE_MASK`. Canonical custom methods are AIP-136
    `:edit`, `:move`, `:rename`, `:rollback`, `:listRevisions`, `:approve`, and
    `:reject`. Legacy slash forms stay and send `Deprecation: true` plus `Link:
    rel="successor-version"`. Folder identity is a vault-relative `path` in the
    JSON body. `GET /v1/audit-events`, `POST /v1/export`, and `POST
    /v1/vault/purge` are signed-in session routes. Personal access tokens
    receive 403 CREDENTIAL_FORBIDDEN. Activity rows are ids and counters only.
    `GET /openapi.json` is unauthenticated. Auth protects only `/v1` and `/mcp`.
  title: Ela Notes API
  version: 0.1.0
servers:
  - description: Production
    url: https://api.elanotes.com
  - description: Local
    url: http://127.0.0.1:4000
security:
  - bearer: []
tags:
  - description: >-
      Lexical (and optional hybrid) search plus grep. Results are handles plus
      coverage.
    name: Search
  - description: >-
      List by AIP-160 filter or legacy path prefix, plus line-sliced or raw
      reads. No folder_id query.
    name: Notes
  - description: >-
      Idempotent create, PATCH field-mask update, delete, and AIP-136
      :edit/:move/:rename. Applied create/PATCH receipts embed the note
      resource.
    name: Writes
  - description: One nested folder level. Identity is `path` in the body, not a folder id.
    name: Folders
  - description: >-
      List, presign, and multipart upload. Upload needs a vault working copy
      (`VAULT_PATH`).
    name: Attachments
  - description: 'Frontmatter tag rollup. Body #hashtags are not tags.'
    name: Tags
  - description: POST-only property aggregation. The list path is GET /v1/notes?filter=.
    name: Query
  - description: >-
      Pending-write queue (including GET /v1/writes/{id}), AIP-162 revisions,
      :rollback, :approve, :reject, and write settings. Reviewer permission when
      auth is on.
    name: Review
  - description: Index status and vault summary for the bound vault.
    name: Vault
  - description: >-
      Session activity, library export, and owner library deletion. No note
      text.
    name: Account
  - description: >-
      Device replica plane (ops, snapshot, checkpoint, E2EE salt). Unchanged by
      the agentic API push.
    name: Sync
  - description: Streamable HTTP MCP on `/mcp` with the same auth plugin as `/v1`.
    name: MCP
  - description: >-
      Unauthenticated AuthKit bootstrap for native public clients. Token grants
      are proxied so the phone never holds WORKOS_API_KEY.
    name: Auth
  - description: Unauthenticated process and contract endpoints.
    name: Meta
paths:
  /v1/sync/e2ee/keys:
    put:
      tags:
        - Sync
      summary: Store wrapped private-note keys
      description: >-
        Replace the passphrase wrap of the vault data key. Optional recovery
        wrap is replaced when present and cleared when null. Write permission
        when auth is on. Personal access tokens receive 403
        CREDENTIAL_FORBIDDEN. The server stores ciphertext only.
      operationId: putE2eeKeys
      requestBody:
        content:
          application/json:
            schema:
              additionalProperties: false
              properties:
                kdf:
                  additionalProperties: false
                  properties:
                    'N':
                      const: 131072
                      type: number
                    p:
                      const: 1
                      type: number
                    r:
                      const: 8
                      type: number
                    salt:
                      maxLength: 128
                      minLength: 1
                      type: string
                  required:
                    - salt
                    - 'N'
                    - r
                    - p
                  type: object
                keyVersion:
                  maximum: 1000
                  minimum: 1
                  type: integer
                recovery:
                  anyOf:
                    - additionalProperties: false
                      properties:
                        salt:
                          maxLength: 128
                          minLength: 1
                          type: string
                        wrappedDek:
                          type: string
                      required:
                        - salt
                        - wrappedDek
                      type: object
                    - type: 'null'
                wrappedDek:
                  type: string
              required:
                - keyVersion
                - kdf
                - wrappedDek
                - recovery
              type: object
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  enabled:
                    type: boolean
                  hierarchy:
                    anyOf:
                      - additionalProperties: false
                        properties:
                          kdf:
                            additionalProperties: false
                            properties:
                              'N':
                                const: 131072
                                type: number
                              p:
                                const: 1
                                type: number
                              r:
                                const: 8
                                type: number
                              salt:
                                maxLength: 128
                                minLength: 1
                                type: string
                            required:
                              - salt
                              - 'N'
                              - r
                              - p
                            type: object
                          keyVersion:
                            maximum: 1000
                            minimum: 1
                            type: integer
                          recovery:
                            anyOf:
                              - additionalProperties: false
                                properties:
                                  salt:
                                    maxLength: 128
                                    minLength: 1
                                    type: string
                                  wrappedDek:
                                    type: string
                                required:
                                  - salt
                                  - wrappedDek
                                type: object
                              - type: 'null'
                          wrappedDek:
                            type: string
                        required:
                          - keyVersion
                          - kdf
                          - wrappedDek
                          - recovery
                        type: object
                      - type: 'null'
                  recoveryNotice:
                    type: string
                  salt:
                    anyOf:
                      - type: string
                      - type: 'null'
                required:
                  - enabled
                  - salt
                  - recoveryNotice
                  - hierarchy
                type: object
          description: Default Response
          headers:
            RateLimit-Limit:
              description: >-
                IETF draft ceiling for this request's policy (read or write) in
                the current window.
              schema:
                type: integer
            RateLimit-Remaining:
              description: Requests remaining in the current window for this policy.
              schema:
                type: integer
            RateLimit-Reset:
              description: Seconds until the current window resets.
              schema:
                type: integer
        '400':
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  details:
                    items:
                      additionalProperties: {}
                      properties:
                        '@type':
                          type: string
                      required:
                        - '@type'
                      type: object
                    type: array
                  error:
                    type: string
                  help:
                    type: string
                  hint:
                    type: string
                  message:
                    type: string
                  reason:
                    type: string
                  requestId:
                    type: string
                  statusCode:
                    maximum: 9007199254740991
                    minimum: -9007199254740991
                    type: integer
                required:
                  - error
                  - message
                  - statusCode
                  - requestId
                  - reason
                  - hint
                  - help
                  - details
                type: object
          description: Default Response
        '403':
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  details:
                    items:
                      additionalProperties: {}
                      properties:
                        '@type':
                          type: string
                      required:
                        - '@type'
                      type: object
                    type: array
                  error:
                    type: string
                  help:
                    type: string
                  hint:
                    type: string
                  message:
                    type: string
                  reason:
                    type: string
                  requestId:
                    type: string
                  statusCode:
                    maximum: 9007199254740991
                    minimum: -9007199254740991
                    type: integer
                required:
                  - error
                  - message
                  - statusCode
                  - requestId
                  - reason
                  - hint
                  - help
                  - details
                type: object
          description: Default Response
        '429':
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  details:
                    items:
                      additionalProperties: {}
                      properties:
                        '@type':
                          type: string
                      required:
                        - '@type'
                      type: object
                    type: array
                  error:
                    type: string
                  help:
                    type: string
                  hint:
                    type: string
                  message:
                    type: string
                  reason:
                    type: string
                  requestId:
                    type: string
                  statusCode:
                    maximum: 9007199254740991
                    minimum: -9007199254740991
                    type: integer
                required:
                  - error
                  - message
                  - statusCode
                  - requestId
                  - reason
                  - hint
                  - help
                  - details
                type: object
          description: Default Response
          headers:
            RateLimit-Limit:
              description: >-
                IETF draft ceiling for this request's policy (read or write) in
                the current window.
              schema:
                type: integer
            RateLimit-Remaining:
              description: Requests remaining in the current window for this policy.
              schema:
                type: integer
            RateLimit-Reset:
              description: Seconds until the current window resets.
              schema:
                type: integer
            Retry-After:
              description: >-
                Seconds to wait before retrying. Present on every 429, including
                write-budget refusals.
              schema:
                type: integer
        '503':
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  details:
                    items:
                      additionalProperties: {}
                      properties:
                        '@type':
                          type: string
                      required:
                        - '@type'
                      type: object
                    type: array
                  error:
                    type: string
                  help:
                    type: string
                  hint:
                    type: string
                  message:
                    type: string
                  reason:
                    type: string
                  requestId:
                    type: string
                  statusCode:
                    maximum: 9007199254740991
                    minimum: -9007199254740991
                    type: integer
                required:
                  - error
                  - message
                  - statusCode
                  - requestId
                  - reason
                  - hint
                  - help
                  - details
                type: object
          description: Default Response
components:
  securitySchemes:
    bearer:
      description: >-
        Static bearer, WorkOS JWT, or open local when AUTH_REQUIRED is off and
        no static tokens are set. The token selects the vault; paths do not
        include vault_id.
      scheme: bearer
      type: http

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.