API Documentation

common.ink provides a RESTful API for programmatic access to your notes, along with MCP (Model Context Protocol) support for AI agent integration.

Table of Contents


Authentication

common.ink supports three authentication methods:

Session Cookies

Session cookies are automatically set when you log in via the web interface. They’re valid for 30 days and use the session_id cookie name with HttpOnly and SameSite=Lax settings.

API Keys

API Keys are the recommended way to authenticate API requests programmatically:

  1. Go to API Keys to create a new key
  2. Include the key in the Authorization header:
curl -H "Authorization: Bearer agentnotes_key_user-xxx_token..." \
  https://common.ink/api/notes

API Key Format: agentnotes_key_{user_id}_{random_token}

OAuth 2.1

For third-party applications, use OAuth 2.1 with PKCE. See the OAuth 2.1 Provider section below.


User Registration & Login

Register with Email/Password

Endpoint: POST /auth/register

Content-Type: application/x-www-form-urlencoded

Field Type Required Description
email string Yes Email address
password string Yes Password (min 8 characters)
# Register a new account (saves session cookie)
curl -X POST https://common.ink/auth/register \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "email=user@example.com&password=SecurePass123" \
  -c cookies.txt

Response: 303 redirect to /notes with session cookie set in cookies.txt.


Login with Email/Password

Endpoint: POST /auth/login

Content-Type: application/x-www-form-urlencoded

Field Type Required Description
email string Yes Email address
password string Yes Password
# Login to existing account (saves session cookie)
curl -X POST https://common.ink/auth/login \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "email=user@example.com&password=SecurePass123" \
  -c cookies.txt

Response: 303 redirect to /notes with session cookie set in cookies.txt.


Google OIDC Sign-In

Endpoint: GET /auth/google

# Opens Google OAuth flow (use in browser)
open "https://common.ink/auth/google"

After Google authentication, the callback at /auth/google/callback sets the session cookie and redirects to /notes.


Request Magic Link:

Endpoint: POST /auth/magic

curl -X POST https://common.ink/auth/magic \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "email=user@example.com"

Response: 303 redirect to /login?magic=sent. A magic link is sent to the email. Clicking it verifies the token at /auth/magic/verify?token=... and logs you in.


Password Reset

Request Reset:

Endpoint: POST /auth/password-reset

curl -X POST https://common.ink/auth/password-reset \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "email=user@example.com"

Confirm Reset:

Endpoint: POST /auth/password-reset-confirm

Field Type Required Description
token string Yes Reset token from email
new_password string Yes New password

Check Authentication Status

Endpoint: GET /auth/whoami

# With session cookie
curl https://common.ink/auth/whoami -b cookies.txt

Note: The /auth/whoami endpoint only supports session cookie authentication. For programmatic auth status checks, use the Notes API (e.g., GET /api/notes) with your API Key or OAuth token – a 200 response confirms valid authentication.

Response:

{
  "user_id": "user-550e8400-e29b-41d4-a716-446655440000",
  "authenticated": true
}

Logout

Endpoint: POST /auth/logout or GET /auth/logout

curl -X POST https://common.ink/auth/logout \
  -b cookies.txt -c cookies.txt

Response: 303 redirect to /. The session cookie is cleared in cookies.txt.


API Keys API

API Keys enable programmatic API access without session cookies. Ideal for:

  • CLI tools and scripts
  • CI/CD pipelines
  • AI assistant integrations (Claude Code, ChatGPT)
  • MCP server connections

Create an API Key

Creates a new API Key. Requires password re-authentication.

Note: You can also create API Keys through the web UI at /api-keys/new, which is the recommended method.

Endpoint: POST /api/keys

Authentication: Session cookie required

Content-Type: application/json

{
  "name": "CLI Access Token",
  "scope": "read_write",
  "expires_in": 2592000,
  "email": "user@example.com",
  "password": "SecurePass123"
}
Field Type Required Description
name string Yes Descriptive name for the token
scope string No Permission scope (default: read_write)
expires_in integer No Seconds until expiry (default/max: 1 year)
email string Yes Your email for re-authentication
password string Yes Your password for re-authentication
# Create an API Key (requires session cookie from login)
curl -X POST https://common.ink/api/keys \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{
    "name": "My CLI Token",
    "scope": "read_write",
    "expires_in": 2592000,
    "email": "user@example.com",
    "password": "SecurePass123"
  }'

