๐ 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 | Long-lived; 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 |
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 | Deploy from a GIT repo (JSON body) |
POST | /api/me/apps/upload | Deploy from a ZIP (multipart: name, file + optional fields) |
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 (decrypted, one-time) |
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 |
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)
GET | /.well-known/mcp-auth.json | Machine-readable auth spec |
POST | /mcp/pair | Start device pairing โ {agentId, code} (6 digits, reusable 120s) |
POST | /mcp/exchange | {agentId, code} โ {token, refresh_token}. Polls with 202 until the student approves |
POST | /mcp/refresh | {refresh_token} โ new token pair (rotated) |
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}
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