Better-PaaS
Guides

AI Agent API Guide

A complete reference for AI coding agents, LLMs, and automation tools to interact with the Better-PaaS API.

Better-PaaS is designed to be fully programmable. This guide gives AI coding agents, CI/CD tools, and automation scripts everything they need to deploy apps, read logs, manage databases, and monitor health - all through a clean JSON REST API.

You can authenticate with either your admin token or a scoped agent token. Agent tokens are recommended for any automated tool - they can be limited to specific actions and revoked independently.

Recommended: connect from your laptop with the paas CLI:

paas connect <dashboard-url>   # browser authorization
paas setup                   # wire up Cursor / Claude Code MCP

Restart your editor, then ask in plain English ("list my apps", "show logs for X"). For manual token creation and scope reference, see Agent Access & Audit Logs.


Using with AI editors (MCP)

The CLI includes a built-in MCP server (paas mcp) that Cursor, Claude Code, and other MCP clients can call directly - no curl in chat.

StepCommand
1. Connectpaas connect https://paas.example.com
2. Configure editorpaas setup
3. Restart editorQuit and reopen Cursor or Claude Code
4. Ask in chat"List my Better-PaaS apps", "Redeploy my-api", etc.

MCP tools available today: paas_list_apps, paas_list_projects, paas_get_app, paas_deploy, paas_redeploy, paas_get_logs, paas_status. See the full list and roadmap in PaaS CLI - MCP tools.

If your tool does not support MCP, use the terminal fallback:

eval "$(paas env)"

Then the AI can call the REST API with $PAAS_API_URL and $PAAS_TOKEN. The endpoints below document that path.


Authentication

Every API endpoint requires a Bearer token:

Authorization: Bearer <token>

Two token types:

TokenUse whenPermissions
Admin tokenDashboard login, managing agents, viewing audit logsFull access to everything
Agent tokenCI/CD, AI assistants, monitoring scripts, automationLimited to assigned scopes

Admin token

Generated on first run. Retrieve it anytime:

On your server
cd ~/better-paas/backend && ./server token

Fastest: use the paas CLI:

paas connect https://paas.example.com
paas setup    # MCP for Cursor / Claude Code - restart editor after
paas status

Manual: create a scoped token via the API:

curl -X POST http://localhost:8080/api/agents/create \
  -H "Authorization: Bearer <admin_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Automation",
    "scopes": ["apps:read", "apps:write", "deploy:trigger", "logs:read"]
  }'

The response includes a bpagt_... token shown once. Save it securely.

WebSocket authentication

WebSocket endpoints (for live logs and stats) authenticate with a short-lived ticket:

# Step 1: mint a ticket (requires any valid token)
curl -X POST http://localhost:8080/api/auth/ws-ticket \
  -H "Authorization: Bearer <token>"
# → { "ticket": "abc123...", "expiresIn": 60 }

# Step 2: connect with the ticket
ws://YOUR_SERVER:8080/ws/logs?ticket=abc123...

Core API Endpoints

Apps

List all apps

GET /api/apps
Authorization: Bearer <token>

Response:

[
  {
    "id": "abc123def4",
    "name": "my-node-app",
    "status": "running",
    "gitRepo": "https://github.com/user/my-node-app",
    "branch": "main",
    "port": 3000,
    "url": "http://abc123def4.192.168.1.10.sslip.io",
    "domains": ["node-app.example.com"],
    "createdAt": "2026-06-01T12:00:00Z",
    "activeCommit": "a1b2c3d",
    "activeCommitMsg": "feat: add user auth"
  }
]

Get a single app

GET /api/apps/get?id=<app-id>
Authorization: Bearer <token>

Returns full app details including runtime configuration, domains, volumes, and latest deployment info. Same shape as items in the list endpoint.

Deploy a new app

POST /api/deploy
Authorization: Bearer <token>
Content-Type: application/json

Body parameters:

FieldTypeRequiredDescription
namestringYesApp name (lowercase letters, digits, hyphens; 2–40 chars)
gitRepostringYesGit clone URL
branchstringYesBranch to deploy
envVarsobjectNoEnvironment variables as key-value strings
buildCommandstringNoOverride the build command
startCommandstringNoOverride the start command
domainsstring[]NoCustom domains
memorystringNoMemory limit (e.g. "512m", "1g")
cpusstringNoCPU limit (e.g. "0.5", "2")
volumesstring[]NoPersistent volumes ("name:/container/path")
healthPathstringNoHTTP health check path (default: TCP probe)

Example:

{
  "name": "marketing-site",
  "gitRepo": "https://github.com/org/marketing",
  "branch": "main",
  "envVars": {
    "NODE_ENV": "production",
    "API_TIMEOUT": "5000"
  },
  "memory": "512m",
  "healthPath": "/health"
}

Redeploy an existing app