Response (201 Created):

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "My CLI Token",
  "token": "agentnotes_key_user-550e8400-e29b-41d4-a716-446655440000_YWJjZGVm...",
  "scope": "read_write",
  "expires_at": "2026-03-03T12:00:00Z",
  "created_at": "2026-02-03T12:00:00Z"
}

Important: Save the token value securely - it cannot be retrieved later!


List API Keys

Endpoint: GET /api/keys

curl https://common.ink/api/keys -b cookies.txt

Response:

{
  "tokens": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "name": "My CLI Token",
      "scope": "read_write",
      "expires_at": "2026-03-03T12:00:00Z",
      "created_at": "2026-02-03T12:00:00Z",
      "last_used_at": "2026-02-03T14:30:00Z"
    }
  ]
}

Revoke an API Key

Endpoint: DELETE /api/keys/{id}

curl -X DELETE https://common.ink/api/keys/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -b cookies.txt

Response: 204 No Content


Notes API

All notes endpoints require authentication via session cookie, API Key, or OAuth JWT.

Base URL

https://common.ink/api

List Notes

Endpoint: GET /api/notes

Parameter Type Default Description
limit integer 50 Max notes to return (max: 1000)
offset integer 0 Number of notes to skip
# With session cookie
curl "https://common.ink/api/notes?limit=10&offset=0" -b cookies.txt

# With API Key
curl "https://common.ink/api/notes?limit=10&offset=0" \
  -H "Authorization: Bearer agentnotes_key_user-xxx_token..."

Response:

{
  "notes": [
    {
      "id": "b3f1a2c4-5d6e-7f89-0abc-def123456789",
      "title": "My Note",
      "content": "Note content here...",
      "is_public": false,
      "created_at": "2026-01-15T10:30:00Z",
      "updated_at": "2026-01-15T10:30:00Z"
    }
  ],
  "total_count": 42,
  "limit": 10,
  "offset": 0
}

Create Note

Endpoint: POST /api/notes

Content-Type: application/json

Field Type Required Description
title string Yes Note title
content string No Note content (markdown supported)
curl -X POST https://common.ink/api/notes \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer agentnotes_key_user-xxx_token..." \
  -d '{
    "title": "Meeting Notes",
    "content": "## Agenda\n- Item 1\n- Item 2"
  }'

Response (201 Created):

{
  "id": "c4e2d1a3-6b7f-8901-2345-abcdef678901",
  "title": "Meeting Notes",
  "content": "## Agenda\n- Item 1\n- Item 2",
  "is_public": false,
  "created_at": "2026-01-15T10:35:00Z",
  "updated_at": "2026-01-15T10:35:00Z"
}

Get Note

Endpoint: GET /api/notes/{id}

curl https://common.ink/api/notes/c4e2d1a3-6b7f-8901-2345-abcdef678901 \
  -H "Authorization: Bearer agentnotes_key_user-xxx_token..."

Response: Same format as Create Note response.


Update Note

Endpoint: PUT /api/notes/{id}

Content-Type: application/json

Field Type Required Description
title string No New title
content string No New content
curl -X PUT https://common.ink/api/notes/c4e2d1a3-6b7f-8901-2345-abcdef678901 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer agentnotes_key_user-xxx_token..." \
  -d '{
    "title": "Updated Title",
    "content": "Updated content..."
  }'

Delete Note

Endpoint: DELETE /api/notes/{id}

curl -X DELETE https://common.ink/api/notes/c4e2d1a3-6b7f-8901-2345-abcdef678901 \
  -H "Authorization: Bearer agentnotes_key_user-xxx_token..."

Response: 204 No Content


Search Notes

Endpoint: POST /api/notes/search

Content-Type: application/json

Field Type Required Description
query string Yes Search query (FTS5 syntax supported)
curl -X POST https://common.ink/api/notes/search \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer agentnotes_key_user-xxx_token..." \
  -d '{"query": "meeting agenda"}'

Response:

