{"title":"aesthetic.computer Public API","version":"1.0","description":"Public APIs for publishing creative works anonymously to aesthetic.computer","baseURL":"https://aesthetic.computer","mcp":{"title":"MCP Server","description":"Model Context Protocol server for AI assistants (Claude, GPT-4, etc.) to interact with aesthetic.computer APIs","package":"@aesthetic.computer/mcp","install":"npx @aesthetic.computer/mcp","repository":"https://tangled.org/aesthetic.computer/core/tree/main/mcp-server","tools":[{"name":"publish_piece","description":"Publish a JavaScript piece to aesthetic.computer","input":{"source":"string","name":"string (optional)"},"output":{"code":"string","url":"string","cached":"boolean"}},{"name":"publish_kidlisp","description":"Publish KidLisp code to aesthetic.computer","input":{"source":"string"},"output":{"code":"string","url":"string","cached":"boolean"}},{"name":"publish_clock","description":"Publish a clock melody to aesthetic.computer","input":{"source":"string"},"output":{"code":"string","url":"string","cached":"boolean"}},{"name":"get_api_info","description":"Fetch the full API documentation","input":{},"output":"API documentation object"}],"resources":[{"uri":"aesthetic-computer://piece-template","description":"Starter template for a new aesthetic.computer piece with all lifecycle functions"},{"uri":"aesthetic-computer://kidlisp-reference","description":"Quick reference guide for KidLisp syntax and common functions"}],"prompts":[{"name":"create-piece","description":"Guided prompt for creating an aesthetic.computer piece","arguments":["name (required)","description (required)"]}],"configuration":{"Claude Desktop":"{\n  \"mcpServers\": {\n    \"aesthetic-computer\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@aesthetic.computer/mcp\"],\n      \"env\": {\n        \"AC_TOKEN\": \"optional-bearer-token\"\n      }\n    }\n  }\n}","Claude Code":"{\n  \"mcpServers\": {\n    \"aesthetic-computer\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@aesthetic-computer/mcp\"]\n    }\n  }\n}","Cursor":"{\n  \"mcpServers\": {\n    \"aesthetic-computer\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@aesthetic-computer/mcp\"]\n    }\n  }\n}"}},"endpoints":[{"name":"Store KidLisp Code","method":"POST","path":"/api/store-kidlisp","description":"Publish KidLisp code anonymously and get a short URL for sharing","authentication":"Optional (Bearer token for authenticated users)","requestBody":{"contentType":"application/json","schema":{"source":{"type":"string","required":true,"description":"KidLisp source code (max 50,000 characters)","example":"(wipe blue)\n(ink red)\n(box 10 10 50 50)"}}},"responseBody":{"schema":{"code":{"type":"string","description":"Short code for accessing the piece (e.g. 'abc123')"},"cached":{"type":"boolean","description":"True if code already existed (deduplication)"}}},"examples":[{"title":"Publish a KidLisp Piece","description":"Create a simple animated piece with KidLisp","curl":"curl -X POST https://aesthetic.computer/api/store-kidlisp \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"source\": \"(wipe blue)\\n(ink yellow)\\n(circle (/ w 2) (/ h 2) 100)\"\n  }'","javascript":"const response = await fetch(\"https://aesthetic.computer/api/store-kidlisp\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    source: \"(wipe blue)\\n(ink yellow)\\n(circle (/ w 2) (/ h 2) 100)\"\n  })\n});\n\nconst { code, cached } = await response.json();\nconsole.log(`View at: https://aesthetic.computer/${code}`);","python":"import requests\n\nresponse = requests.post(\n    \"https://aesthetic.computer/api/store-kidlisp\",\n    json={\n        \"source\": \"(wipe blue)\\n(ink yellow)\\n(circle (/ w 2) (/ h 2) 100)\"\n    }\n)\n\ndata = response.json()\nprint(f\"View at: https://aesthetic.computer/{data['code']}\")","response":{"status":201,"body":{"code":"xyz789","cached":false}}}]},{"name":"Store Clock Melody","method":"POST","path":"/api/store-clock","description":"Publish a clock melody string and get a pronounceable short code","authentication":"Optional (Bearer token for authenticated users)","requestBody":{"contentType":"application/json","schema":{"source":{"type":"string","required":true,"description":"Clock melody string (max 10,000 characters)","example":"c4 d4 e4 f4 g4"},"melody":{"type":"string","required":false,"description":"Legacy field name (use 'source' instead)"}}},"responseBody":{"schema":{"code":{"type":"string","description":"Pronounceable short code (e.g. 'bako', 'milu')"},"cached":{"type":"boolean","description":"True if melody already existed (deduplication)"}}},"examples":[{"title":"Publish a Clock Melody","description":"Store a musical sequence for the clock piece","curl":"curl -X POST https://aesthetic.computer/api/store-clock \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"source\": \"c4 e4 g4 c5 g4 e4 c4\"\n  }'","javascript":"const response = await fetch(\"https://aesthetic.computer/api/store-clock\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    source: \"c4 e4 g4 c5 g4 e4 c4\"\n  })\n});\n\nconst { code, cached } = await response.json();\nconsole.log(`Listen at: https://aesthetic.computer/clock~${code}`);","python":"import requests\n\nresponse = requests.post(\n    \"https://aesthetic.computer/api/store-clock\",\n    json={\n        \"source\": \"c4 e4 g4 c5 g4 e4 c4\"\n    }\n)\n\ndata = response.json()\nprint(f\"Listen at: https://aesthetic.computer/clock~{data['code']}\")","response":{"status":201,"body":{"code":"bako","cached":false}}}]},{"name":"List Chat Messages","method":"GET","path":"/api/chat-messages","description":"Read recent messages from a chat channel. `system` backs the main `/chat` piece; `clock` backs `laer-klokken` (r8Dio). Results are chronological (oldest → newest) within each page. Paginate further back with `before`.","authentication":"None (public read)","queryParameters":{"instance":{"type":"string","enum":["system","clock"],"default":"system","description":"Chat channel to read. `system` = main chat, `clock` = laer-klokken."},"limit":{"type":"number","default":50,"max":100,"description":"How many messages to return. Values over 100 return HTTP 400."},"before":{"type":"string","required":false,"description":"ISO-8601 timestamp. Returns messages strictly older than this — pass the `nextBefore` from the previous response to page back."}},"responseBody":{"schema":{"instance":{"type":"string","description":"Echoes the queried channel."},"count":{"type":"number","description":"Number of messages in this page."},"messages":{"type":"array","description":"Chronological (oldest → newest).","items":{"id":{"type":"string","description":"Mongo ObjectId string."},"from":{"type":"string","description":"`@handle` of the sender, or `anon` if unresolved."},"text":{"type":"string","description":"Message body as posted."},"when":{"type":"string","description":"ISO timestamp."},"hearts":{"type":"number","description":"Heart-reaction count from the shared `hearts` collection."}}},"nextBefore":{"type":"string","description":"ISO timestamp of the oldest message in this page — pass as `before=` to fetch the previous page. `null` when the page is empty."}}},"examples":[{"title":"Latest 50 messages from the main chat","description":"Default channel is `system`.","curl":"curl \"https://aesthetic.computer/api/chat-messages\"","javascript":"const res = await fetch(\"https://aesthetic.computer/api/chat-messages\");\nconst { messages } = await res.json();\nfor (const m of messages) console.log(m.when, m.from, m.text);","python":"import requests\n\ndata = requests.get(\"https://aesthetic.computer/api/chat-messages\").json()\nfor m in data[\"messages\"]:\n    print(m[\"when\"], m[\"from\"], m[\"text\"])"},{"title":"Latest 100 from the laer-klokken (clock) channel","curl":"curl \"https://aesthetic.computer/api/chat-messages?instance=clock&limit=100\"","javascript":"const res = await fetch(\n  \"https://aesthetic.computer/api/chat-messages?instance=clock&limit=100\"\n);\nconst { messages, nextBefore } = await res.json();\nconsole.log(messages.length, \"messages; older page cursor:\", nextBefore);"},{"title":"Paginate back through older messages","description":"Use `nextBefore` from each response to walk back in time.","javascript":"async function* allClockMessages() {\n  let before;\n  while (true) {\n    const url = new URL(\"https://aesthetic.computer/api/chat-messages\");\n    url.searchParams.set(\"instance\", \"clock\");\n    url.searchParams.set(\"limit\", \"100\");\n    if (before) url.searchParams.set(\"before\", before);\n    const res = await fetch(url);\n    const page = await res.json();\n    if (page.count === 0) break;\n    yield page.messages;\n    before = page.nextBefore;\n  }\n}","python":"import requests\n\ndef all_clock_messages():\n    before = None\n    while True:\n        params = {\"instance\": \"clock\", \"limit\": 100}\n        if before:\n            params[\"before\"] = before\n        page = requests.get(\"https://aesthetic.computer/api/chat-messages\",\n                            params=params).json()\n        if page[\"count\"] == 0:\n            break\n        yield page[\"messages\"]\n        before = page[\"nextBefore\"]"}],"notes":["Responses are cached in Redis for 2 minutes, keyed on (instance, limit, before).","`from` falls back to `anon` when a message's author has no resolved `@handle`.","`hearts` comes from the shared `hearts` collection (`type: chat-<instance>`)."]},{"name":"Store JavaScript Piece","method":"POST","path":"/api/store-piece","description":"Publish a JavaScript piece (.mjs) anonymously by providing source code as a string. No S3 credentials needed - the server handles storage automatically.","authentication":"Optional (Bearer token for authenticated users)","requestBody":{"contentType":"application/json","schema":{"source":{"type":"string","required":true,"description":"JavaScript piece source code (max 100,000 characters). Must contain at least one lifecycle function export.","example":"export function boot($) { $.wipe('blue'); }\nexport function paint($) { $.ink('red'); $.box(10, 10, 50, 50); }"},"name":{"type":"string","required":false,"description":"Optional name for the piece (used for code generation)"}}},"responseBody":{"schema":{"code":{"type":"string","description":"Short code for accessing the piece (e.g. 'drift', 'wave')"},"cached":{"type":"boolean","description":"True if code already existed (deduplication)"},"url":{"type":"string","description":"Full URL to view the piece"}}},"examples":[{"title":"Publish a Simple Piece","description":"Create a piece with basic drawing","curl":"curl -X POST https://aesthetic.computer/api/store-piece \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"source\": \"export function boot($) {\\n  $.wipe(\"blue\");\\n}\\n\\nexport function paint($) {\\n  $.ink(\"red\");\\n  $.box(10, 10, 50, 50);\\n}\",\n    \"name\": \"red-box\"\n  }'","javascript":"const source = `export function boot($) {\n  $.wipe(\"blue\");\n}\n\nexport function paint($) {\n  $.ink(\"red\");\n  $.box(10, 10, 50, 50);\n}`;\n\nconst response = await fetch(\"https://aesthetic.computer/api/store-piece\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    source,\n    name: \"red-box\"\n  })\n});\n\nconst { code, url, cached } = await response.json();\nconsole.log(`View at: ${url}`);","python":"import requests\n\nsource = \"\"\"export function boot($) {\n  $.wipe(\"blue\");\n}\n\nexport function paint($) {\n  $.ink(\"red\");\n  $.box(10, 10, 50, 50);\n}\"\"\"\n\nresponse = requests.post(\n    \"https://aesthetic.computer/api/store-piece\",\n    json={\n        \"source\": source,\n        \"name\": \"red-box\"\n    }\n)\n\ndata = response.json()\nprint(f\"View at: {data['url']}\")","response":{"status":201,"body":{"code":"red-box","cached":false,"url":"https://aesthetic.computer/red-box"}}},{"title":"Publish Interactive Piece","description":"Create a piece with user interaction","curl":"curl -X POST https://aesthetic.computer/api/store-piece \\\n  -H \"Content-Type: \"application/json\" \\\n  -d '{\n    \"source\": \"let x = 0;\\n\\nexport function boot($) {\\n  x = $.screen.width / 2;\\n}\\n\\nexport function paint($) {\\n  $.wipe(\"black\");\\n  $.ink(\"yellow\");\\n  $.circle(x, $.screen.height / 2, 20);\\n}\\n\\nexport function act($) {\\n  if ($.event.is(\"touch\")) x = $.event.x;\\n}\"\n  }'","javascript":"const source = `let x = 0;\n\nexport function boot($) {\n  x = $.screen.width / 2;\n}\n\nexport function paint($) {\n  $.wipe(\"black\");\n  $.ink(\"yellow\");\n  $.circle(x, $.screen.height / 2, 20);\n}\n\nexport function act($) {\n  if ($.event.is(\"touch\")) x = $.event.x;\n}`;\n\nconst response = await fetch(\"https://aesthetic.computer/api/store-piece\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({ source })\n});\n\nconst { code, url } = await response.json();\nconsole.log(`View at: ${url}`);","python":"import requests\n\nsource = \"\"\"let x = 0;\n\nexport function boot($) {\n  x = $.screen.width / 2;\n}\n\nexport function paint($) {\n  $.wipe(\"black\");\n  $.ink(\"yellow\");\n  $.circle(x, $.screen.height / 2, 20);\n}\n\nexport function act($) {\n  if ($.event.is(\"touch\")) x = $.event.x;\n}\"\"\"\n\nresponse = requests.post(\n    \"https://aesthetic.computer/api/store-piece\",\n    json={\"source\": source}\n)\n\ndata = response.json()\nprint(f\"View at: {data['url']}\")","response":{"status":201,"body":{"code":"touch","cached":false,"url":"https://aesthetic.computer/touch"}}}]},{"name":"Track Media (Publish Artwork)","method":"POST","path":"/api/track-media","description":"Publish a painting (PNG), JavaScript piece (MJS), or recording tape (ZIP) anonymously. Note: Files must be uploaded to S3/storage before calling this endpoint.","authentication":"Optional (Bearer token for authenticated users)","requestBody":{"contentType":"application/json","schema":{"slug":{"type":"string","required":true,"description":"S3/storage path where the file was uploaded"},"ext":{"type":"string","required":true,"enum":["png","mjs","zip"],"description":"File extension: 'png' for paintings, 'mjs' for JavaScript pieces, 'zip' for tapes"},"metadata":{"type":"object","required":false,"description":"Optional metadata (for tapes: totalDuration in seconds, max 30s)","properties":{"totalDuration":{"type":"number","description":"Duration in seconds (tapes only, max 30)"}}}}},"responseBody":{"schema":{"code":{"type":"string","description":"Short code for accessing the media"}}},"examples":[{"title":"Publish a JavaScript Piece","description":"After uploading .mjs file to S3, register it in the database","curl":"curl -X POST https://aesthetic.computer/api/track-media \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"slug\": \"2026/02/12/my-piece.mjs\",\n    \"ext\": \"mjs\"\n  }'","javascript":"// Step 1: Upload your .mjs file to S3 (requires credentials)\n// Step 2: Register the uploaded file\nconst response = await fetch(\"https://aesthetic.computer/api/track-media\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    slug: \"2026/02/12/my-piece.mjs\",\n    ext: \"mjs\"\n  })\n});\n\nconst { code } = await response.json();\nconsole.log(`View at: https://aesthetic.computer/${code}`);","python":"import requests\n\n# After uploading your .mjs file to S3\nresponse = requests.post(\n    \"https://aesthetic.computer/api/track-media\",\n    json={\n        \"slug\": \"2026/02/12/my-piece.mjs\",\n        \"ext\": \"mjs\"\n    }\n)\n\ndata = response.json()\nprint(f\"View at: https://aesthetic.computer/{data['code']}\")","response":{"status":200,"body":{"code":"abc456"}}},{"title":"Publish a Painting (PNG)","description":"Register a painting image after uploading to S3","curl":"curl -X POST https://aesthetic.computer/api/track-media \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"slug\": \"2026/02/12/my-painting.png\",\n    \"ext\": \"png\"\n  }'","javascript":"const response = await fetch(\"https://aesthetic.computer/api/track-media\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    slug: \"2026/02/12/my-painting.png\",\n    ext: \"png\"\n  })\n});\n\nconst { code } = await response.json();\nconsole.log(`View at: https://aesthetic.computer/${code}`);","python":"import requests\n\nresponse = requests.post(\n    \"https://aesthetic.computer/api/track-media\",\n    json={\n        \"slug\": \"2026/02/12/my-painting.png\",\n        \"ext\": \"png\"\n    }\n)\n\ndata = response.json()\nprint(f\"View at: https://aesthetic.computer/{data['code']}\")","response":{"status":200,"body":{"code":"def789"}}},{"title":"Publish a Recording Tape (ZIP)","description":"Register a recording after uploading ZIP to S3","curl":"curl -X POST https://aesthetic.computer/api/track-media \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"slug\": \"2026/02/12/my-recording.zip\",\n    \"ext\": \"zip\",\n    \"metadata\": {\n      \"totalDuration\": 15.5\n    }\n  }'","javascript":"const response = await fetch(\"https://aesthetic.computer/api/track-media\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    slug: \"2026/02/12/my-recording.zip\",\n    ext: \"zip\",\n    metadata: {\n      totalDuration: 15.5  // seconds (max 30)\n    }\n  })\n});\n\nconst { code } = await response.json();\nconsole.log(`Watch at: https://aesthetic.computer/${code}`);","python":"import requests\n\nresponse = requests.post(\n    \"https://aesthetic.computer/api/track-media\",\n    json={\n        \"slug\": \"2026/02/12/my-recording.zip\",\n        \"ext\": \"zip\",\n        \"metadata\": {\n            \"totalDuration\": 15.5  # seconds (max 30)\n        }\n    }\n)\n\ndata = response.json()\nprint(f\"Watch at: https://aesthetic.computer/{data['code']}\")","response":{"status":200,"body":{"code":"ghi012"}}}]}],"notes":["✨ All endpoints support anonymous (guest) publishing without authentication","🔑 To associate uploads with your account, include a Bearer token in the Authorization header","🎨 KidLisp is a creative coding language - visit https://kidlisp.com for documentation","💾 /api/store-piece handles storage automatically - no S3 credentials needed","📦 For /api/track-media: Files must be uploaded to S3/storage first (contact admins for credentials)","📏 Maximum source code lengths: KidLisp 50,000 chars, JavaScript pieces 100,000 chars","⏱️ Maximum clock melody length: 10,000 characters","🎬 Maximum tape duration: 30 seconds","♻️ Duplicate content is automatically deduplicated (same content returns same code)","🔧 JavaScript pieces must export at least one lifecycle function: boot, paint, sim, or act"],"relatedResources":[{"name":"KidLisp Documentation","url":"https://kidlisp.com"},{"name":"aesthetic.computer Main Site","url":"https://aesthetic.computer"}]}