STACKS API Guide
STACKS API Guide
Overview
The STACKS API lets you manage your containers programmatically. Use it to automate deployments, integrate with CI/CD pipelines, or give your AI agents access to infrastructure — all with built-in spending controls.
Base URL: https://api.stackshosting.cloud/api/v1
Authentication
All API requests require an API key passed in the Authorization header:
Authorization: Bearer sk_stacks_your_key_here
Creating an API Key
- Log in to your STACKS dashboard
- Go to Account Settings → API Keys
- Click Create New Key
- Enter a descriptive name (e.g., “CI/CD Pipeline”, “Agent Key”)
- Select the permissions (scopes) your key needs
- Optionally set a monthly spending cap
- Click Create API Key
Important: Your API key is shown only once at creation. Copy it immediately and store it securely. You will not be able to view it again.
API Key Format
STACKS API keys follow this format:
sk_stacks_{64_character_hex_string}
Supported Clients
The API accepts requests from any HTTP client including:
- Node.js
fetch/axios - Python
requests,httpx,urllib - cURL
- PowerShell
Invoke-RestMethod - Any language or tool that can make HTTP requests
No special User-Agent or TLS configuration is required. Server-to-server automation clients are fully supported.
Security Best Practices
- Never commit API keys to version control
- Never share API keys in chat, email, or public channels
- Use the minimum scopes required for your use case
- Set spending caps on keys used by automated systems
- Rotate keys regularly and revoke unused keys immediately
- Use separate keys for different applications or environments
Permissions (Scopes)
Each API key has specific permissions that control what it can access:
| Scope | Description |
|---|---|
containers:read |
List containers, view status, logs, credentials, metrics, and backups |
containers:create |
Create new containers (subject to spending cap) |
containers:manage |
Start, stop, restart containers, manage connections, external access, IP restrictions, and trigger backups |
containers:delete |
Permanently delete containers |
A key with only containers:read cannot create, modify, or delete containers. Attempting to use an endpoint that requires a scope your key doesn’t have will return a 403 Forbidden error.
What API Keys Cannot Access
API keys are restricted to container-related operations. The following endpoints require dashboard access (JWT authentication) and cannot be accessed via API key:
- Billing and payment management
- User account settings
- API key management (create/revoke keys from the dashboard only)
- Database browser
- File manager
- Container resize (billing implications)
- SSH password changes
Spending Caps
When creating an API key, you can set a monthly spending cap. This limits the total cost of containers created through that key in a given calendar month.
- If a create request would exceed the spending cap, the API returns
402 Payment Requiredwith a clear error message - Spending caps are per-key, not per-account — different keys can have different limits
- Spending resets at the beginning of each calendar month
- Setting no spending cap allows unlimited creation (subject to account limits)
Recommended: Always set a spending cap on keys given to automated systems or AI agents.
Rate Limiting
Each API key has a configurable rate limit (default: 60 requests per minute).
Rate limit information is returned in response headers:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests per minute |
X-RateLimit-Remaining |
Requests remaining in current window |
X-RateLimit-Reset |
Unix timestamp when the window resets |
When rate limited, the API returns 429 Too Many Requests with a Retry-After header. Back off and retry after the specified delay.
Endpoints
List Containers
Retrieve all containers associated with your account.
GET /containers
Required scope: containers:read
Example:
curl -H "Authorization: Bearer sk_stacks_YOUR_KEY" \
https://api.stackshosting.cloud/api/v1/containers
PowerShell:
$headers = @{"Authorization"="Bearer sk_stacks_YOUR_KEY"}
Invoke-RestMethod -Uri "https://api.stackshosting.cloud/api/v1/containers" -Headers $headers
Response:
{
"success": true,
"count": 3,
"data": [
{
"id": "usr-abc12345-myapp",
"name": "myapp",
"type": "nodejs",
"plan": "basic",
"status": "running",
"cpu_allocated": 1,
"memory_allocated": 1024,
"storage_allocated": 10240,
"ip_address": "10.52.0.5",
"created_at": "2026-02-25T10:30:00.000Z",
"raw_docker_status": "Up 2 hours (healthy)",
"image_tag": "stackshosting/nodejs:latest",
"version": "22",
"version_major": "22"
}
]
}
Get Container Status
Get detailed status for a specific container.
GET /containers/status/{container_id}
Required scope: containers:read
Example:
curl -H "Authorization: Bearer sk_stacks_YOUR_KEY" \
https://api.stackshosting.cloud/api/v1/containers/status/usr-abc12345-myapp
Response:
{
"success": true,
"id": "usr-abc12345-myapp",
"name": "myapp",
"template": "nodejs",
"type": "nodejs",
"status": "running",
"rawStatus": "Up 2 hours (healthy)",
"ram": 1,
"storage": 10240,
"externalAccessEnabled": false,
"ipAddress": "10.52.0.5",
"version": "22",
"versionMajor": "22",
"plan": "basic",
"cpuAllocated": "1.00",
"memoryAllocated": 1024,
"storageAllocated": 10240,
"createdAt": "2026-02-25T10:30:00.000Z",
"updatedAt": "2026-02-25T12:00:00.000Z"
}
Get Container Credentials
Retrieve connection credentials for a container.
GET /containers/{container_id}/credentials
Required scope: containers:read
Example:
curl -H "Authorization: Bearer sk_stacks_YOUR_KEY" \
https://api.stackshosting.cloud/api/v1/containers/usr-abc12345-mydb/credentials
Response:
{
"success": true,
"credentials": [...],
"data": {
"containerName": "mydb",
"type": "postgresql",
"mainCredentials": {
"username": "admin",
"password": "generated-password"
},
"connectionInfo": {
"host": "10.52.0.5",
"port": 5432,
"internalHost": "usr-abc12345-mydb"
}
}
}
Note: Returns SSH credentials for compute containers and database/web UI credentials for managed services. The connectionInfo.port is the service port (5432 for PostgreSQL, 6379 for Redis, etc.). Use internalHost for VPC connections between containers.
Get Container Logs
Retrieve recent logs from a container.
GET /containers/{container_id}/logs?lines=100
Required scope: containers:read
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
lines |
integer | 100 | Number of log lines to return (max: 500) |
Get Container Metrics
Retrieve CPU, memory, disk, and network metrics.
GET /containers/{container_id}/metrics
Required scope: containers:read
Response includes: CPU, memory, disk, network_in, and network_out metric arrays with timestamps.
Create Container
Create a new container. Container creation is asynchronous. The API returns immediately with the container ID and a pending status. Poll GET /containers/status/{id} to track progress until the status changes to running.
POST /containers
Required scope: containers:create
Request body:
{
"name": "my-api-server",
"template": "nodejs",
"plan": "basic"
}
| Field | Required | Description |
|---|---|---|
name |
Yes | Container name: 3-32 characters, letters, numbers, and hyphens. Must start and end with a letter or number. No consecutive hyphens. |
template |
Yes | Container template ID (see Available Templates below) |
plan |
No | micro, basic, standard, or premium (default: basic) |
discount_code |
No | Promotional discount code |
additional_storage_tier |
No | Additional storage: 1 (32GB), 3 (64GB), or 5 (128GB). Only for non-micro, non-database containers. |
Name validation errors return 400 with field-level details:
{
"success": false,
"error": "Container name must be no more than 32 characters",
"field": "name"
}
Example:
curl -X POST \
-H "Authorization: Bearer sk_stacks_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"my-api","template":"nodejs","plan":"basic"}' \
https://api.stackshosting.cloud/api/v1/containers
Response (202 Accepted):
{
"success": true,
"message": "Container is being created. Poll GET /containers/status/{id} to track progress.",
"data": {
"id": "usr-abc12345-my-api",
"name": "my-api",
"template": "nodejs",
"type": "nodejs",
"plan": "basic",
"status": "pending",
"port": 8005,
"cpu_allocated": 1,
"memory_allocated": 1024,
"storage_allocated": 10240,
"createdAt": "2026-05-17T16:33:23.116Z",
"hasWebUI": false,
"sshUser": "usr-abc12345",
"sshPort": 8005,
"publicHost": "stacks.hosting",
"hasSshAccess": true,
"vpcNetwork": "vpc-usr-abc12345",
"imageTag": "stackshosting/nodejs:latest",
"version": "22",
"versionMajor": "22"
}
}
Polling for completion:
# Poll until status changes from "pending" to "running"
curl -H "Authorization: Bearer sk_stacks_YOUR_KEY" \
https://api.stackshosting.cloud/api/v1/containers/status/usr-abc12345-my-api
Containers typically transition from pending to running within 10-30 seconds. If a container enters error status, check logs or contact support.
Start / Stop / Restart Container
Update a container’s running state.
PUT /containers/{container_id}/status
Required scope: containers:manage
Request body:
{
"status": "running"
}
| Value | Action |
|---|---|
running |
Start the container |
stopped |
Stop the container |
restart |
Restart the container |
Delete Container
Permanently delete a container and all its data.
DELETE /containers/{container_id}
Required scope: containers:delete
Warning: This action is irreversible. All container data, volumes, and backups will be permanently removed.
Download SSH Certificate
Download the SSH private key for a container.
GET /containers/{container_id}/certificate
Required scope: containers:read
Note: Only available for compute containers (Ubuntu, Node.js, Python, etc.), not managed services.
Regenerate SSH Certificate
Generate a new SSH keypair for a container.
POST /containers/{container_id}/certificate/regenerate
Required scope: containers:manage
List Available Templates
Get all container templates with descriptions, categories, and pricing.
GET /containers/templates
Required scope: containers:read
External Access & IP Restrictions
Manage public access to your containers and control which IP addresses can connect.
Toggle External Access
Enable or disable public access for a container.
PUT /containers/{container_id}/external-access
Required scope: containers:manage
Request body:
{
"enabled": true
}
Response (when enabling):
{
"success": true,
"message": "External access enabled. Your container is available at: myapp-ab1c2d.stackshosting.cloud",
"your_ip": "203.0.113.45",
"subdomain": "ab1c2d",
"url": "myapp-ab1c2d.stackshosting.cloud"
}
When you enable external access, your current IP is automatically added to the allowed list. Disabling removes the subdomain and clears all IP restrictions.
List Allowed IPs
GET /containers/{container_id}/allowed-ips
Required scope: containers:read
Response:
{
"success": true,
"data": {
"external_access_enabled": true,
"subdomain": "ab1c2d",
"url": "myapp-ab1c2d.stackshosting.cloud",
"allowed_ips": [
{
"id": "ip-uuid-here",
"ip_address": "203.0.113.45",
"description": "Your IP (Auto-added)",
"service_type": "both",
"added_at": "2026-05-16T18:00:00.000Z"
}
],
"your_current_ip": "203.0.113.45"
}
}
Add Allowed IP
POST /containers/{container_id}/allowed-ips
Required scope: containers:manage
Request body:
{
"ip_address": "198.51.100.0/24",
"description": "Production servers",
"service_type": "both"
}
| Field | Required | Description |
|---|---|---|
ip_address |
Yes | IPv4 or IPv6 address, with optional CIDR notation |
description |
No | Human-readable label (default: “Custom IP”) |
service_type |
No | http, tcp, or both (default: both) |
Remove Allowed IP
DELETE /containers/{container_id}/allowed-ips/{ip_id}
Required scope: containers:manage
Note: You cannot remove the last IP address — this prevents accidental lockout.
Container Connections
Connect containers within your VPC network. Connections inject environment variables (host, port, credentials) into the source container so it can communicate with the target.
List Available Targets
Get containers that can be connected to.
GET /containers/{container_id}/available-targets
Required scope: containers:read
Response:
{
"success": true,
"data": {
"databases": [
{"id": "usr-abc12345-mydb", "name": "mydb", "type": "postgresql", "suggested": true}
],
"caches": [
{"id": "usr-abc12345-myredis", "name": "myredis", "type": "redis", "suggested": true}
],
"loadBalancers": [],
"applications": [],
"all": [...]
}
}
Create Connection
POST /containers/{source_id}/connect/{target_id}
Required scope: containers:manage
Request body (optional):
{
"connectionName": "primary-db"
}
Response:
{
"success": true,
"data": {
"id": "connection-uuid-here",
"sourceContainer": "usr-abc12345-myapp",
"targetContainer": "usr-abc12345-mydb",
"connectionName": "primary-db",
"connectionType": "postgresql",
"environmentVariables": {
"POSTGRES_HOST": "usr-abc12345-mydb",
"POSTGRES_PORT": "5432",
"POSTGRES_USER": "postgres",
"POSTGRES_PASSWORD": "****",
"POSTGRES_DB": "postgres"
},
"connectionString": "postgresql://postgres:****@usr-abc12345-mydb:5432/postgres",
"message": "Connection created. Container will be updated with new environment variables.",
"note": "You may need to restart the source container for changes to take effect."
}
}
List Connections
GET /containers/{container_id}/connections
Required scope: containers:read
Response:
{
"success": true,
"data": {
"outgoing": [
{
"id": "connection-uuid-here",
"connection_name": "primary-db",
"connection_type": "postgresql",
"connection_string": "postgresql://postgres:****@usr-abc12345-mydb:5432/postgres",
"created_at": "2026-05-16T18:00:00.000Z",
"target_name": "mydb",
"target_type": "postgresql",
"target_status": "running"
}
],
"incoming": []
}
}
Delete Connection
DELETE /containers/connections/{connection_id}
Required scope: containers:manage
Note: The path is /containers/connections/{id}, not /connections/{id}.
Backups
Daily automated backups run at 2:00 AM UTC with 7-day retention on replicated storage.
List Backups
GET /containers/{container_id}/backups
Required scope: containers:read
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
integer | 50 | Number of backups to return (max: 100) |
Response:
{
"success": true,
"data": [
{
"id": 178,
"container_id": "usr-abc12345-mydb",
"container_name": "mydb",
"backup_type": "scheduled",
"status": "completed",
"snapshot_id": "fc26902e",
"size_bytes": 746862,
"backup_server": "cluster1host3",
"created_at": "2026-05-17T02:00:14.000Z",
"completed_at": "2026-05-17T02:00:16.000Z",
"error_message": null,
"pinned": 0
}
]
}
Trigger Manual Backup
POST /containers/{container_id}/backup
Required scope: containers:manage
Container must be in running status. Backup runs in the background.
Response (202 Accepted):
{
"success": true,
"message": "Backup started in background",
"data": {
"container_id": "usr-abc12345-mydb",
"status": "in_progress"
}
}
Backup Statistics
Get backup stats across all your containers.
GET /backups/stats
Required scope: containers:read
Response:
{
"success": true,
"data": {
"total_backups": 32,
"completed_backups": "32",
"failed_backups": "0",
"total_size_bytes": "5933384",
"last_backup_at": "2026-05-17T17:08:02.000Z"
}
}
Restore Backup
POST /backups/{backup_id}/restore
Required scope: containers:manage
Container must be in running status.
Pin Backup
Protect a backup from automatic cleanup.
POST /backups/{backup_id}/pin
Required scope: containers:manage
Unpin Backup
Remove cleanup protection from a backup.
POST /backups/{backup_id}/unpin
Required scope: containers:manage
Delete Backup
DELETE /backups/{backup_id}
Required scope: containers:delete
Backup details:
- Scheduled daily at 2:00 AM UTC
- 7-day retention by default
- Stored on replicated GlusterFS storage
- Pin important backups to protect from auto-cleanup
- Manual backups can be triggered at any time
VPN Management
Manage WireGuard VPN clients for STACKS VPN containers.
List VPN Clients
GET /containers/{vpn_container_id}/vpn-clients
Add VPN Client
POST /containers/{vpn_container_id}/vpn-clients
Request body:
{
"name": "my-laptop"
}
Download VPN Config
GET /containers/{vpn_container_id}/vpn-config?client=default
Remove VPN Client
DELETE /containers/{vpn_container_id}/vpn-clients/{client_name}
Available Templates
Micro Tier ($1/month — 512MB RAM, 0.1 vCPU, 5GB storage)
| Template ID | Name | Type | Description |
|---|---|---|---|
ubuntu-micro |
Ubuntu Micro | System | Lightweight Ubuntu 24.04 |
nginx-micro |
Nginx Micro | Web Server | Lightweight web server for static sites |
redis-micro |
Redis Micro | Database | Redis 8.4 cache |
pocketbase-micro |
PocketBase Micro | Database | PocketBase 0.37 realtime backend |
rqlite-micro |
RQLite Micro SQL | Database | SQLite over HTTP API |
storage-micro |
MinIO Micro | Storage | S3-compatible object storage |
secrets-micro |
STACKS Secrets | Infrastructure | Encrypted secrets management |
loadbalancer-micro |
Load Balancer Micro | Infrastructure | Simple load balancer (2-3 backends) |
forgejo-micro |
Forgejo Micro | Application | Self-hosted Git server |
picoclaw-micro |
PicoClaw Agent Micro | Agent | AI agent with persistent memory |
Basic Tier ($4/month — 1 vCPU, 1GB RAM, 10GB storage)
| Template ID | Name | Type | Description |
|---|---|---|---|
ubuntu |
Ubuntu | System | Ubuntu 24.04 LTS with SSH access |
nodejs |
Node.js | Runtime | Node.js 22 LTS with PM2 process manager |
python |
Python | Runtime | Python 3.14 with Flask and SSH access |
postgresql |
PostgreSQL | Database | PostgreSQL 18 database server |
mongodb |
MongoDB | Database | MongoDB 7.0 NoSQL database |
mysql |
MySQL | Database | MySQL 8.4 database server |
redis |
Redis | Database | Redis 8.4 in-memory data store |
pocketbase |
PocketBase | Database | Open source backend with realtime database |
wordpress |
WordPress | Application | WordPress 6.9 with PHP 8.3 |
ghost |
Ghost | Application | Professional publishing and blogging platform |
forgejo |
Forgejo | Application | Self-hosted Git server with issues, PRs, and CI |
php-apache |
PHP + Apache | Application | PHP 8.5 with Apache |
nginx |
Nginx | Web Server | High-performance web server |
minio |
MinIO | Storage | S3-compatible object storage |
stacks-loadbalancer |
STACKS LoadBalancer | Infrastructure | Load balancer for distributing traffic |
picoclaw |
PicoClaw Agent | Agent | AI agent with persistent memory and skills |
Other Tiers
| Template ID | Tier | Price | Resources | Description |
|---|---|---|---|---|
stacks-vpn |
VPN | $5/month | 2GB RAM, 1 vCPU, 20GB | Private VPN access to your container network |
Container Plans
| Plan | CPU | RAM | Storage | Price |
|---|---|---|---|---|
micro |
0.1 vCPU | 512 MB | 5 GB | $1/month |
basic |
1 vCPU | 1 GB | 10 GB | $4/month |
standard |
1 vCPU | 2 GB | 20 GB | $5/month |
premium |
2 vCPU | 4 GB | 40 GB | $10/month |
All plans include VPC network isolation, per-minute billing (capped at monthly rate), and 3x data replication.
Additional Storage
Available for non-micro, non-database containers:
| Tier | Size | Price |
|---|---|---|
1 |
32 GB | $1/month |
3 |
64 GB | $3/month |
5 |
128 GB | $5/month |
Error Codes
| Status | Meaning |
|---|---|
202 |
Accepted — request is being processed (container creation, backup trigger). Poll for status. |
400 |
Bad request — check your request body and parameters. Validation errors include field and specific details. |
401 |
Unauthorized — invalid or missing API key |
402 |
Payment required — add a payment method or spending cap reached |
403 |
Forbidden — insufficient scope or endpoint not available to API keys |
404 |
Not found — container doesn’t exist or doesn’t belong to your account |
429 |
Rate limited — check Retry-After header and slow down |
500 |
Server error — contact support |
503 |
No capacity — you’ve been added to the waitlist |
Common Error Responses
Invalid API key:
{
"error": "Invalid API key"
}
Insufficient scope:
{
"error": "API key lacks required scope: containers:delete",
"required_scope": "containers:delete",
"key_scopes": ["containers:read", "containers:create"]
}
Spending cap exceeded:
{
"error": "Monthly spending cap reached",
"spending_cap": 10.00,
"amount_spent": 10.00,
"message": "Your API key spending cap of $10.00/month has been reached."
}
Name validation error:
{
"success": false,
"error": "Container name must be no more than 32 characters",
"field": "name"
}
Endpoint blocked for API keys:
{
"error": "This endpoint requires dashboard access (JWT authentication)",
"hint": "This operation requires human approval via the dashboard."
}
Common Workflows
Deploy an Application
# 1. Create a Node.js container (returns 202 with status "pending")
curl -X POST \
-H "Authorization: Bearer sk_stacks_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"my-app","template":"nodejs","plan":"basic"}' \
https://api.stackshosting.cloud/api/v1/containers
# 2. Poll status until "running"
curl -H "Authorization: Bearer sk_stacks_YOUR_KEY" \
https://api.stackshosting.cloud/api/v1/containers/status/usr-YOUR_ID-my-app
# 3. Get SSH credentials
curl -H "Authorization: Bearer sk_stacks_YOUR_KEY" \
https://api.stackshosting.cloud/api/v1/containers/usr-YOUR_ID-my-app/credentials
# 4. Deploy your code via SSH using the returned credentials
Connect App to Database
# 1. Create a PostgreSQL database
curl -X POST \
-H "Authorization: Bearer sk_stacks_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"my-db","template":"postgresql","plan":"basic"}' \
https://api.stackshosting.cloud/api/v1/containers
# 2. Wait for container to be running, then connect
curl -X POST \
-H "Authorization: Bearer sk_stacks_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"connectionName":"primary-db"}' \
https://api.stackshosting.cloud/api/v1/containers/usr-YOUR_ID-my-app/connect/usr-YOUR_ID-my-db
# 3. Restart your app to pick up the connection environment variables
curl -X PUT \
-H "Authorization: Bearer sk_stacks_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"status":"restart"}' \
https://api.stackshosting.cloud/api/v1/containers/usr-YOUR_ID-my-app/status
Expose a Container Publicly with IP Restrictions
# 1. Enable external access
curl -X PUT \
-H "Authorization: Bearer sk_stacks_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled":true}' \
https://api.stackshosting.cloud/api/v1/containers/usr-YOUR_ID-mydb/external-access
# 2. Add additional allowed IPs
curl -X POST \
-H "Authorization: Bearer sk_stacks_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"ip_address":"198.51.100.0/24","description":"Production servers","service_type":"both"}' \
https://api.stackshosting.cloud/api/v1/containers/usr-YOUR_ID-mydb/allowed-ips
# 3. List current restrictions
curl -H "Authorization: Bearer sk_stacks_YOUR_KEY" \
https://api.stackshosting.cloud/api/v1/containers/usr-YOUR_ID-mydb/allowed-ips
Backup Before a Risky Change
# 1. Trigger a manual backup
curl -X POST \
-H "Authorization: Bearer sk_stacks_YOUR_KEY" \
https://api.stackshosting.cloud/api/v1/containers/usr-YOUR_ID-mydb/backup
# 2. Verify it completed
curl -H "Authorization: Bearer sk_stacks_YOUR_KEY" \
https://api.stackshosting.cloud/api/v1/containers/usr-YOUR_ID-mydb/backups
# 3. Pin the backup so it won't be auto-cleaned
curl -X POST \
-H "Authorization: Bearer sk_stacks_YOUR_KEY" \
https://api.stackshosting.cloud/api/v1/backups/BACKUP_ID/pin
AI Agent Workflow
Give your AI agent an API key with a spending cap:
- Create an API key with
containers:read,containers:create,containers:managescopes and a $10/month spending cap - Pass the key to your agent
- The agent can create containers, deploy code, and manage infrastructure
- If the spending cap is reached, the API rejects further container creation — your agent can’t run up the bill
- You get billed monthly as usual through your payment method
Revoking an API Key
- Go to Account Settings → API Keys
- Find the key you want to revoke
- Click the revoke button (X icon)
- Confirm the revocation
Revocation is immediate and permanent. Any system using the revoked key will instantly lose access. There is no way to un-revoke a key — create a new one instead.
Need Help?
- Support: support@stackshosting.cloud
- Documentation: https://docs.stackshosting.cloud
- Status Page: https://status.stackshosting.cloud