{
  "results": [
    {
      "note": {
        "id": "c4e2d1a3-6b7f-8901-2345-abcdef678901",
        "title": "Meeting Notes",
        "content": "## Agenda\n- Goal setting",
        "is_public": false,
        "created_at": "2026-01-15T10:35:00Z",
        "updated_at": "2026-01-15T10:35:00Z"
      },
      "rank": -1.5
    }
  ],
  "query": "meeting agenda",
  "total_count": 1
}

Note: Uses FTS5 full-text search. Lower (more negative) rank values indicate better matches. Supports standard SQLite FTS5 query syntax.


MCP (Model Context Protocol)

common.ink implements the MCP 2025-03-26 specification using Streamable HTTP transport.

Endpoint

POST /mcp

Headers

Header Value Required
Content-Type application/json Yes
Accept application/json, text/event-stream Yes
Authorization Bearer {token} Yes

Available Tools

Tool Description
note_list List notes with pagination support
note_view Retrieve a single note by its ID
note_create Create a new note with a title and optional content
note_update Update an existing note’s title and/or content
note_search Search notes using full-text search (FTS5)
note_delete Delete a note by its ID

List Available Tools

curl -X POST https://common.ink/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer agentnotes_key_user-xxx_token..." \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "note_create",
        "description": "Create a new note with a title and optional content",
        "inputSchema": {
          "type": "object",
          "properties": {
            "title": {"type": "string", "description": "The title of the note (required)"},
            "content": {"type": "string", "description": "The content/body of the note (optional)"}
          },
          "required": ["title"]
        }
      },
      {
        "name": "note_delete",
        "description": "Delete a note by its ID",
        "inputSchema": {
          "type": "object",
          "properties": {
            "id": {"type": "string", "description": "The unique identifier of the note to delete"}
          },
          "required": ["id"]
        }
      },
      {
        "name": "note_list",
        "description": "List notes with pagination support",
        "inputSchema": {
          "type": "object",
          "properties": {
            "limit": {"type": "integer", "description": "Maximum number of notes to return (default: 50, max: 1000)"},
            "offset": {"type": "integer", "description": "Number of notes to skip for pagination (default: 0)"}
          }
        }
      },
      {
        "name": "note_search",
        "description": "Search notes using full-text search (FTS5)",
        "inputSchema": {
          "type": "object",
          "properties": {
            "query": {"type": "string", "description": "The search query to match against note titles and content"}
          },
          "required": ["query"]
        }
      },
      {
        "name": "note_update",
        "description": "Update an existing note's title and/or content",
        "inputSchema": {
          "type": "object",
          "properties": {
            "id": {"type": "string", "description": "The unique identifier of the note to update"},
            "title": {"type": "string", "description": "The new title for the note (optional)"},
            "content": {"type": "string", "description": "The new content for the note (optional)"}
          },
          "required": ["id"]
        }
      },
      {
        "name": "note_view",
        "description": "Retrieve a single note by its ID",
        "inputSchema": {
          "type": "object",
          "properties": {
            "id": {"type": "string", "description": "The unique identifier of the note to retrieve"}
          },
          "required": ["id"]
        }
      }
    ]
  }
}

Create Note via MCP

curl -X POST https://common.ink/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer agentnotes_key_user-xxx_token..." \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "note_create",
      "arguments": {
        "title": "Created via MCP",
        "content": "This note was created by an AI assistant."
      }
    },
    "id": 2
  }'

Search Notes via MCP

curl -X POST https://common.ink/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer agentnotes_key_user-xxx_token..." \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "note_search",
      "arguments": {
        "query": "meeting"
      }
    },
    "id": 3
  }'

List Notes via MCP

curl -X POST https://common.ink/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer agentnotes_key_user-xxx_token..." \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "note_list",
      "arguments": {
        "limit": 10,
        "offset": 0
      }
    },
    "id": 4
  }'

Connecting AI Assistants

Claude Code

Add to your Claude Code MCP configuration (~/.claude/mcp.json):

{
  "mcpServers": {
    "common-ink": {
      "url": "https://common.ink/mcp",
      "transport": "streamable-http",
      "headers": {
        "Authorization": "Bearer agentnotes_key_user-xxx_token..."
      }
    }
  }
}

ChatGPT (Custom GPT)

  1. Create an API Key at API Keys
  2. In ChatGPT’s custom GPT settings, add an action:
    • Server URL: https://common.ink
    • Authentication: API Key (Bearer Token)
    • API Key: Your API Key

