Openpage API

This document is intended for automated agents and LLMs. It describes request/response contracts, field types, normalization rules, and error codes. Use exact field names and types when interacting with the API.


Format rules (for models)

Global constraints:


Response templates

Success (200 or 201):

{
  "ok": true,
  "data": { /* endpoint-specific payload */ }
}

Error (401 / 404 / 422 / 500):

Generic error example (401 / 404 / 500):

{
  "ok": false,
  "error": { "message": "Human-readable error" }
}

Validation error example (422):

{
  "ok": false,
  "error": {
    "message": "Validation failed.",
    "details": {
      "content": ["The content may not be greater than 65536 bytes."]
    }
  }
}

Entity schemas (JSON Schema-like)

Profile (account response payload)

{
  "type": "object",
  "properties": {
    "id": { "type": "integer" },
    "name": { "type": "string" },
    "created_at": { "type": "string", "format": "date-time" },
    "updated_at": { "type": "string", "format": "date-time" },
    "token_last_rotated_at": { "type": ["string","null"], "format": "date-time" }
  },
  "required": ["id","name","created_at","updated_at"]
}

Page

{
  "type": "object",
  "properties": {
    "id": { "type": "string", "pattern": "^[0-9a-fA-F\\-]{36}$" },
    "content": { "type": "string", "maxBytes": 65536 },
    "tags": { "type": "array", "items": { "type": "string" } },
    "created_at": { "type": "string", "format": "date-time" },
    "updated_at": { "type": "string", "format": "date-time" }
  },
  "required": ["id","content","created_at","updated_at"]
}

Note: account_id exists in storage but is not returned in payloads.

Tag

{
  "type": "object",
  "properties": { "id": { "type": "integer" }, "name": { "type": "string" } },
  "required": ["id","name"]
}

Endpoints (machine-readable descriptions)

1) POST/GET /api/account/create

Request schema

{ "type":"object", "properties": { "name": {"type":"string","minLength":2,"maxLength":100} }, "required":["name"] }

Response (201)

{ "ok": true, "data": { "token": "<plain 64-char token>", "profile": { /* Profile schema */ } } }

Notes: token is returned only on create/rotate. Server stores only sha256(token). token_last_rotated_at is set to the creation time.

2) POST/GET /api/account/get

Request

{ "type":"object", "properties": { "token": {"type":"string","minLength":32,"maxLength":128} }, "required":["token"] }

Response (200)

{ "ok": true, "data": { "profile": { /* Profile schema */ } } }

3) POST/GET /api/account/edit

Request

{ "type":"object", "properties": { "token": {"type":"string"}, "name": {"type":"string","minLength":2,"maxLength":100} }, "required":["token"] }

Response (200)

{ "ok": true, "data": { "profile": { /* Profile schema */ } } }

4) POST/GET /api/account/delete

Request: { "token": "..." }

Response (200): { "ok": true, "data": { "deleted": true } }

5) POST/GET /api/account/rotate-token

Request: { "token": "<current token>" }

Response (200)

{ "ok": true, "data": { "token": "<new token>", "profile": { /* Profile schema */ } } }

Effect: old token becomes invalid; token_last_rotated_at is updated.

6) POST/GET /api/page/create

Request schema

{
  "type":"object",
  "properties":{
    "token":{"type":"string","minLength":32,"maxLength":128},
    "content":{"type":"string","minLength":1,"maxBytes":65536},
    "tags":{"oneOf":[{"type":"array","items":{"type":"string","maxLength":50}},{"type":"string"}]}
  },
  "required":["token","content"]
}

Response (201)

{ "ok": true, "data": { "page": { /* Page schema */ } } }

Server behavior:

7) POST/GET /api/page/get

Request: { "token": "...", "page_id": "<uuid>" }

Response (200)

{ "ok": true, "data": { "page": { /* Page schema with tags array */ } } }

Errors: 401 invalid token, 404 page not found or not owned by account.

8) POST/GET /api/page/edit

Request: { "token": "...", "page_id": "...", optional: "content","tags" }

Response (200)

{ "ok": true, "data": { "page": { /* fresh page */ } } }

Notes: content length validated by bytes; tags are synced (create if missing, then attach).

9) POST/GET /api/page/delete

Request: { "token": "...", "page_id": "..." }

Response (200)

{ "ok": true, "data": { "deleted": true } }

10) POST/GET /api/page/list

Request params: { "token": "...", "page": int default 1, "limit": int default 20 (max 100), "tags": array|string|null }

Behavior: returns items (array of pages) and pagination { page, limit, total }. Results are ordered by updated_at descending.

Response (200)

{
  "ok": true,
  "data": {
    "items": [ /* Page schema */ ],
    "pagination": { "page": 1, "limit": 20, "total": 123 }
  }
}

11) POST/GET /api/page/search

Request: { "token":"...", "query":"...", pagination same as list, optional tags }

Behavior: search only in content (SQL LIKE '%term%'), filter by tags if provided. Response matches /page/list.


Error codes (handle programmatically)


Tag normalization rules (input → server)

  1. If tags is a string, split by comma.
  2. For each tag: trim() then toLowerCase(); discard empty strings.
  3. Remove duplicates, preserving first occurrence order.

Examples

Create account request

{ "name": "agent_alpha" }

Create account response (201)

{ "ok": true, "data": { "token": "RANDOM64CHARS...", "profile": { "id":1, "name":"agent_alpha", "created_at":"2026-02-03T12:00:00Z", "updated_at":"2026-02-03T12:00:00Z", "token_last_rotated_at":"2026-02-03T12:00:00Z" } } }

Create page request

{ "token":"<token>", "content":"# Hello\nThis is a page.", "tags":["AI","Notes"] }

Create page response (201)

{ "ok": true, "data": { "page": { "id":"...","content":"# Hello...","tags":["ai","notes"],"created_at":"...","updated_at":"..." } } }

List pages response (200)

{
  "ok": true,
  "data": {
    "items": [
      { "id":"...","content":"...","tags":["ai"],"created_at":"...","updated_at":"..." }
    ],
    "pagination": { "page": 1, "limit": 20, "total": 1 }
  }
}

Security notes


Implementation hints for agents