Mikipage API

The Mikipage REST API is the complete feature set of the product. Everything you can do on Mikipage — create and search notes, build AI pages, manage groups, publish to the web, run AI — is available here.

The other ways to use Mikipage are deliberate subsets of this API:

  • Web is an opinionated, minimalistic surface for everyday use. It makes choices for you and hides advanced options.
  • MCP is a curated subset aimed at AI power users. Some operations are intentionally withheld (deleting notes and pages, account settings, group and member management, file bytes, comments) to keep AI usage safe and cheap. Deletion in particular stays off MCP because it is irreversible and easy for an AI agent to apply in bulk — delete from the Web or this API instead. See [1].

Anything that exists on Web or MCP is expressible through the API, with three deliberate exceptions: anonymous public content (the landing page, /docs, published pages), the chat product itself, and credentials, plan changes and purchases. Those are Web-only by decision, and the list is closed. Everything else lives here, unopinionated and with the full set of options.

Python SDK

We provide an official Python SDK in the open-source miki_client library. It handles authentication (including the OAuth browser flow, token caching, and automatic refresh), pagination, file uploads, and the presigned-download flow for you.

miki_client currently lives in a private GitHub repository and will be published publicly (PyPI + open source) at a later date. The examples below show both the SDK and the raw HTTP calls, so you can integrate in any language today.

from miki_client import MikiClient

# OAuth (default): opens a browser to log in, then caches + auto-refreshes tokens.
client = MikiClient()

note = client.create_note("# Shopping list\n- milk\n- eggs")
print(note["id"])

Base URL and conventions

  • Base URL: https://api.mikipage.com — there is no version prefix and no path prefix. GET https://api.mikipage.com/notes lists your notes.

  • Two hostnames. The API is api.mikipage.com. Sign-in is not: the OAuth endpoints, the /.well-known/ discovery documents and the OpenAPI document all live on https://mikipage.com. You need both, and neither is derivable from the other — do not assume a shared domain.

  • JSON everywhere: request and response bodies are JSON with snake_case field names.

  • Partial updates use PATCH — send only the fields you want to change.

  • List responses are enveloped:

    {
      "items": [ /* ... */ ],
      "next_cursor": "opaque-string-or-null",
      "has_more": false
    }
    

    To page, pass next_cursor back as the cursor query parameter until has_more is false.

  • Timestamps are RFC 3339 strings (e.g. 2026-07-23T18:04:00Z).

  • Errors use RFC 9457 application/problem+json. Every error body carries a stable machine-readable code you can switch on — never string-match the human detail:

    {
      "type": "about:blank",
      "title": "Not Found",
      "status": 404,
      "code": "not_found",
      "detail": "Note not found",
      "request_id": "req_..."
    }
    

    Common codes: invalid_request (400), unauthenticated / invalid_token (401), insufficient_credits (402), permission_denied / insufficient_scope / quota_exceeded (403), not_found (404), conflict (409), content_too_large (413), rate_limited (429), internal (500), unavailable (503). A missing resource and one you're not allowed to see return an identical not_found body — there are no existence leaks.

  • OpenAPI: the machine-readable spec is served at GET https://mikipage.com/openapi.json (OpenAPI 3.1), and rendered as the interactive API reference. It is the source of truth for the wire contract: the request and response types both our TypeScript server and the Python SDK are checked against are generated from it, so a shape documented there is one the code actually produces. The SDK's methods themselves are hand-written.

Authentication

Mikipage uses OAuth 2.1 access tokens (Cognito-issued JWTs), verified at the edge. Send the token as a Bearer header on every request:

Authorization: Bearer <access-token>