Other MCP Clients

Use the official MCP SDK for your language:


OAuth 2.1 Provider

common.ink implements an OAuth 2.1 provider for third-party integrations.

Well-Known Metadata

Endpoint Description
GET /.well-known/oauth-authorization-server Authorization server metadata
GET /.well-known/oauth-protected-resource Protected resource metadata
GET /.well-known/jwks.json JSON Web Key Set for token verification

Dynamic Client Registration

Endpoint: POST /oauth/register

Note: Redirect URIs must be in the server’s allowlist. Currently supported redirect URIs:

  • https://chatgpt.com/connector_platform_oauth_redirect (ChatGPT)
  • https://platform.openai.com/apps-manage/oauth (OpenAI platform)
  • https://claude.ai/api/mcp/auth_callback (Claude)
  • https://claude.com/api/mcp/auth_callback (Claude)
curl -X POST https://common.ink/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My AI Agent",
    "redirect_uris": ["https://claude.ai/api/mcp/auth_callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "response_types": ["code"],
    "token_endpoint_auth_method": "none"
  }'

Authorization

GET /oauth/authorize?
  response_type=code&
  client_id=CLIENT_ID&
  redirect_uri=REDIRECT_URI&
  scope=notes:read%20notes:write&
  code_challenge=PKCE_CHALLENGE&
  code_challenge_method=S256&
  state=RANDOM_STATE

Token Exchange

Endpoint: POST /oauth/token

curl -X POST https://common.ink/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code&code=AUTH_CODE&redirect_uri=REDIRECT_URI&client_id=CLIENT_ID&code_verifier=PKCE_VERIFIER"

Rate Limits

Tier Requests/Minute Burst
Free 60 100
Paid 6000 10000

Rate limit headers are included in all responses:

Header Description
X-RateLimit-Limit Maximum requests per window
X-RateLimit-Remaining Remaining requests in current window
X-RateLimit-Reset Unix timestamp when the window resets

Error Responses

All errors return JSON:

{
  "error": "Error message description"
}

HTTP Status Codes

Code Description
200 Success
201 Created
204 No Content (successful deletion)
400 Bad Request (invalid input)
401 Unauthorized (missing or invalid authentication)
403 Forbidden (insufficient permissions)
404 Not Found
429 Too Many Requests (rate limited)
500 Internal Server Error

401 Unauthorized Response

When authentication fails, the response includes a WWW-Authenticate header:

WWW-Authenticate: Bearer resource_metadata="https://common.ink/.well-known/oauth-protected-resource", error="invalid_token", error_description="Invalid API key"

Complete Workflow Example

#!/bin/bash
# Complete API workflow example

# 1. Register (or login if account exists)
curl -X POST https://common.ink/auth/register \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "email=demo@example.com&password=DemoPass123" \
  -c cookies.txt -s > /dev/null

# 2. Create an API Key for API access
API_KEY=$(curl -s -X POST https://common.ink/api/keys \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{
    "name": "Demo Token",
    "email": "demo@example.com",
    "password": "DemoPass123"
  }' | jq -r '.token')

echo "Your API Key: $API_KEY"

# 3. Create a note using the API Key
echo "Creating note..."
curl -X POST https://common.ink/api/notes \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_KEY" \
  -d '{
    "title": "API Test Note",
    "content": "Created via REST API!"
  }'

# 4. List all notes
echo -e "\n\nListing notes..."
curl https://common.ink/api/notes \
  -H "Authorization: Bearer $API_KEY"

# 5. Search notes
echo -e "\n\nSearching notes..."
curl -X POST https://common.ink/api/notes/search \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_KEY" \
  -d '{"query": "API"}'

# 6. Create note via MCP
echo -e "\n\nCreating note via MCP..."
curl -X POST https://common.ink/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer $API_KEY" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "note_create",
      "arguments": {
        "title": "MCP Test Note",
        "content": "Created via MCP!"
      }
    },
    "id": 1
  }'

Health Check

Endpoint: GET /health

No authentication required.

curl https://common.ink/health

Response:

{
  "status": "healthy",
  "service": "remote-notes",
  "milestone": 3
}

Support

For API support, contact: api-support@common.ink