๐ AIC-Web REST API Reference
Base URL: https://aicc-web.com ยท Everything below is usable with plain HTTP โ no MCP session required. AI agents and scripts welcome.
Authentication
All student endpoints (/api/me/*) accept a Bearer token in the Authorization header. Two token kinds work:
| Token | How to get it | Lifetime |
| Student API key | Portal login (username + password at POST /api/me/login), or the magic-login link | Long-lived; rotates on each password login |
| Paired-agent token | Device pairing: POST /mcp/pair โ student approves โ POST /mcp/exchange | Access token SHORT-LIVED (expires_at in the exchange response; may be non-expiring per server config) + a long-lived refresh token (rotated on each use at POST /mcp/refresh). Per-agent, revocable. |
# Get a token with username + password
curl -X POST https://aicc-web.com/api/me/login \
-H 'Content-Type: application/json' \
-d '{"username":"you","password":"..."}'
# โ {"api_key":"<64 hex chars>", ...}
# Use it on every call
curl https://aicc-web.com/api/me -H 'Authorization: Bearer <api_key>'
The same auth works for the MCP endpoints (/mcp, /sse) โ one credential, every surface.
Identity & Quotas
GET | /api/me | Your id, username, limits (apps, static sites, DBs, disk) and live usage |
GET | /api/me/runtimes | Available runtimes (node, static โ python planned) |
curl https://aicc-web.com/api/me -H 'Authorization: Bearer <key>'
# โ { "limits": {"maxApps":1,"maxStaticSites":5,...}, "usage": {"apps":0,"staticSites":0,"databases":0,"disk_mb":0} }
Apps (Node.js process apps)
Deploy
POST | /api/me/apps/draft | Reserve a BLANK app: {name, custom_slug?, agent_note?}. No code yet โ deploy into it later by the same name (any order: git, zip, database). Consumes an app slot |
POST | /api/me/apps | Deploy from a GIT repo (JSON body) โ also DEPLOYS INTO a draft when name matches |
POST | /api/me/apps/upload | Deploy from a ZIP (multipart: name, file + optional fields) โ same draft pickup by name |
PATCH | /api/me/apps/:name/agent-note | Update the standing note your AI agent sees (draft or deployed) |
Blank apps: create the slot first with a custom URL (e.g. myshop.s1.โฆ instead of myshop-you.s1.โฆ) and a note like โa shop with photo uploadsโ. Your AI agent lists your apps, sees the DRAFT + note, and deploys into that exact name โ the reserved URL and note are kept.
Common optional fields (JSON snake_case; multipart same names):
| Field | Meaning |
git_url / git_key / git_branch | Repo to clone / SSH private key for private repos (stored encrypted, reused on redeploys) / branch to track (default: repo's default) |
root_dir | Monorepo: subdirectory that contains the app (e.g. apps/web) |
build_command | Build step, e.g. npm run build (installs devDeps) |
release_command | Runs after build, before the process swap (e.g. npx prisma migrate deploy). Failure aborts the deploy; the old version keeps serving |
start_command | Defaults to package.json scripts.start |
database | auto (provision + inject DATABASE_URL), none, or an existing DB name |
env / replace_env | Env vars (JSON) / full-replace instead of merge on redeploy |
# Zip upload with migrations
curl -X POST https://aicc-web.com/api/me/apps/upload \
-H 'Authorization: Bearer <key>' \
-F name=myapp -F file=@app.zip \
-F build_command='npm run build' \
-F release_command='npx prisma migrate deploy'
# โ { "app_id":"...", "url":"https://myapp-you.s1.aicc-web.com", "state":"RUNNING", "port":3154 }
The platform auto-runs npx prisma generate when prisma/schema.prisma exists. Include your lockfile โ npm/pnpm/yarn are all installed and auto-detected.
Operate
GET | /api/me/apps | List your process apps (static sites excluded). Git URLs are sanitized โ no embedded tokens |
GET | /api/me/apps/:name | Status: state, pid, port, restarts, uptime, last exit code |
POST | /api/me/apps/:name/redeploy | Redeploy a git app from its STORED git URL + branch + commands + env. No body needed |
GET | /api/me/apps/:name/logs?lines=50&since=last_deploy | Logs โ timestamped, merged stdout+stderr; since:last_deploy scopes to the current boot |
POST | /api/me/apps/:name/stop|restart|delete | Lifecycle actions |
GET | /api/me/apps/:name/stats | Visitor stats from access logs (total, unique IPs, status codes) |
GET | /api/me/apps/:name/disk | Disk usage of the app directory (MB) |
Git settings + auto-deploy webhook
GET | /api/me/apps/:name/git-settings | Tracked branch, autoDeploy flag, webhook URL (secret never included) |
PATCH | /api/me/apps/:name/git-settings | Update {git_branch?, auto_deploy?} |
POST | /api/me/apps/:name/webhook-secret | Generate/regenerate the webhook secret โ plaintext returned once |
GET | /api/me/apps/:name/webhook-secret?reveal=true | Reveal the stored secret |
POST | /api/hooks/github/:slug | PUBLIC GitHub webhook. HMAC-verified (X-Hub-Signature-256); a matching push triggers an async redeploy. Same path works for static sites |
Deploy keys (private repos without sharing secrets)
POST | /api/me/deploy-keys | Get-or-create the platform deploy key for a repo. Body: {git_url}. Returns the PUBLIC key + paste-on-GitHub instructions |
GET | /api/me/deploy-keys | List your deploy keys (public halves only) |
DELETE | /api/me/deploy-keys/:id | Delete a deploy key (clones of that repo stop working) |
# Private repo, no secret ever shared:
curl -X POST https://aicc-web.com/api/me/deploy-keys \
-H 'Authorization: Bearer <key>' -H 'Content-Type: application/json' \
-d '{"git_url":"git@github.com:me/private-repo.git"}'
# โ { "public_key":"ssh-ed25519 AAAAโฆ aicweb-you", "instructions":"โฆ" }
# student pastes it on GitHub (Deploy keys, read-only), then:
curl -X POST https://aicc-web.com/api/me/apps \
-H 'Authorization: Bearer <key>' -H 'Content-Type: application/json' \
-d '{"name":"myapp","git_url":"git@github.com:me/private-repo.git"}'
# Redeploy from the stored repo (no source re-supplied)
curl -X POST https://aicc-web.com/api/me/apps/myapp/redeploy \
-H 'Authorization: Bearer <key>'
# โ { "app_id":"...", "url":"https://myapp-you.s1.aicc-web.com", "state":"RUNNING", "port":3154 }
Environment
GET | /api/me/apps/:name/env | Your stored env vars (managed keys listed by name only) |
PATCH | /api/me/apps/:name/env | Merge env keys and re-apply to the running process |
PUT | /api/me/apps/:name/env | Replace the whole env |
GET | /api/me/apps/:name/env/resolved?reveal=1 | Full runtime env (secrets masked unless reveal=1) |
One-off commands (no SSH needed)
POST | /api/me/apps/:name/run | Run a command in the app dir as you โ migrations, psql, seeds. DATABASE_URL injected. Body: {command, timeoutMs?} (max 300s) |
curl -X POST https://aicc-web.com/api/me/apps/myapp/run \
-H 'Authorization: Bearer <key>' -H 'Content-Type: application/json' \
-d '{"command":"npx prisma migrate deploy"}'
# โ { "exitCode":0, "stdout":"...", "stderr":"", "durationMs":2210 }
Files
GET | /api/me/apps/:name/files?path=&root=app|storage | List a directory (folders first). root=storage targets the persistent storage dir (process.env.STORAGE_PATH โ survives redeploys) |
GET | /api/me/apps/:name/files/content?path=&root= | Read a file (binary/>1MB come back non-editable) |
PUT | /api/me/apps/:name/files?path=&root= | Create/overwrite a file. Body: {content}. App-root edits need a restart; storage edits are live |
DELETE | /api/me/apps/:name/files?path=&root= | Delete a file |
Paths are server-validated to stay inside the chosen root โ traversal (../../etc/passwd) and absolute paths are rejected. Every deploy WIPES the code directory โ runtime uploads belong in STORAGE_PATH.
Custom domains
GET | /api/me/apps/:name/domains | List custom domains |
POST | /api/me/apps/:name/domains | Add one โ body {domain}. Returns the server IP for your A record; TLS is automatic |
DELETE | /api/me/apps/:name/domains/:domain | Remove one |
Static Sites
POST | /api/me/static-sites | Deploy from git (JSON: name, git_url, git_key?, git_branch?, output_dir?, spa_fallback?) |
POST | /api/me/static-sites/upload | Deploy from a ZIP (multipart: name, file, output_dir?, spa_fallback?) |
POST | /api/me/static-sites/:name/redeploy | Redeploy a git site from its stored URL + branch |
GET | /api/me/static-sites | List your static sites |
GET | /api/me/static-sites/:name | Status + root path |
GET | /api/me/static-sites/:name/disk | Disk usage (MB) |
DELETE | /api/me/static-sites/:name | Delete the site |
Files and custom domains work exactly like apps, under /api/me/static-sites/:name/files... and /domains. Saves are live instantly (no process).
Databases
POST | /api/me/databases | Create โ body {name}. Returns DATABASE_URL once |
GET | /api/me/databases | List with live size, cap, attached app, orphan flag |
GET | /api/me/databases/:name | Connection info incl. DATABASE_URL (role password decrypted on read) |
GET | /api/me/databases/:name/tables | Explorer: public-schema table list |
POST | /api/me/databases/:name/query | Explorer: read-only SQL (SELECT/SHOW/EXPLAIN/WITH). Body: {sql} |
DELETE | /api/me/databases/:name | Drop the DB + role. Refused while attached to a live app |
For an app-ATTACHED database you can also use the app-scoped explorer:
POST /api/me/apps/:name/query (same read-only SQL, same {sql} body, same {columns, rows, rowCount} response).
The query surface is read-only by design. Writes/migrations go through POST /api/me/apps/:name/run (e.g. psql "$DATABASE_URL" -c '...').
MCP (for AI agents)
MCP tools are the lightweight control plane; file transfers (zips, static files) go through the REST endpoints above.
The server exposes 21 tools:
| Group | Tools |
| Docs & discovery | deployment_guide (full reference โ call first), list_runtimes, list_apps (every app: state, URL, agent_note, git, DB โ also shows BLANK drafts the student reserved for you), list_static_sites |
| Deploy | deploy_app (git), deploy_static_files (local files โ default for static sites), deploy_static_site (git), redeploy_app (redeploy from stored git โ nothing re-supplied), delete_app, delete_static_site |
| App control | get_app_status, get_app_logs (use since:'last_deploy'), stop_app, restart_app, update_app_env, get_app_env_resolved, run_app_command |
| Databases & private repos | create_database, get_database_connection, delete_database, create_deploy_key (private repos โ public key only, no secret sharing) |
| Endpoint | Purpose |
GET | /.well-known/mcp-auth.json | Machine-readable auth spec (start here if you're an MCP client) |
POST | /mcp/pair | Start device pairing โ {agentId, code} (6 digits, reusable 120s) |
POST | /mcp/exchange | {agentId, code} โ {token, refresh_token, expires_at}. Polls with 202 until the student approves; re-call with the same code to re-mint |
POST | /mcp/refresh | {refresh_token} โ new token pair (rotated) โ do this BEFORE expires_at |
POST | /mcp | Streamable-HTTP MCP endpoint (Authorization: Bearer) |
GET | /sse | SSE MCP transport (for clients preferring SSE) + POST /messages?sessionId= |
# Typical agent flow
curl -X POST https://aicc-web.com/mcp/pair # โ code shown to the student
# student enters the code in the portal โ
curl -X POST https://aicc-web.com/mcp/exchange \
-H 'Content-Type: application/json' \
-d '{"agentId":"<id>","code":"123-456"}' # โ {token, refresh_token, expires_at}
# 401 with {"expired":true,...} โ POST /mcp/refresh with the refresh_token
Private git repos: call create_deploy_key โ the platform generates the keypair and returns the PUBLIC key for the repo owner to paste on GitHub. Never ask a student for a private key or PAT.
Errors & Conventions
- Errors are JSON:
{"error": "message"} with meaningful status codes (400 validation, 401 auth, 404 not found, 409 quota, 410 gone/expired, 500 deploy failure โ deploy failures include logs_tail).
- All timestamps are ISO 8601 UTC. Deploy results include the app's public
url and assigned port.
- Rate limits and quotas come back in
GET /api/me โ check before acting.
AIC-Web ยท Home ยท Deploy Guide ยท Student Portal