{
  "openapi": "3.1.0",
  "info": {
    "title": "zpe notes API",
    "version": "1.0.0",
    "description": "The notes app's machine surface: the account's notes, their tags, the search over them, the docs tree, attachments and one public read per shared note. Every operation lives under the versioned base `/_/api/v1`, and this document is served at `/_/api/v1/openapi.json`. The retired base `/api` answers 308 with `Link: rel=\"successor-version\"` naming the successor, so a client that has not been updated still lands.\n\n## Who a caller is\n\nIdentity is the door's (auth.zpe.vc), never this app's: the request carries the door's session cookie, and the account it names is the owner key of every row read or written. There is no app-local login, no password to store and no token this app mints. A signed-out client is refused with 401 and a JSON body, never a redirect it cannot read and never a challenge header.\n\n## What a row belongs to\n\nEvery note, tag, doc node and share is scoped by `account_id`. An id belonging to another account is a 404, not a 403: for that caller it does not exist. The one unscoped read in the app is the public note route, where the address IS the capability and a live share row is what authorises it.",
    "contact": {
      "name": "Oddbit",
      "url": "https://oddbit.id"
    },
    "license": {
      "name": "AGPL-3.0-or-later",
      "url": "https://www.gnu.org/licenses/agpl-3.0.html"
    }
  },
  "servers": [
    {
      "url": "/notes"
    }
  ],
  "security": [
    {
      "cookieAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "cookieAuth": {
        "type": "apiKey",
        "in": "cookie",
        "name": "session",
        "description": "The door's own session cookie, set by auth.zpe.vc when a person signs in. This app never sets it and cannot mint one."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "A refusal, in the fleet's error contract. `code` is the closed word a client branches on, `title` is the fixed sentence for that code, `detail` is what the route had to add, and `instance` is the path that failed.",
        "properties": {
          "type": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "code": {
            "type": "string"
          },
          "instance": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "code",
          "instance"
        ],
        "additionalProperties": false
      },
      "Attachment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The storage id: `account/<account>/note/<note>/<id>` under the app's R2 bucket."
          },
          "name": {
            "type": "string"
          },
          "size": {
            "type": "integer"
          },
          "type": {
            "type": "string",
            "description": "The content type the browser sent, which the download route answers with."
          }
        },
        "required": [
          "id",
          "name",
          "size",
          "type"
        ],
        "additionalProperties": true
      },
      "Note": {
        "type": "object",
        "description": "One note, as the API answers it. `files` is an array here; the column it comes from is a JSON string, which is why the shape differs from the row.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "account_id": {
            "type": "string",
            "description": "The door's principal id, which is the owner key of every row this app writes."
          },
          "content": {
            "type": "string",
            "description": "The plaintext column, and it is EMPTY on every note this app wrote. The text is in `body_sealed`, which only the account's key opens. A row written before encryption existed (2026-11-06) still carries its text here."
          },
          "body_sealed": {
            "type": [
              "string",
              "null"
            ],
            "description": "`v1.<base64url(iv||ciphertext||tag)>`: AES-256-GCM under the account's key, made in the browser. Null only on a row written before encryption existed."
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attachment"
            }
          },
          "created_at": {
            "type": "integer",
            "description": "Unix seconds."
          },
          "updated_at": {
            "type": "integer",
            "description": "Unix seconds."
          },
          "is_pinned": {
            "type": "integer",
            "enum": [
              0,
              1
            ]
          },
          "is_favorited": {
            "type": "integer",
            "enum": [
              0,
              1
            ]
          },
          "is_archived": {
            "type": "integer",
            "enum": [
              0,
              1
            ]
          },
          "pics": {
            "type": [
              "string",
              "null"
            ],
            "description": "The markdown image targets, as the JSON string upstream stored in the column."
          },
          "videos": {
            "type": "string",
            "description": "A JSON array as a string. Nothing in this port writes a video, so it is `[]`."
          }
        },
        "required": [
          "id",
          "content",
          "created_at",
          "updated_at",
          "is_pinned",
          "is_favorited",
          "is_archived"
        ],
        "additionalProperties": true
      },
      "NoteWithTags": {
        "type": "object",
        "description": "One note after an edit, plus the tags the edit left it with.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "account_id": {
            "type": "string",
            "description": "The door's principal id, which is the owner key of every row this app writes."
          },
          "content": {
            "type": "string",
            "description": "The plaintext column, and it is EMPTY on every note this app wrote. The text is in `body_sealed`, which only the account's key opens. A row written before encryption existed (2026-11-06) still carries its text here."
          },
          "body_sealed": {
            "type": [
              "string",
              "null"
            ],
            "description": "`v1.<base64url(iv||ciphertext||tag)>`: AES-256-GCM under the account's key, made in the browser. Null only on a row written before encryption existed."
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attachment"
            }
          },
          "created_at": {
            "type": "integer",
            "description": "Unix seconds."
          },
          "updated_at": {
            "type": "integer",
            "description": "Unix seconds."
          },
          "is_pinned": {
            "type": "integer",
            "enum": [
              0,
              1
            ]
          },
          "is_favorited": {
            "type": "integer",
            "enum": [
              0,
              1
            ]
          },
          "is_archived": {
            "type": "integer",
            "enum": [
              0,
              1
            ]
          },
          "pics": {
            "type": [
              "string",
              "null"
            ],
            "description": "The markdown image targets, as the JSON string upstream stored in the column."
          },
          "videos": {
            "type": "string",
            "description": "A JSON array as a string. Nothing in this port writes a video, so it is `[]`."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "id",
          "content",
          "created_at",
          "updated_at",
          "is_pinned",
          "is_favorited",
          "is_archived",
          "tags"
        ],
        "additionalProperties": true
      },
      "NotePage": {
        "type": "object",
        "description": "One page of notes: ten rows, and whether the next page exists.",
        "properties": {
          "notes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Note"
            }
          },
          "hasMore": {
            "type": "boolean"
          }
        },
        "required": [
          "notes",
          "hasMore"
        ],
        "additionalProperties": true
      },
      "TagUsage": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "count": {
            "type": "integer",
            "description": "How many of this account's notes carry the tag."
          }
        },
        "required": [
          "name",
          "count"
        ],
        "additionalProperties": true
      },
      "Stats": {
        "type": "object",
        "properties": {
          "memos": {
            "type": "integer",
            "description": "Upstream's name for a note count, kept so a port's consumer keeps working."
          },
          "tags": {
            "type": "integer"
          },
          "oldestNoteTimestamp": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Unix seconds, or null when the account has no notes."
          }
        },
        "required": [
          "memos",
          "tags",
          "oldestNoteTimestamp"
        ],
        "additionalProperties": true
      },
      "Timeline": {
        "type": "object",
        "description": "A count tree keyed by year, then month, then day, in the timezone the request named.",
        "additionalProperties": true
      },
      "Settings": {
        "type": "object",
        "description": "The account's settings document, stored whole. An absent key means the default, so a client sends only what it changes.",
        "additionalProperties": true
      },
      "ShareLink": {
        "type": "object",
        "properties": {
          "publicId": {
            "type": "string",
            "description": "The uuid the public address carries. The address IS the capability: no principal can read it."
          },
          "displayUrl": {
            "type": "string",
            "description": "The page a person opens."
          },
          "rawUrl": {
            "type": "string",
            "description": "The note's text at the machine base's public route."
          },
          "expiresAt": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Unix seconds, or null for a link that never lapses."
          }
        },
        "required": [
          "publicId",
          "displayUrl",
          "rawUrl",
          "expiresAt"
        ],
        "additionalProperties": true
      },
      "DocNode": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "file",
              "folder"
            ]
          },
          "title": {
            "type": "string"
          },
          "content": {
            "type": [
              "string",
              "null"
            ]
          },
          "parent_id": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "type",
          "title",
          "content"
        ],
        "additionalProperties": true
      },
      "DocTree": {
        "type": "object",
        "description": "One node of the docs tree, with its children nested under `children`.",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "file",
              "folder"
            ]
          },
          "title": {
            "type": "string"
          },
          "parent_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "children": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            },
            "description": "The same shape, one level down. Absent on a leaf."
          }
        },
        "required": [
          "id",
          "type",
          "title",
          "parent_id"
        ],
        "additionalProperties": true
      },
      "AttachmentPage": {
        "type": "object",
        "properties": {
          "attachments": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "hasMore": {
            "type": "boolean"
          }
        },
        "required": [
          "attachments",
          "hasMore"
        ],
        "additionalProperties": true
      },
      "PublicNote": {
        "type": "object",
        "properties": {
          "content": {
            "type": "string"
          },
          "updated_at": {
            "type": "integer",
            "description": "Unix seconds."
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attachment"
            },
            "description": "Always empty: a shared note's text is shared, its files are not."
          }
        },
        "required": [
          "content",
          "updated_at",
          "files"
        ],
        "additionalProperties": true
      }
    }
  },
  "paths": {
    "/_/api/v1/attachments": {
      "get": {
        "operationId": "listAttachments",
        "summary": "Every image, video and file the account has",
        "tags": [
          "files"
        ],
        "responses": {
          "200": {
            "description": "One page, newest first, drawn from the notes' own `pics`, `videos` and `files` columns.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentPage"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "1-based page of 20.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          }
        ]
      }
    },
    "/_/api/v1/docs/node": {
      "post": {
        "operationId": "createDocNode",
        "summary": "Create a doc file or folder",
        "tags": [
          "docs"
        ],
        "responses": {
          "201": {
            "description": "The node as stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocNode"
                }
              }
            }
          },
          "400": {
            "description": "Not a JSON body, no title, or a type that is neither `file` nor `folder`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Names one intent. A retry carrying the same key gets the first answer instead of a second row; the same key on a different operation is a 409.",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "A new node. A file starts with `# <title>` as its body.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "file",
                      "folder"
                    ]
                  },
                  "title": {
                    "type": "string"
                  },
                  "parent_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Absent or null puts the node at the root."
                  }
                },
                "required": [
                  "type",
                  "title"
                ]
              }
            }
          }
        }
      }
    },
    "/_/api/v1/docs/node/{id}": {
      "get": {
        "operationId": "getDocNode",
        "summary": "Read one doc node",
        "tags": [
          "docs"
        ],
        "responses": {
          "200": {
            "description": "The node and its body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocNode"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The node is not this account's.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The note's id, or the doc node's uuid.",
            "schema": {
              "type": "string"
            }
          }
        ]
      },
      "put": {
        "operationId": "updateDocNode",
        "summary": "Save a doc node's body",
        "tags": [
          "docs"
        ],
        "responses": {
          "200": {
            "description": "Saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "id"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "No `content` string in the body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The node is not this account's.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The note's id, or the doc node's uuid.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The body. An empty string is a body, not a delete.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string"
                  }
                },
                "required": [
                  "content"
                ]
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteDocNode",
        "summary": "Delete a doc node and its subtree",
        "tags": [
          "docs"
        ],
        "responses": {
          "200": {
            "description": "Deleted, naming every id the delete took with it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "deletedIds": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "success",
                    "deletedIds"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The node is not this account's.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The note's id, or the doc node's uuid.",
            "schema": {
              "type": "string"
            }
          }
        ]
      },
      "patch": {
        "operationId": "moveDocNode",
        "summary": "Move a doc node under another folder",
        "tags": [
          "docs"
        ],
        "responses": {
          "200": {
            "description": "Moved, or already where the request asked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "message"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Not a JSON body, or a destination that is this node's own descendant or another account's folder.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The node is not this account's.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The note's id, or the doc node's uuid.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The destination folder, or null for the root.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "new_parent_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Null moves the node to the root."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/_/api/v1/docs/node/{id}/rename": {
      "post": {
        "operationId": "renameDocNode",
        "summary": "Rename a doc node",
        "tags": [
          "docs"
        ],
        "responses": {
          "200": {
            "description": "Renamed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "new_title": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success",
                    "new_title"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "No title in the body, or a blank one.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The node is not this account's.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The note's id, or the doc node's uuid.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The new title; a blank one is refused.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "new_title": {
                    "type": "string"
                  }
                },
                "required": [
                  "new_title"
                ]
              }
            }
          }
        }
      }
    },
    "/_/api/v1/docs/tree": {
      "get": {
        "operationId": "docsTree",
        "summary": "The account's docs tree",
        "tags": [
          "docs"
        ],
        "responses": {
          "200": {
            "description": "One page of the tree, one level per parent.",
            "headers": {
              "x-next-cursor": {
                "description": "Present when another page exists: the `cursor` to send for it. Absent on the last page.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DocTree"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many rows this page carries. Default 100 for tags, 200 for the docs tree; the most a page returns is 500.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The `x-next-cursor` value from the previous page. A keyset cursor, not an offset, so a write during the walk neither repeats nor skips a row.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/_/api/v1/files/{noteId}/{fileId}": {
      "get": {
        "operationId": "downloadAttachment",
        "summary": "Download one attachment",
        "tags": [
          "files"
        ],
        "responses": {
          "200": {
            "description": "The object, with the content type it was stored under. Text-like files are served as `text/plain` so a stored page cannot run in the app's origin.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "The note id is not a whole number.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The note is not this account's, or the object is not in storage.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer, or this deployment has no R2 bucket bound.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "noteId",
            "in": "path",
            "required": true,
            "description": "The note the attachment belongs to. Checked against the account before storage is read.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "fileId",
            "in": "path",
            "required": true,
            "description": "The attachment's id, as the note's `files` array names it.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "preview",
            "in": "query",
            "description": "`true` asks the browser to render rather than download.",
            "schema": {
              "type": "boolean"
            }
          }
        ]
      }
    },
    "/_/api/v1/notes": {
      "get": {
        "operationId": "listNotes",
        "summary": "List the account's notes",
        "tags": [
          "notes"
        ],
        "responses": {
          "200": {
            "description": "One page of notes, newest first with pinned rows on top.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotePage"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer, so this app cannot say whose rows these are.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "1-based page of ten notes. A page below one is read as one.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "tag",
            "in": "query",
            "description": "Only notes carrying this tag, lowercased by the writer.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "favorites",
            "in": "query",
            "description": "`true` limits the answer to favorited notes.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "archived",
            "in": "query",
            "description": "`true` lists archived notes instead of live ones.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "startTimestamp",
            "in": "query",
            "description": "Unix seconds. Used together with `endTimestamp`; alone it is ignored.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "endTimestamp",
            "in": "query",
            "description": "Unix seconds, exclusive. Used together with `startTimestamp`.",
            "schema": {
              "type": "integer"
            }
          }
        ]
      },
      "post": {
        "operationId": "createNote",
        "summary": "Create a note, with attachments",
        "tags": [
          "notes"
        ],
        "responses": {
          "201": {
            "description": "The note as stored: `content` empty, the envelope in `body_sealed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Note"
                }
              }
            }
          },
          "400": {
            "description": "No sealed body, a `sealed` part that is not a v1 envelope, a plaintext `content` part, or a body that is not multipart.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer, or this deployment has no R2 bucket bound so the attachment cannot be stored.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Names one intent. A retry carrying the same key gets the first answer instead of a second row; the same key on a different operation is a 409.",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "`sealed` (required), plus `tags` and `pics`, plus zero or more `file` parts. `sealed` is the `v1.` envelope the browser made under this account's key; `tags` and `pics` are the JSON arrays it derived from the text it kept. A plaintext `content` part is refused with 400: this route stores no note in the clear, and the Worker holds no key to seal one.",
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "sealed": {
                    "type": "string",
                    "description": "The `v1.` envelope. Required: a create without one stores nothing."
                  },
                  "tags": {
                    "type": "string",
                    "description": "A JSON array of tag names, as multipart parts must be strings. The browser derives it from the plaintext."
                  },
                  "pics": {
                    "type": "string",
                    "description": "A JSON array of the markdown image URLs the body names. Derived by the browser, since the server cannot read the body."
                  },
                  "tokens": {
                    "type": "string",
                    "description": "A JSON array of search tokens: one 22-character HMAC per normalised word, from `searchTokens(key, plaintext)`. Omit it and the note is written unsearchable; there is no server-side substitute."
                  },
                  "file": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    },
                    "description": "Stored under the account's prefix. Image parts are not stored: upstream put images in the note's markdown instead."
                  }
                },
                "required": [
                  "sealed"
                ]
              }
            }
          }
        }
      }
    },
    "/_/api/v1/notes/merge": {
      "post": {
        "operationId": "mergeNotes",
        "summary": "Merge one note into another",
        "tags": [
          "notes"
        ],
        "responses": {
          "200": {
            "description": "The target, after the merge.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Note"
                }
              }
            }
          },
          "400": {
            "description": "Not a JSON body, the two ids are equal or not whole numbers, or no `sealed` envelope was sent.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "One or both notes are not this account's.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "description": "The two notes to merge and the envelope the client made of their joined text.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sourceNoteId": {
                    "type": "integer",
                    "description": "The note that is absorbed and removed."
                  },
                  "targetNoteId": {
                    "type": "integer",
                    "description": "The note that survives and grows."
                  },
                  "sealed": {
                    "type": "string",
                    "description": "The merged body, sealed by the client: both bodies are ciphertext, so the server cannot join them. The client decrypts both, applies its separator, and sends the envelope. Required."
                  },
                  "pics": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "The image URLs of the merged body, derived by the client from the text it joined."
                  },
                  "tokens": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "The search tokens of the merged body. Absent leaves the target's old index rows in place, which would answer its pre-merge words."
                  }
                },
                "required": [
                  "sourceNoteId",
                  "targetNoteId",
                  "sealed"
                ]
              }
            }
          }
        }
      }
    },
    "/_/api/v1/notes/timeline": {
      "get": {
        "operationId": "notesTimeline",
        "summary": "Counts of notes by year, month and day",
        "tags": [
          "notes"
        ],
        "responses": {
          "200": {
            "description": "A nested count tree, over the account's 5000 most recent notes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Timeline"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "timezone",
            "in": "query",
            "description": "An IANA name. An unrecognised one falls back to UTC rather than failing.",
            "schema": {
              "type": "string",
              "default": "UTC"
            }
          }
        ]
      }
    },
    "/_/api/v1/notes/{id}": {
      "get": {
        "operationId": "getNote",
        "summary": "Read one note, for the editor",
        "tags": [
          "notes"
        ],
        "responses": {
          "200": {
            "description": "The note, its attachments and its tags.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NoteWithTags"
                }
              }
            }
          },
          "400": {
            "description": "The id is not a whole number.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The note is not this account's, and for this caller it does not exist.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The note's id, or the doc node's uuid.",
            "schema": {
              "type": "string"
            }
          }
        ]
      },
      "put": {
        "operationId": "editNote",
        "summary": "Edit a note, its attachments and its mood",
        "tags": [
          "notes"
        ],
        "responses": {
          "200": {
            "description": "The note after the edit, plus its tags. Or `{success, noteDeleted}` when the edit emptied it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NoteWithTags"
                }
              }
            }
          },
          "400": {
            "description": "The id is not a whole number, the body is not multipart, `sealed` is not a v1 envelope, or a plaintext `content` part was sent.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The note is not this account's, and for this caller it does not exist.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer, or this deployment has no R2 bucket bound.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The note's id, or the doc node's uuid.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "`sealed` (the new envelope), `tags`, `pics`, `filesToDelete` (a JSON list of attachment ids), `file` parts, `update_timestamp`, and the three mood fields `isPinned`, `isFavorited`, `is_archived`. An edit with no `sealed` changes no body. `sealed` of `\"\"` says the body is now empty: with no attachment left the note is deleted, which is upstream's rule; the Server cannot see emptiness inside an envelope. A plaintext `content` part is refused with 400.",
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "sealed": {
                    "type": "string",
                    "description": "The `v1.` envelope of the new body, or `\"\"` for an empty body (which deletes a note with no attachments)."
                  },
                  "tags": {
                    "type": "string",
                    "description": "A JSON array of tag names; absent leaves the note's tags alone."
                  },
                  "pics": {
                    "type": "string",
                    "description": "A JSON array of image URLs; absent leaves the column alone."
                  },
                  "tokens": {
                    "type": "string",
                    "description": "A JSON array of search tokens for the new body. Absent leaves the old index rows in place, which is only right when the body did not change."
                  },
                  "filesToDelete": {
                    "type": "string",
                    "description": "A JSON array of attachment ids, as multipart parts are strings."
                  },
                  "file": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    }
                  },
                  "update_timestamp": {
                    "type": "string",
                    "enum": [
                      "true",
                      "false"
                    ],
                    "description": "`false` keeps the old `updated_at`."
                  },
                  "isPinned": {
                    "type": "string",
                    "enum": [
                      "true",
                      "false"
                    ]
                  },
                  "isFavorited": {
                    "type": "string",
                    "enum": [
                      "true",
                      "false"
                    ]
                  },
                  "is_archived": {
                    "type": "string",
                    "enum": [
                      "true",
                      "false"
                    ]
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteNote",
        "summary": "Delete a note and its attachments",
        "tags": [
          "notes"
        ],
        "responses": {
          "204": {
            "description": "Deleted. Nothing in the body."
          },
          "400": {
            "description": "The id is not a whole number.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The note is not this account's, and for this caller it does not exist.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The note's id, or the doc node's uuid.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/_/api/v1/notes/{id}/share": {
      "post": {
        "operationId": "shareNote",
        "summary": "Share a note to a public address",
        "tags": [
          "sharing"
        ],
        "responses": {
          "200": {
            "description": "The link: the same one a second share returns, and no shorter a life than before.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShareLink"
                }
              }
            }
          },
          "400": {
            "description": "The id is not a whole number.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The note is not this account's.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The note's body is sealed and a public link carries no key to open it, so no link was created.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The note's id, or the doc node's uuid.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Optional; a share with no body gets the default hour.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "expirationTtl": {
                    "type": "integer",
                    "description": "Seconds the link lives. Defaults to 3600; zero means it never lapses."
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "revokeNoteShare",
        "summary": "Revoke a note's public link",
        "tags": [
          "sharing"
        ],
        "responses": {
          "200": {
            "description": "Revoked, whether or not a link was live.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "success"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The id is not a whole number.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The note is not this account's.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The note's id, or the doc node's uuid.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/_/api/v1/public/note/raw/{public_id}": {
      "get": {
        "operationId": "getPublicNoteRaw",
        "summary": "Read a shared note's text, as plain text",
        "tags": [
          "public"
        ],
        "responses": {
          "200": {
            "description": "The note's text, exactly as written.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No live share answers to this id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The note's body is sealed, and a public link carries no key to open it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "public_id",
            "in": "path",
            "required": true,
            "description": "The uuid the share link carries.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": []
      }
    },
    "/_/api/v1/public/note/{public_id}": {
      "get": {
        "operationId": "getPublicNote",
        "summary": "Read a shared note by its public id",
        "tags": [
          "public"
        ],
        "responses": {
          "200": {
            "description": "The shared note's text. Its attachments are not shared.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicNote"
                }
              }
            }
          },
          "404": {
            "description": "No live share answers to this id: it was never shared, it was revoked, or it lapsed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The note's body is sealed, and a public link carries no key to open it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "public_id",
            "in": "path",
            "required": true,
            "description": "The uuid the share link carries. The address IS the capability: no principal is asked for.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": []
      }
    },
    "/_/api/v1/search": {
      "get": {
        "operationId": "searchNotes",
        "summary": "Search the account's notes",
        "tags": [
          "notes"
        ],
        "responses": {
          "200": {
            "description": "One page of matching notes, in the list's own order: an index of HMACs cannot rank.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotePage"
                }
              }
            }
          },
          "400": {
            "description": "`q` is a word, not a search token. Tokenise the query with the account's search key in the client.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "A search token: the HMAC the account's search key made of one normalised word. Not a word — a word is refused with 400, because this store holds no search key and would answer an empty page for a note that is there. Repeat `q` to require every token. Empty falls back to the listing.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "1-based page of ten notes. A page below one is read as one.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "tag",
            "in": "query",
            "description": "Only notes carrying this tag, lowercased by the writer.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "favorites",
            "in": "query",
            "description": "`true` limits the answer to favorited notes.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "startTimestamp",
            "in": "query",
            "description": "Unix seconds. Used together with `endTimestamp`; alone it is ignored.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "endTimestamp",
            "in": "query",
            "description": "Unix seconds, exclusive. Used together with `startTimestamp`.",
            "schema": {
              "type": "integer"
            }
          }
        ]
      }
    },
    "/_/api/v1/settings": {
      "get": {
        "operationId": "getSettings",
        "summary": "Read the account's settings document",
        "tags": [
          "settings"
        ],
        "responses": {
          "200": {
            "description": "The stored document, or the defaults when the account has never saved one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Settings"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "putSettings",
        "summary": "Replace the account's settings document",
        "tags": [
          "settings"
        ],
        "responses": {
          "200": {
            "description": "Saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The body is not a JSON object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "description": "The settings document.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true,
                "description": "The whole document. It is stored as sent, so a partial send drops the keys it omits."
              }
            }
          }
        }
      }
    },
    "/_/api/v1/stats": {
      "get": {
        "operationId": "noteStats",
        "summary": "Note, tag and age counts for the account",
        "tags": [
          "notes"
        ],
        "responses": {
          "200": {
            "description": "Three counts over the account's own rows.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Stats"
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/_/api/v1/tags": {
      "get": {
        "operationId": "listTags",
        "summary": "The account's tags, most used first",
        "tags": [
          "notes"
        ],
        "responses": {
          "200": {
            "description": "One page of the tags this account has used, most used first, then by name.",
            "headers": {
              "x-next-cursor": {
                "description": "Present when another page exists: the `cursor` to send for it. Absent on the last page.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TagUsage"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No door session, or a session the door could not name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The door did not answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many rows this page carries. Default 100 for tags, 200 for the docs tree; the most a page returns is 500.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The `x-next-cursor` value from the previous page. A keyset cursor, not an offset, so a write during the walk neither repeats nor skips a row.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    }
  },
  "x-surface": {
    "app": "zpe-notes",
    "base": "/_/api/v1",
    "document": "/_/api/v1/openapi.json",
    "generated_by": "src/spec.ts buildDocument"
  },
  "x-zpe-routes": 26,
  "x-zpe-documented": 25,
  "x-zpe-internal": [
    "GET /_/api/v1/openapi.json"
  ]
}
