API Reference

Deploy, inspect and manage your SnapDeploy containers programmatically

Base URL: https://snapdeploy.dev

Authentication

Your API key authenticates the /api/mobile/** endpoints documented on this page. Send it in any of three forms:

curl -H "X-API-Key: your-api-key" https://snapdeploy.dev/api/mobile/bootstrap
# or: Authorization: ApiKey your-api-key
# or: ?api_key=your-api-key

Get your key from the API Keys page in your dashboard — it also has one-line setup for the MCP connector (Claude Code, Cursor and other AI assistants).

Destructive actions are refused for API keys, by design. Deleting containers, databases, domains or your account, changing the password, and cancelling subscriptions all return 403 INTERACTIVE_SESSION_REQUIRED — they can only be done by a person signed in to the web dashboard or the mobile app. A leaked key (or an over-eager AI agent) cannot destroy anything.

Account & Quota

GET /api/mobile/bootstrap

Everything in one call: your profile and plan, all containers, the deploy limit and your free-hours usage.

Response (200 OK, trimmed):
{
  "user": { "userId": "…", "email": "…", "plan": "FREE" },
  "containers": [ { "containerId": "…", "name": "my-app", "status": "RUNNING", "url": "https://my-app.containers.snapdeploy.app" } ],
  "deployCap": { "capped": true, "remaining": 5, "dailyLimit": 5 },
  "usage": { "hoursUsed": 12.5, "hoursLimit": 100, "remainingHours": 87.5 }
}
GET /api/mobile/deploy-cap

Deploy limit only: capped, remaining, dailyLimit, timeUntilReset. Free accounts get 10 deploys a day (5 per rolling 12 hours) — failed attempts count. Always-On or a $1 Sprint Pack removes the limit.

Containers

POST /api/mobile/containers

Create a container. For GitHub-built apps use the placeholder image "pending" — the build supplies the real image after you link a repo.

Request Body:
{
  "name": "my-app",
  "image": "pending",
  "memory": 512,
  "environmentVariables": { "NODE_ENV": "production" }
}
GET /api/mobile/containers/{containerId}

Full container detail: status, URL, port (with detection confidence), detected technology, resources, env-var names.

POST /api/mobile/containers/{containerId}/start · /stop · /restart · /wake

Lifecycle actions. Start/restart/wake are subject to the free-hours gate — see the 402 error below. Restart also consumes a deploy-limit unit on free accounts.

PUT /api/mobile/containers/{containerId}/env

Replace the container's environment variables (rolling restart, no rebuild). Public-prefixed names (VITE_*, NEXT_PUBLIC_*, …) are applied at build time — trigger a redeploy for them to reach the compiled bundle.

{ "environmentVariables": { "DATABASE_URL": "…", "VITE_API_BASE": "…" } }
GET /api/mobile/containers/{containerId}/logs?lines=200

Recent runtime logs (CloudWatch), up to 1,000 lines per request.

GET /api/mobile/containers/{containerId}/deployments?limit=20

Deployment history for a container, newest first (max 50).

GitHub Deploys

Requires GitHub connected to your account (GET /api/mobile/github/status; if not connected, GET /api/mobile/github/connect-url returns the browser link).

GET /api/mobile/github/repos · /repos/search?q= · /repos/{owner}/{repo}/branches · /repos/{owner}/{repo}/detect-env-vars

Browse your repositories, and scan one for the environment variables it needs before the first deploy (Supabase and other well-known keys come with where-to-find help).

POST /api/mobile/github/link

Link a repo to a container. Linking triggers the first build itself and returns its deploymentId — don't call the deploy endpoint right after linking, or you'll start (and pay the deploy-limit for) a second build.

{
  "containerId": "…",
  "repoFullName": "you/your-repo",
  "deployBranch": "main"
}
POST /api/mobile/github/link/{repoLinkId}/deploy

Redeploy an already-linked repo — the CI/CD hook for scripts and assistants. Consumes one deploy-limit unit at trigger.

Response (200 OK):
{ "status": "triggered", "deploymentId": "…" }

Deployments

GET /api/mobile/deployments/{deploymentId}

Poll this after triggering a build. Terminal statuses are COMPLETED, FAILED, CANCELLED, ROLLED_BACK. On failure the response carries everything needed to diagnose:

{
  "deploymentId": "…",
  "status": "FAILED",
  "errorCode": "BUILD_FAILED",
  "errorMessage": "…human-readable cause…",
  "buildLogsTail": "…last lines of the build log…",
  "smartFixEligible": true
}
GET /api/mobile/deployments/{deploymentId}/stream

Live build log as Server-Sent Events, for real-time progress UIs.

SmartFix: when smartFixEligible is true, SnapDeploy can propose an automatic fix. Deterministic fixes are free for everyone; the AI-powered fix requires an active Sprint Pack or Always-On subscription. Applying a fix commits to your repository, so it requires an interactive session — it cannot be applied with an API key.

Add-ons, Domains & Always-On

GET POST /api/mobile/services/addons

List or create managed add-ons (PostgreSQL, MySQL, MariaDB, MongoDB, Redis, RabbitMQ). Creation requires a purchased add-on subscription — without one the response carries the purchase link. Credentials are injected into linked containers as env vars; they are shown to you in the dashboard, not returned to API-key callers' tooling.

GET POST /api/mobile/services/domains

List or attach custom domains ({ "domain": "app.example.com", "containerId": "…" }); fetch the DNS records to create via GET …/domains/{domainId}/dns-records, then verify with POST …/domains/{domainId}/verify.

GET /api/mobile/services/always-on/unassigned POST /api/mobile/services/always-on/assign

See unassigned Always-On subscriptions and attach one to a container ({ "subscriptionId": "…", "containerId": "…" }) — assigning also starts a stopped container. Cancelling or detaching a subscription is interactive-only.

Errors & Limits

400
Bad Request
Invalid parameters — the body's fieldErrors map names each offending field
401
Unauthorized
Invalid or missing API key
402
Payment Required — a plan limit, not a bug
Free hours exhausted (start/wake) or a paid feature. The body carries message, actionUrl and actionText — show them to the user; when spareSubscriptionId is present, an unused Always-On can be assigned instead. Don't retry the call.
403
INTERACTIVE_SESSION_REQUIRED
Destructive action attempted with an API key (delete, password change, subscription cancel, SmartFix apply). Sign in to the dashboard or the mobile app instead — this is deliberate and has no API workaround.
404
Not Found
Resource missing or not yours
429
Too Many Requests
Rate limits: 150 requests/minute and 2,500/hour per user; invalid API keys are throttled at 30/minute per IP. A body with DEPLOY_CAP_REACHED means the free deploy limit (10 a day — 5 per rolling 12 h, failed attempts count) — the $1 Sprint Pack link in actionUrl lifts it for 24 hours.
500
Internal Server Error
Something went wrong on our end

Build with the SnapDeploy API

Sign up free, grab your key from the API Keys page, and deploy from scripts, CI or your AI assistant