Developers
Use user API keys to call the Hostless HTTP API from scripts, CI pipelines, or other services. Each key belongs to your account, carries explicit scopes, and acts on behalf of you across every project you can access.
Base URL
https://api.hostless.dev
All paths below are prefixed with /v1.
Authentication
API keys (automation)
Send the full token as the Authorization header. Do not add a Bearer prefix.
curl -sS \
-H "Authorization: hlk_a1b2c3d4_your_secret_here" \
"https://api.hostless.dev/v1/apps"
| Header | Value |
|---|---|
Authorization | hlk_<prefix>_<secret> |
Some HTTP clients always send Bearer. This is also accepted when the token starts with hlk_:
Authorization: Bearer hlk_a1b2c3d4_your_secret_here
User sessions (dashboard / key management)
Browser login and creating or revoking API keys require a normal JWT session:
Authorization: Bearer <access_token>
API keys cannot call key-management routes or account-level billing routes (payment methods, currency).
Quick start
- In the dashboard, open Account → Settings → API Keys.
- Create a key, select scopes, set optional expiry, and copy the token immediately (shown once).
- Call any allowed platform endpoint with
Authorization: hlk_....
export HOSTLESS_API="https://api.hostless.dev"
export HOSTLESS_TOKEN="hlk_xxxxxxxx_your_secret"
# List apps across all projects you can access
curl -sS -H "Authorization: $HOSTLESS_TOKEN" "$HOSTLESS_API/v1/apps"
List endpoints return resources from all projects you belong to. Optional ?projectId= filters to one project. When creating resources with an API key, include projectId in the request body.
Model Context Protocol (MCP)
Hostless exposes a remote HTTP MCP server at https://mcp.hostless.app with two endpoints:
| Endpoint | Auth | Purpose |
|---|---|---|
POST /mcp/docs | None | Read Hostless documentation (docs_search, docs_get_page, docs_list) |
POST /mcp | API key, JWT, or OAuth | Call platform APIs as typed tools (apps_*, projects_*, etc.) |
The MCP service is separate from the REST API. Platform tool calls are proxied to https://api.hostless.dev/v1/* using your credentials. API key scopes and project roles apply the same way as direct HTTP calls.
Use /mcp/docs when you only need documentation (onboarding agents, open-source examples, or users without a Hostless account). Use /mcp when the agent should manage apps, sites, databases, and other resources.
MCP base URL
https://mcp.hostless.app
Use the URL of your deployed MCP service if self-hosting.
| Route | Method | Purpose |
|---|---|---|
/health | GET | Liveness check (no auth) |
/.well-known/oauth-protected-resource | GET | OAuth resource metadata for /mcp |
/mcp/docs | POST | Public documentation MCP (no auth) |
/mcp | POST | Platform API MCP (auth required) |
/mcp, /mcp/docs | GET | Not supported (returns 405) |
Health check
curl -sS "https://mcp.hostless.app/health"
{
"ok": true,
"service": "hostless-mcp",
"baseUrl": "https://api.hostless.dev",
"docsMcpPath": "/mcp/docs"
}
Public documentation MCP (no auth)
Read-only access to Hostless guides, API reference, and framework tutorials. No API key, no JWT, no Authorization header.
Docs MCP URL
https://mcp.hostless.app/mcp/docs
| Tool | Purpose |
|---|---|
search | OpenAI-compatible search; returns document ids, titles, and URLs |
fetch | OpenAI-compatible fetch; loads full document text and citation metadata by id |
docs_search | Search documentation by keyword; returns titles, paths, snippets, and public URLs |
docs_get_page | Load a full page by path (e.g. app/deployments, developers) — omit .md / .mdx |
docs_list | List every documentation page |
Tips for agents
- Prefer
searchthenfetchfor ChatGPT/OpenAI clients and deep-research-style retrieval. docs_search,docs_get_page, anddocs_listremain available for existing MCP clients.- Paths match the public docs site (
https://web.hostless.cloud/...) without the file extension. - For deploys, billing, or resource changes, connect to the platform API MCP instead.
Connect from Cursor (no auth)
Prefer a native HTTP MCP entry — no mcp-remote bridge and no env vars:
{
"mcpServers": {
"hostless-docs": {
"url": "https://mcp.hostless.app/mcp/docs"
}
}
}
Alternatively, use mcp-remote:
{
"mcpServers": {
"hostless-docs": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.hostless.app/mcp/docs"]
}
}
}
Restart Cursor after saving ~/.cursor/mcp.json (or your project MCP config).
Connect from Claude Desktop (no auth)
Add a streamable HTTP MCP server (Settings → Developer → MCP):
| Field | Value |
|---|---|
| URL | https://mcp.hostless.app/mcp/docs |
Authorization header | Omit — not required |
Local development (no auth)
Run the MCP HTTP server from the monorepo:
cd hostless-api
npm run build
npm run start:mcp:http # default http://localhost:3333
Docs are loaded automatically from hostless-docs/docs/ when the repo is checked out side by side. Set API_BASE_URL in hostless-api/.env if startup fails (required to boot the process even when you only use /mcp/docs).
Point Cursor at the local docs endpoint:
{
"mcpServers": {
"hostless-docs-local": {
"url": "http://localhost:3333/mcp/docs"
}
}
}
Verify locally:
curl -sS http://localhost:3333/health
Docs + platform API together
Use two MCP servers in the same config — docs without credentials, platform with your API key:
{
"mcpServers": {
"hostless-docs": {
"url": "https://mcp.hostless.app/mcp/docs"
},
"hostless": {
"url": "https://mcp.hostless.app/mcp",
"headers": {
"Authorization": "hlk_a1b2c3d4_your_secret_here"
}
}
}
}
For local platform API calls, swap hostless to http://localhost:3333/mcp and use your dev API key.
Platform API MCP (auth required)
Every POST /mcp request must include an Authorization header. Use the same tokens as the REST API:
| Token type | Header |
|---|---|
| API key | Authorization: hlk_<prefix>_<secret> |
| API key (some clients) | Authorization: Bearer hlk_<prefix>_<secret> |
| User JWT | Authorization: Bearer <access_token> |
| OAuth access token | Authorization: Bearer <oauth_access_token> |
Create an API key in the dashboard (Account → Settings → API Keys) with the scopes you need (for example apps:read, projects:read). The MCP server does not accept a server-wide token — each client sends its own credential on every request.
OAuth for ChatGPT and Claude connectors
AI clients can connect to Hostless MCP using OAuth 2.0 authorization code + PKCE instead of pasting an API key. When a client has no token, POST /mcp returns 401 with a WWW-Authenticate header pointing at the protected-resource metadata URL.
Discovery
| Document | URL |
|---|---|
| Authorization server (API host) | https://api.hostless.dev/.well-known/oauth-authorization-server |
| JWKS (API host) | https://api.hostless.dev/.well-known/jwks.json |
| Protected resource (MCP host) | https://mcp.hostless.app/.well-known/oauth-protected-resource |
Flow (summary)
- Client discovers metadata from the URLs above.
- Client opens
GET /v1/oauth/authorizeon the API with PKCE (code_challenge/ S256),resource=https://mcp.hostless.app, and a registeredredirect_uri. - Hostless redirects to the web app consent screen (
/oauth/authorize). Sign in if needed, then Allow or Deny. - Client exchanges the authorization code at
POST /v1/oauth/tokenfor a short-lived RS256 access token and refresh token. - Client calls
POST https://mcp.hostless.app/mcpwithAuthorization: Bearer <oauth_access_token>.
Scopes (v1): OAuth connectors receive read-only scopes (for example projects:read, apps:read, logs:read). Write and delete scopes are not granted to ChatGPT/Claude connectors in this release.
Revoke access: In the dashboard go to Account → Settings → Connected AI apps and revoke the connector. Access stops on the next MCP or API request.
Claude custom connector credentials
Claude requires a Client ID and Client secret in Advanced settings. Generate your own credentials in the dashboard (Account → Settings → Connected AI apps → Generate Claude credentials). Each credential pair is tied to your account — you do not need a shared platform secret.
- Generate credentials and copy the Client ID (
hloc_…) and Client secret (shown once). - In Claude: add a custom connector with MCP URL
https://mcp.hostless.app/mcp. - Open Advanced settings and paste the Client ID and Client secret.
- Complete OAuth consent when Claude connects.
ChatGPT
ChatGPT uses the public openai-chatgpt OAuth client. Per-connector redirect URLs (https://chatgpt.com/connector/oauth/{id}) are allowed via redirect URI prefix matching — no manual env configuration per connector.
Legacy shared Claude client
The seeded claude-custom-connector client remains for backward compatibility when OAUTH_CLAUDE_CLIENT_SECRET is set in server config. New integrations should use per-user credentials from the dashboard instead.
Connect from Cursor
Native HTTP MCP (Cursor 0.4.7+):
{
"mcpServers": {
"hostless": {
"url": "https://mcp.hostless.app/mcp",
"headers": {
"Authorization": "hlk_a1b2c3d4_your_secret_here"
}
}
}
}
Or use mcp-remote:
{
"mcpServers": {
"hostless": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://mcp.hostless.app/mcp",
"--header",
"Authorization:${HOSTLESS_TOKEN}"
],
"env": {
"HOSTLESS_TOKEN": "hlk_a1b2c3d4_your_secret_here"
}
}
}
}
Replace HOSTLESS_TOKEN with your API key (hlk_...) or Bearer <jwt>. On Windows, omit spaces around the colon in --header (Authorization:${HOSTLESS_TOKEN}) if Cursor mangles spaced arguments.
Connect from Claude Desktop
Add a streamable HTTP server entry (Claude Desktop → Settings → Developer → MCP). Point the URL at https://mcp.hostless.app/mcp and set the Authorization header to your hlk_... token or Bearer JWT.
Exact UI labels vary by Claude Desktop version; the required values are the MCP URL and the same Authorization header as above.
Available tools
Tools mirror common platform operations. Names follow {resource}_{action}:
| Resource | Tools |
|---|---|
| Projects | projects_list, projects_get, projects_create, projects_patch, projects_delete |
| Apps | apps_list, apps_get, apps_create, apps_patch, apps_delete |
| Sites | sites_list, sites_get, sites_create, sites_patch, sites_delete |
| Workers | workers_list, workers_get, workers_create, workers_patch, workers_delete |
| Cron jobs | cron_jobs_list, cron_jobs_get, cron_jobs_create, cron_jobs_patch, cron_jobs_delete |
| Databases | databases_list, databases_get, databases_create, databases_patch, databases_delete |
Tips for agents
- Call
projects_listfirst, then passprojectIdwhen listing or creating apps, sites, workers, cron jobs, or databases. - Apps, sites, workers, and cron jobs are identified by name, not MongoDB id.
- Project create/update/delete requires a user JWT; API keys can only read projects (
projects:read). - Use public documentation MCP (
/mcp/docs) to look up guides without an API key.
MCP errors
| Status | Meaning |
|---|---|
401 Unauthorized | POST /mcp only — missing or invalid Authorization header (POST /mcp/docs never returns 401) |
405 Method Not Allowed | GET /mcp or GET /mcp/docs — use POST for MCP requests |
Tool isError: true | Platform API error (missing scope, not found, validation, etc.) — body includes the API error |
Docs tool isError: true | Page not found or invalid argument (e.g. empty query or path) |
Key management API
These endpoints require a user JWT, not an API key. You can only create or revoke your own keys.
Create API key
POST /v1/users/me/api-keys
Request body
{
"name": "CI deploy",
"scopes": ["apps:read", "deployments:write"],
"expiresAt": "2026-12-31T23:59:59.000Z"
}
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Label for the key (1–128 characters). |
scopes | string[] | yes | One or more scopes from the scope reference. |
expiresAt | string (ISO 8601) | no | Expiry time. Omit or null for no expiry. Must be in the future. |
Response 201
{
"token": "hlk_a1b2c3d4f8e7d6c5b4a39281726354849302ab1cd2ef3a4b5c6d7e8f9a0b1",
"apiKey": {
"id": "674a1b2c3d4e5f6789012345",
"name": "CI deploy",
"prefix": "hlk_a1b2c3d4",
"scopes": ["apps:read", "deployments:write"],
"expiresAt": "2026-12-31T23:59:59.000Z",
"lastUsedAt": null,
"createdAt": "2026-05-22T12:00:00.000Z"
}
}
| Field | Description |
|---|---|
token | Full secret. Returned only once. Store it securely; it cannot be retrieved later. |
apiKey.prefix | Public identifier for lists (not the secret). |
apiKey.id | Use when revoking the key. |
Example
curl -sS -X POST "$HOSTLESS_API/v1/users/me/api-keys" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"name": "CI deploy",
"scopes": ["apps:read", "deployments:write"],
"expiresAt": null
}'
List API keys
GET /v1/users/me/api-keys
Response 200 — array of key metadata (no secrets):
[
{
"id": "674a1b2c3d4e5f6789012345",
"name": "CI deploy",
"prefix": "hlk_a1b2c3d4",
"scopes": ["apps:read", "deployments:write"],
"expiresAt": "2026-12-31T23:59:59.000Z",
"lastUsedAt": "2026-05-22T14:30:00.000Z",
"createdAt": "2026-05-22T12:00:00.000Z"
}
]
Revoke API key
DELETE /v1/users/me/api-keys/{keyId}
Response 200
{
"success": true
}
Revoked keys stop working immediately.
Using keys on platform APIs
Send Authorization: hlk_... on any endpoint below. The required scope is determined by HTTP method and path:
| Method | Typical scope |
|---|---|
GET, HEAD | resource:read (or deployments:read / logs:read when the path matches those segments) |
POST, PUT, PATCH | resource:write (or deployments:write / hostless-file:write) |
DELETE | resource:delete (integrations and billing use :write for DELETE) |
Paths use {appName}, {siteName}, {workerName}, {cronJobName}, {databaseName}, {projectId}, {deploymentId}, and {id} as path parameters. Expand a scope to see every matching route.
With an API key, GET list routes return data from all projects you can access unless you pass ?projectId=. POST create routes require projectId in the JSON body.
API reference (by scope)
apps:read — List and get apps
| Method | Path |
|---|---|
GET | /apps |
GET | /apps/{appName} |
GET | /apps/{appName}/search |
GET | /apps/{appName}/environment-variables |
GET | /apps/{appName}/custom-domains |
GET | /apps/{appName}/commits |
GET | /apps/{appName}/collaborators |
GET | /apps/{appName}/review-apps |
GET | /apps/{appName}/review-apps/pull-requests |
GET | /apps/{appName}/worker-processes |
GET | /apps/{appName}/worker-processes/{workerId} |
GET | /apps/{appName}/metrics/request-count |
GET | /apps/{appName}/metrics/response-duration |
GET | /apps/{appName}/metrics/cpu-usage |
GET | /apps/{appName}/metrics/memory-usage |
apps:write — Create and update apps
| Method | Path |
|---|---|
POST | /apps |
PATCH | /apps/{appName} |
POST | /apps/{appName}/restart |
POST | /apps/{appName}/custom-domains |
PATCH | /apps/{appName}/upgrade |
PATCH | /apps/{appName}/collaborators |
POST | /apps/{appName}/scale/down |
POST | /apps/{appName}/scale/up |
POST | /apps/{appName}/review-apps/trigger |
PATCH | /apps/{appName}/review-apps/extend |
PATCH | /apps/{appName}/worker-processes/{workerId} |
POST | /apps/{appName}/worker-processes |
apps:delete — Delete apps
| Method | Path |
|---|---|
DELETE | /apps/{appName} |
DELETE | /apps/{appName}/custom-domains |
DELETE | /apps/{appName}/collaborators/{email} |
DELETE | /apps/{appName}/worker-processes/{workerId} |
sites:read — List and get sites
| Method | Path |
|---|---|
GET | /sites |
GET | /sites/{siteName} |
GET | /sites/{siteName}/search |
GET | /sites/{siteName}/environment-variables |
GET | /sites/{siteName}/custom-domains |
GET | /sites/{siteName}/commits |
GET | /sites/{siteName}/review-sites |
GET | /sites/{siteName}/review-sites/pull-requests |
GET | /sites/{siteName}/visual-builder/project |
GET | /sites/{siteName}/visual-builder/assets |
GET | /sites/{siteName}/metrics/request-count |
GET | /sites/{siteName}/metrics/request-count/total |
GET | /sites/{siteName}/metrics/response-duration |
GET | /sites/{siteName}/metrics/bandwidth |
GET | /sites/{siteName}/metrics/unique-visitors |
GET | /sites/{siteName}/metrics/top-paths |
GET | /sites/{siteName}/metrics/cpu-usage |
GET | /sites/{siteName}/metrics/memory-usage |
GET | /sites/{siteName}/deployments/{siteDeploymentId}/metrics/build-stats |
sites:write — Create and update sites
| Method | Path |
|---|---|
POST | /sites |
POST | /sites/from-upload |
POST | /sites/from-visual-builder |
PATCH | /sites/{siteName} |
POST | /sites/{siteName}/restart |
POST | /sites/{siteName}/upload |
POST | /sites/{siteName}/custom-domains |
POST | /sites/{siteName}/scale/down |
POST | /sites/{siteName}/scale/up |
POST | /sites/{siteName}/review-sites/trigger |
PUT | /sites/{siteName}/visual-builder/project |
POST | /sites/{siteName}/visual-builder/publish |
POST | /sites/{siteName}/visual-builder/assets |
sites:delete — Delete sites
| Method | Path |
|---|---|
DELETE | /sites/{siteName} |
DELETE | /sites/{siteName}/custom-domains |
workers:read — List and get workers
| Method | Path |
|---|---|
GET | /workers |
GET | /workers/{workerName} |
GET | /workers/{workerName}/search |
GET | /workers/{workerName}/environment-variables |
GET | /workers/{workerName}/commits |
GET | /workers/{workerName}/collaborators |
GET | /workers/{workerName}/metrics/cpu-usage |
GET | /workers/{workerName}/metrics/memory-usage |
workers:write — Create and update workers
| Method | Path |
|---|---|
POST | /workers |
PATCH | /workers/{workerName} |
POST | /workers/{workerName}/restart |
POST | /workers/{workerName}/scale/down |
POST | /workers/{workerName}/scale/up |
PATCH | /workers/{workerName}/collaborators |
workers:delete — Delete workers
| Method | Path |
|---|---|
DELETE | /workers/{workerName} |
DELETE | /workers/{workerName}/collaborators/{email} |
cron-jobs:read — List and get cron jobs (standalone and per-app)
| Method | Path |
|---|---|
GET | /cron-jobs |
GET | /cron-jobs/{cronJobName} |
GET | /cron-jobs/{cronJobName}/search |
GET | /cron-jobs/{cronJobName}/environment-variables |
GET | /cron-jobs/{cronJobName}/commits |
GET | /cron-jobs/{cronJobName}/runs |
GET | /cron-jobs/{cronJobName}/runs/{runId} |
GET | /cron-jobs/{cronJobName}/metrics/cpu-usage |
GET | /cron-jobs/{cronJobName}/metrics/memory-usage |
GET | /apps/{appName}/cron-jobs |
GET | /apps/{appName}/cron-jobs/{jobId} |
GET | /apps/{appName}/cron-jobs/{jobId}/runs |
GET | /apps/{appName}/cron-jobs/{jobId}/runs/{runId} |
cron-jobs:write — Create and update cron jobs
| Method | Path |
|---|---|
POST | /cron-jobs |
PATCH | /cron-jobs/{cronJobName} |
POST | /cron-jobs/{cronJobName}/pause |
POST | /cron-jobs/{cronJobName}/resume |
POST | /apps/{appName}/cron-jobs |
PATCH | /apps/{appName}/cron-jobs/{jobId} |
POST | /apps/{appName}/cron-jobs/{jobId}/run |
cron-jobs:delete — Delete cron jobs
| Method | Path |
|---|---|
DELETE | /cron-jobs/{cronJobName} |
DELETE | /apps/{appName}/cron-jobs/{jobId} |
deployments:read — List deployments and get deployment details
Applies when the path contains /deployments (except mutating routes below).
| Method | Path |
|---|---|
GET | /apps/{appName}/deployments |
GET | /apps/{appName}/deployments/{deploymentId} |
GET | /apps/{appName}/deployments/{deploymentId}/stats |
GET | /apps/{appName}/build/stats |
GET | /sites/{siteName}/deployments |
GET | /sites/{siteName}/deployments/{deploymentId} |
GET | /workers/{workerName}/deployments |
GET | /workers/{workerName}/deployments/{deploymentId} |
GET | /cron-jobs/{cronJobName}/deployments |
GET | /cron-jobs/{cronJobName}/deployments/{deploymentId} |
deployments:write — Trigger, rerun, or create deployments
| Method | Path |
|---|---|
POST | /apps/{appName}/deployments/new |
POST | /apps/{appName}/deployments/{deploymentId}/rerun |
POST | /sites/{siteName}/deployments/new |
POST | /sites/{siteName}/deployments/{deploymentId}/rerun |
POST | /workers/{workerName}/deployments/new |
POST | /workers/{workerName}/deployments/{deploymentId}/rerun |
POST | /cron-jobs/{cronJobName}/deployments/new |
POST | /cron-jobs/{cronJobName}/deployments/{deploymentId}/rerun |
logs:read — Runtime, deployment, build, and run logs
Applies when the path contains /logs, /post-build-logs, or /runtime-logs.
| Method | Path |
|---|---|
GET | /apps/{appName}/logs |
GET | /apps/{appName}/deployments/{deploymentId}/logs |
GET | /apps/{appName}/deployments/{deploymentId}/post-build-logs |
GET | /sites/{siteName}/logs |
GET | /sites/{siteName}/deployments/{deploymentId}/logs |
GET | /workers/{workerName}/logs |
GET | /workers/{workerName}/deployments/{deploymentId}/logs |
GET | /cron-jobs/{cronJobName}/logs |
GET | /cron-jobs/{cronJobName}/deployments/{deploymentId}/logs |
GET | /cron-jobs/{cronJobName}/runs/{runId}/logs |
GET | /apps/{appName}/cron-jobs/{jobId}/runs/{runId}/logs |
GET | /databases/{databaseName}/import/{cloneId}/logs |
databases:read — List and get databases
| Method | Path |
|---|---|
GET | /databases |
GET | /databases/{databaseName}/search |
GET | /databases/{databaseName}/details |
GET | /databases/{databaseName}/linked-apps |
GET | /databases/{databaseName}/import/{cloneId}/status |
databases:write — Create and update databases
| Method | Path |
|---|---|
POST | /databases |
PATCH | /databases/{databaseName} |
PATCH | /databases/{databaseName}/tier |
POST | /databases/{databaseName}/pause |
POST | /databases/{databaseName}/unpause |
POST | /databases/{databaseName}/import |
databases:delete — Delete databases
| Method | Path |
|---|---|
DELETE | /databases/{databaseName} |
integrations:read — Discover and list integrations
| Method | Path |
|---|---|
GET | /integrations/available |
GET | /projects/{projectId}/integrations |
integrations:write — Configure project integrations
| Method | Path |
|---|---|
POST | /projects/{projectId}/integrations/slack |
PUT | /projects/{projectId}/integrations/slack |
DELETE | /projects/{projectId}/integrations/slack |
POST | /projects/{projectId}/integrations/slack/test |
projects:read — Get project metadata
Only GET / HEAD on /projects paths (except git, integrations, and members routes).
| Method | Path |
|---|---|
GET | /projects |
GET | /projects/{projectId} |
GET | /projects/{projectId}/apps |
GET | /projects/{projectId}/connected-accounts |
git:read — Read project Git provider connections
| Method | Path |
|---|---|
GET | /projects/{projectId}/github/accounts |
GET | /projects/{projectId}/github/accounts/{accountLogin}/repos |
GET | /projects/{projectId}/gitlab/repos |
GET | /projects/{projectId}/bitbucket/workspaces |
GET | /projects/{projectId}/bitbucket/workspaces/{workspace}/repos |
git:write — Connect or disconnect Git accounts
| Method | Path |
|---|---|
DELETE | /projects/{projectId}/github/disconnect |
DELETE | /projects/{projectId}/gitlab/disconnect |
DELETE | /projects/{projectId}/bitbucket/disconnect |
traces:read — App traces
| Method | Path |
|---|---|
GET | /traces/{appName} |
hostless-file:read — Cost estimation from a Hostless file
| Method | Path |
|---|---|
POST | /estimate-costs |
hostless-file:write — Generate, update, or commit Hostless file config
| Method | Path |
|---|---|
POST | /apps/{appName}/hostless-file/generate |
POST | /apps/{appName}/hostless-file/update |
POST | /apps/{appName}/hostless-file/commit |
POST | /apps/{appName}/hostless-file/calculate-costs |
virtual-machines:read — List and get VMs
| Method | Path |
|---|---|
GET | /virtual-machines |
GET | /virtual-machines/pricing |
GET | /virtual-machines/project/{projectId} |
GET | /virtual-machines/{id} |
GET | /virtual-machines/{id}/metrics |
GET | /virtual-machines/{id}/metrics/cpu-usage |
GET | /virtual-machines/{id}/metrics/memory-usage |
GET | /virtual-machines/{id}/metrics/network-in |
GET | /virtual-machines/{id}/metrics/network-out |
GET | /virtual-machines/{id}/metrics/disk-usage |
GET | /virtual-machines/{id}/firewall |
virtual-machines:write — Create and manage VMs
| Method | Path |
|---|---|
POST | /virtual-machines |
POST | /virtual-machines/{id}/start |
POST | /virtual-machines/{id}/stop |
POST | /virtual-machines/{id}/restart |
PUT | /virtual-machines/{id}/resources |
PUT | /virtual-machines/{id}/firewall |
virtual-machines:delete — Delete VMs
| Method | Path |
|---|---|
DELETE | /virtual-machines/{id} |
billing:read — Usage, cycles, and invoices (account-wide)
| Method | Path |
|---|---|
GET | /billing/breakdown |
GET | /billing/databases/breakdown |
GET | /billing/cycles |
GET | /billing/currencies |
GET | /billing/payment-config |
GET | /invoices |
GET | /invoices/{id} |
GET | /invoices/{id}/receipt |
billing:write — Pay a project invoice
| Method | Path |
|---|---|
POST | /billing/pay/{invoiceId} |
metrics:read — Standalone metrics routes
No customer routes are mounted at /metrics today. App, site, worker, and cron job metrics use each resource's :read scope (for example apps:read on GET /apps/{appName}/metrics/...). This scope is reserved for future /metrics endpoints.
Not available with API keys
These routes require a user JWT (dashboard session), not an API key:
- Key management:
/users/me/api-keys - Account auth and profile:
/auth,/users(except your own key routes above) - Project members and transfers:
/projects/{projectId}/members,PATCH /projects/{projectId},DELETE /projects/{projectId}, resource transfers - Payment methods and currency:
/billing/stripe/*,/billing/paystack/*,PATCH /billing/currency/*,GET /billing/currencies/{currencyCode} - Admin, SSH keys, health, campaigns, credit, and standalone
/github,/gitlab,/bitbucketOAuth callbacks
Example: trigger a deployment
curl -sS -X POST \
-H "Authorization: $HOSTLESS_TOKEN" \
-H "Content-Type: application/json" \
"$HOSTLESS_API/v1/apps/my-app/deployments/new" \
-d '{}'
Requires deployments:write (and project role owner/editor if your user is a viewer).
Example: read billing breakdown
curl -sS \
-H "Authorization: $HOSTLESS_TOKEN" \
"$HOSTLESS_API/v1/billing/breakdown?startDate=2026-05-01&endDate=2026-05-31"
Requires billing:read. Returns usage across all projects you can access.
Scope reference
Scopes use the format resource:action.
| Scope | Description |
|---|---|
| Apps | |
apps:read | List and get apps |
apps:write | Create and update apps |
apps:delete | Delete apps |
| Sites | |
sites:read | List and get sites |
sites:write | Create and update sites |
sites:delete | Delete sites |
| Workers | |
workers:read | List and get workers |
workers:write | Create and update workers |
workers:delete | Delete workers |
| Cron jobs | |
cron-jobs:read | List and get cron jobs |
cron-jobs:write | Create and update cron jobs |
cron-jobs:delete | Delete cron jobs |
| Deployments | |
deployments:read | List deployments, get deployment details |
deployments:write | Trigger, rerun, or create deployments |
| Logs | |
logs:read | Deployment logs, post-build logs, runtime logs |
| Databases | |
databases:read | List and get databases |
databases:write | Create and update databases |
databases:delete | Delete databases |
| Integrations | |
integrations:read | List project integrations |
integrations:write | Configure or update integrations |
| Project | |
projects:read | Get project and list project apps |
git:read | Read project git account connections |
git:write | Connect or disconnect project git accounts |
| Observability | |
metrics:read | App/site/worker metrics endpoints |
traces:read | App trace endpoints |
| Hostless file | |
hostless-file:read | Read or estimate from hostless file config |
hostless-file:write | Update hostless file config |
| Virtual machines | |
virtual-machines:read | List and get VMs |
virtual-machines:write | Create and update VMs |
virtual-machines:delete | Delete VMs |
| Billing | |
billing:read | Usage breakdown, cycles, invoices (account-wide) |
billing:write | Pay an invoice |
Authorization rules
Access is the intersection of:
- Valid API key — not revoked, not expired, correct secret.
- Scopes — the key includes every scope required for the route.
- Project role — the key creator's membership role still applies. For example, a project viewer cannot perform
POST/PUT/PATCH/DELETEeven if the key has write scopes. - App collaborators — existing app-level collaborator checks still apply.
Scopes are a ceiling, not an elevation: they cannot bypass role or collaborator rules.
Errors
| Status | Meaning |
|---|---|
401 Unauthorized | Missing/invalid token, expired key, or wrong secret. |
403 Forbidden | Missing scope, viewer blocked on mutation, wrong project resource, or JWT-only route called with an API key. |
404 Not Found | Project or resource not found (or not in a project you can access). |
Example scope error:
{
"statusCode": 403,
"message": "Missing required scope(s): deployments:write",
"error": "Forbidden"
}