POST /api/apps/redeploy
Authorization: Bearer <token>
Content-Type: application/json
{
  "id": "abc123def4",
  "noCache": false
}

Stop / Start / Delete

POST /api/apps/stop
POST /api/apps/start
POST /api/apps/delete
Authorization: Bearer <token>
Content-Type: application/json
{ "id": "abc123def4" }

Rollback

POST /api/apps/rollback
Authorization: Bearer <token>
Content-Type: application/json
{
  "id": "abc123def4",
  "deploymentId": "dep_xyz789"
}

Update app configuration

POST /api/apps/update
Authorization: Bearer <token>
Content-Type: application/json
{
  "id": "abc123def4",
  "envVars": { "API_KEY": "new-value" },
  "memory": "1g",
  "domains": ["app.example.com"]
}

Runtime logs

GET /api/apps/runtime-logs?id=<app-id>&lines=100
Authorization: Bearer <token>

Response:

{
  "logs": ["line 1", "line 2", "line 3"]
}

Per-app metrics

GET /api/metrics/apps
Authorization: Bearer <token>

Response:

[
  {
    "appId": "abc123def4",
    "name": "my-node-app",
    "cpuPercent": 12.5,
    "memUsageMb": 128.0,
    "memLimitMb": 512.0,
    "memPercent": 25.0
  }
]

Add-ons (Databases & Caches)

List all add-ons

GET /api/addons
Authorization: Bearer <token>

Get a single add-on

GET /api/addons/get?id=<addon-id>
Authorization: Bearer <token>

Create an add-on

POST /api/addons/create
Authorization: Bearer <token>
Content-Type: application/json
{
  "name": "prod-db",
  "type": "postgres"
}

Supported types: postgres, redis, mysql.

When you attach a database to an app, Better-PaaS automatically injects connection env vars like DATABASE_URL and REDIS_URL into the app container. No custom networking needed.

Attach / Detach

POST /api/addons/attach
POST /api/addons/detach
Authorization: Bearer <token>
Content-Type: application/json
{
  "addonId": "addon_123",
  "appId": "abc123def4"
}

Database operations (Postgres/MySQL only)

POST /api/addons/db/query
Authorization: Bearer <token>
Content-Type: application/json
{
  "addonId": "addon_123",
  "query": "SELECT * FROM users LIMIT 10"
}

Other endpoints: /api/addons/db/tables, /api/addons/db/columns, /api/addons/db/row/insert, /api/addons/db/row/update, /api/addons/db/row/delete.


Projects

GET    /api/projects
GET    /api/projects/get?id=<id>
POST   /api/projects/create       { "name": "my-project" }
POST   /api/projects/rename       { "id": "...", "name": "new-name" }
POST   /api/projects/delete       { "id": "..." }

Servers

GET    /api/servers
GET    /api/server/info
POST   /api/servers/create
POST   /api/servers/delete       { "id": "..." }
POST   /api/servers/test         { "id": "..." }

Cron Jobs

GET    /api/cron
POST   /api/cron/create
POST   /api/cron/update
POST   /api/cron/delete
POST   /api/cron/run             { "id": "..." }

Backups

GET    /api/backups
POST   /api/backups/create
POST   /api/backups/restore
POST   /api/backups/delete

System

GET    /api/system/version
GET    /api/health
POST   /api/docker/prune
POST   /api/system/update/apply
GET    /api/deployments/history

Guidelines for apps deployed on Better-PaaS

1. Bind to the injected PORT

Better-PaaS passes the PORT environment variable. Do not hardcode a port.

Node.js / Express:

const port = process.env.PORT || 3000;
app.listen(port, '0.0.0.0', () => console.log(`Listening on ${port}`));

Python / FastAPI:

import os
import uvicorn

if __name__ == "__main__":
    port = int(os.environ.get("PORT", 8000))
    uvicorn.run("main:app", host="0.0.0.0", port=port)

2. Containers are ephemeral

Each deploy creates a fresh container. For persistent state:

  • Logs: Write to stdout/stderr. Better-PaaS captures and streams them automatically.
  • Files / uploads / SQLite: Mount a persistent volume via the API: "volumes": ["mydata:/data"].

3. Commit lockfiles

Nixpacks detects your language from lockfiles. Always commit:

  • JavaScript: package-lock.json, pnpm-lock.yaml, or yarn.lock
  • Python: requirements.txt or poetry.lock
  • Go: go.sum
  • Ruby: Gemfile.lock

4. Add a health check endpoint

Better-PaaS does a TCP probe by default. For faster, safer zero-downtime deploys, add an HTTP health endpoint:

app.get('/health', (req, res) => {
  res.status(200).send('ok');
});

Then set healthPath: "/health" when deploying.


Rate limits and errors

  • Failed authentication attempts from an IP trigger an escalating lockout (HTTP 429 with Retry-After).
  • Request bodies are capped at 2 MiB.
  • All errors return JSON:
{ "error": "App not found" }

On this page