How to get a token

  • OAuth authorization-code + PKCE (recommended for apps and interactive tools). Discover the endpoints at GET /.well-known/oauth-authorization-server:

    • authorization_endpoint: https://mikipage.com/oauth/authorize
    • token_endpoint: https://mikipage.com/oauth/token
    • registration_endpoint: https://mikipage.com/oauth/register (dynamic client registration)

    Refresh tokens are issued for long-lived CLI/SDK sessions. The Python SDK automates the whole loop — browser login, token cache (~/.mikipage/), and silent refresh.

  • Email/password (for headless / CI where a browser isn't available). Credential lifecycle belongs to Cognito, not the API — there is no login endpoint. The SDK mints tokens directly against Cognito for you:

    client = MikiClient(
        email="user@example.com",
        password="…",
        auth_mode="password",
    )
    client.login()
    

Scopes

Not enforced. A valid token grants full access to everything you can reach. Scopes are advisory documentation only — they describe the authorization model the API is designed around, and per-scope enforcement is not currently planned (GitHub #256). Do not build anything that depends on a token being restricted.

Each operation carries an x-required-scopes annotation, published in openapi.json and shown against each operation in the API reference. It records which capability an operation belongs to — roughly one family per resource: notes:*, pages:*, groups:*, runs:*, account:*, usage:read and publishing.

Comments have no scope of their own — they ride on the parent note's or page's :read / :write. Every endpoint requires a token; there is no anonymous namespace.

Quickstart

Create a note

A note is just Markdown content. Mikipage indexes it for AI search and page-matching automatically.

SDK

note = client.create_note(
    "# Meeting notes\n\nDiscussed Q3 roadmap. #planning",
    group_ids=["<group-id>"],   # optional: share to groups on creation
)
note_id = note["id"]

HTTP

curl -X POST https://api.mikipage.com/notes \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content": "# Meeting notes\n\nDiscussed Q3 roadmap. #planning"}'

Response (Note):

{
  "id": "note_abc123",
  "content": "# Meeting notes\n\nDiscussed Q3 roadmap. #planning",
  "starred": false,
  "archived": false,
  "stale_match": false,
  "owner_id": "…",
  "state": "active",
  "created_at": "2026-07-23T18:04:00Z",
  "updated_at": "2026-07-23T18:04:00Z"
}

Retrieve a note

SDK

note = client.get_note("note_abc123")

# Pull in related data in one call:
note = client.get_note("note_abc123", expand=["groups", "pages", "attachments"])

HTTP

curl https://api.mikipage.com/notes/note_abc123?expand=groups,pages \
  -H "Authorization: Bearer $TOKEN"

You can read a note if you own it or if it's shared to a group you belong to.

A protected note ("is_protected": true) carries its secret text in protected as an encrypted Base64 envelope; content is its plaintext description. The server cannot decrypt it — a client holding the password can. [2] describes the format, and the OpenAPI reference documents how PATCH /notes/{id} protects a note.

Search notes

POST /notes/search ranks notes by cross-lingual semantic similarity. Any "quoted" substring in q becomes a strict phrase filter. It is a read — it needs only the notes:read scope — and takes a body so your query text stays out of URLs and server logs.

results = client.search_notes(q='roadmap "Q3"', tags=["planning"], limit=20)
for item in results["items"]:
    print(item["id"], item["content"][:60])

GET /notes is the plain listing, ordered by recency. It is a separate endpoint, and the search parameters are not accepted there.

API reference

Every endpoint, with its parameters, request body and response shape, is in the interactive API reference — rendered directly from the OpenAPI document, so it is never out of date with what the API actually serves.

The raw document is at GET https://mikipage.com/openapi.json if you would rather generate a client or load it into your own tooling.

Operations are grouped there under Notes, Attachments, Pages, Comments, Groups, Publishing, AI runs and Account — the same families described above.

To be implemented

The following are designed but not yet available. They will require additional backend work and are tracked for a follow-up iteration — build against the current surface for now.

Chat is not on this list and is not coming: it is a web-app feature with no published API, deliberately. Its shape is tied to the UI it was built for, and freezing that into a public contract would buy nobody anything.

  • Webhooks & events: the entire webhooks family and a server-sent-events stream (GET /events).
  • Runs: run-id-addressable reads (GET /runs/{id}), a global cross-entity runs list with cursors, POST /runs/{id}/cancel, and server runs returning a run id on creation.
  • Restore: POST /notes/{id}/restore and POST /pages/{id}/restore for soft-deleted items.
  • Pictures: PUT /me/picture and PUT /groups/{id}/picture (today, clients upload directly to storage).
  • Reliability: Idempotency-Key support and rate limiting.
  • Personal access tokens (PATs): the design is PAT-ready (reserved mikip_ prefix); OAuth + refresh tokens cover current flows. A device-authorization grant is also under evaluation for headless bootstrap.
  • List refinements: a stale= filter and expand= on list endpoints.
Today 5:49pm
Comments
Log in to comment.

No comments yet.