Skip to content
Hoody.com

The Hoody API is the control plane. You use it to create projects and containers, configure their networking and proxy settings, and manage your account.

The Vision covers why Hoody is built this way. This page covers how the API is organized and what each group of endpoints does.


This Foundation page explains how the Hoody API works. The reference pages below carry the complete endpoint documentation, with parameters and responses.

Core management:

Networking and security:

Proxy and routing:

Data and state:


Hoody exposes two HTTP surfaces, and they do different jobs:

Hoody API (Platform Management)

https://api.hoody.com

What it controls:

  • User authentication
  • Project creation
  • Container spawning
  • Network configuration
  • Firewall rules
  • Proxy aliases
  • Snapshots
  • Billing

Mental model: “The dashboard API”

Container Services (Hoody Kit)

https://{project}-{container}-terminal-1.node-sg-sin-1.containers.hoody.com
https://{project}-{container}-display-1.node-sg-sin-1.containers.hoody.com
https://{project}-{container}-files-1.node-sg-sin-1.containers.hoody.com

What they provide:

  • Terminal execution
  • Desktop access
  • File operations
  • Database queries
  • Browser automation
  • Script execution
  • +12 more services

Mental model: “The containers themselves”

The two are used in sequence:

  1. Use the Hoody API to spawn a container
  2. The container gets URLs for all its services automatically
  3. Use those URLs to work with the container

The Hoody API creates the infrastructure. The container URLs are how you use it.


The endpoints fall into seven areas.

Manage your account and create access credentials:

Terminal window
# Login as a user
hoody auth login --username your_username --password your_password
# Create long-lived API token for automation
hoody auth create --alias "my-automation-token" --expires-at "2027-04-12T00:00:00Z"
# Get current user profile
hoody auth profile current

See: Authentication → | API Reference →

Projects are the folders that hold your containers:

Terminal window
# Create a project
hoody projects create --alias "my-project"
# List your projects
hoody projects list

See: Projects & Containers → | API Reference →

Create an isolated container inside a project:

Terminal window
# Spawn a container in a project
hoody containers create --project $PROJECT_ID --server-id $SERVER_ID --name "dev-env"
# Container URLs are automatically constructed:
# https://{project_id}-{container_id}-terminal-1.{server_name}.containers.hoody.com
# https://{project_id}-{container_id}-display-1.{server_name}.containers.hoody.com
# ... plus files, exec, agent, sqlite, curl, cron, pipe, n, browser, code, daemon, notes, watch, run, logs, tunnel, and dynamic http/https ports

The container is running with all its HTTP services live within 1-5 seconds.

See: Container Lifecycle → | API Reference →

Configure how containers connect and communicate:

Terminal window
# Configure firewall rules
POST https://api.hoody.com/api/v1/containers/{id}/firewall/ingress
# Route traffic through proxies/VPNs
PATCH https://api.hoody.com/api/v1/containers/{id}/network
# Add an outbound firewall rule
POST https://api.hoody.com/api/v1/containers/{id}/firewall/egress

See: Networking → | Firewall →

Give a container a shorter, custom URL and control who can reach it:

Terminal window
# Create custom alias: my-app.$serverName.containers.hoody.com
POST https://api.hoody.com/api/v1/proxy/aliases
# Configure permissions
PATCH https://api.hoody.com/api/v1/containers/{id}/proxy/permissions

See: Hoody Proxy → | Aliases →

Manage persistent data and state:

Terminal window
# Snapshot a container (capture complete state)
POST https://api.hoody.com/api/v1/containers/{id}/snapshots
# Share directories between containers
POST https://api.hoody.com/api/v1/containers/{id}/storage/shares

See: Snapshots → | Storage Shares →

Inspect the servers and images behind your containers:

Terminal window
# List your active server rentals
GET https://api.hoody.com/api/v1/rentals
# Manage container images
GET https://api.hoody.com/api/v1/images/public

See: Servers → | Images →


The Hoody API is plain REST over HTTP, which has two practical consequences.

LLMs are trained on HTTP, so they already know how to construct JSON payloads, make authenticated requests, parse responses, and handle errors.

There is no SDK to install. An agent can drive your whole infrastructure over HTTP:

// An agent can write this from the endpoint list alone
const workflow = [
{
description: "Create project for client",
call: "POST https://api.hoody.com/api/v1/projects/",
body: { alias: "client-acme", color: "#3498db" }
},
{
description: "Spawn 3 containers: frontend, backend, database",
call: "POST https://api.hoody.com/api/v1/projects/{project_id}/containers",
repeat: 3,
body: { server_id: "...", hoody_kit: true, dev_kit: true }
},
{
description: "Configure firewall for database",
call: "POST https://api.hoody.com/api/v1/containers/{db_id}/firewall/ingress",
body: { action: "allow", protocol: "tcp", description: "Allow backend to database", destination_port: "5432", source: "{backend_ip}" }
}
];
// Each step runs over plain HTTP, without a custom SDK
for (const step of workflow) {
const [method, url] = step.call.split(' ');
const response = await fetch(url, {
method,
headers: {
'Authorization': `Bearer ${process.env.HOODY_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(step.body)
});
}

Every programming language has HTTP libraries:

Terminal window
# List all projects
hoody projects list

The SDK is optional. Any language with an HTTP client can call the API directly, including JavaScript, Python, Go, and Ruby.


1. AUTHENTICATE
POST /api/v1/users/auth/login
→ Receive JWT tokens
2. CREATE AUTH TOKEN (for automation)
POST /api/v1/auth/tokens
→ Get hdy_... token with IP whitelist, expiration
→ Use this in scripts/AI instead of user credentials
3. CREATE PROJECT
POST /api/v1/projects/
→ Get project_id
4. SPAWN CONTAINER
POST /api/v1/projects/{project_id}/containers
→ Get container_id, server_name
→ Container URLs automatically available:
• https://{project_id}-{container_id}-terminal-1.{server_name}.containers.hoody.com
• https://{project_id}-{container_id}-display-1.{server_name}.containers.hoody.com
• https://{project_id}-{container_id}-exec-1.{server_name}.containers.hoody.com
• ... plus files, agent, sqlite, curl, cron, pipe, n, browser, code, daemon, notes, watch, run, logs, tunnel, and dynamic http/https ports
5. CONFIGURE (optional)
PATCH /api/v1/containers/{id}/network → Route through VPN
POST /api/v1/containers/{id}/firewall/ingress → Add inbound firewall rule
POST /api/v1/containers/{id}/firewall/egress → Add outbound firewall rule
POST /api/v1/proxy/aliases → Create custom domain
6. USE CONTAINER SERVICES
# Now use the container URLs directly
POST https://{project}-{container}-terminal-1.{server}.containers.hoody.com/api/v1/terminal/execute
GET https://{project}-{container}-files-1.{server_name}.containers.hoody.com/api/v1/files/home/

The endpoint reference is grouped by area:


Almost every request requires authentication. Login and a few public endpoints, such as GET /api/v1/notifications/public, are the exceptions.

Terminal window
# Login (stores credentials locally)
hoody auth login --username your_username --password your_password
# All subsequent commands use the stored token
hoody projects list
hoody containers list

For automation and AI, use Auth Tokens: long-lived, IP-restricted, and revocable. For user sessions, use the JWT from login, which is short-lived and refreshable.

The standard error response:

{
"statusCode": 400,
"error": "Bad Request",
"message": "Detailed explanation of what went wrong"
}

Common status codes:

  • 400 - Bad Request (validation errors)
  • 401 - Unauthorized (missing/invalid token)
  • 403 - Forbidden (insufficient permissions)
  • 404 - Not Found (resource doesn’t exist)
  • 409 - Conflict (duplicate name, invalid state)
  • 500 - Internal Server Error

List endpoints support pagination:

Terminal window
GET /api/v1/projects/?page=1&limit=20&sort_by=created_at&sort_order=desc

The response includes pagination metadata:

{
"data": {
"projects": [...],
"pagination": {
"total": 150,
"page": 1,
"limit": 20,
"totalPages": 8
}
}
}

Many endpoints support filtering:

Terminal window
# Filter containers by realm
GET /api/v1/containers/?realm_id=507f1f77bcf86cd799439011
# Sort by status
GET /api/v1/containers/?sort_by=status&sort_order=desc
# Sort by creation date
GET /api/v1/projects/?sort_by=created_at&sort_order=desc

Most platforms manage infrastructure through one of three surfaces:

  • CLI tools (installed binaries, version conflicts)
  • Custom SDKs (language-specific, maintenance burden)
  • Proprietary protocols (hard to debug)

Hoody uses HTTP, so the control plane is:

  • Callable from any language and any device
  • Understood natively by AI agents
  • Reachable with curl, from a script, or from a browser
  • Observable, debuggable, and auditable
  • Composable with any other HTTP service

The same provisioning flow from an AI agent, using only fetch:

// Provision a client project: containers, alias, firewall rule
async function deployClientProject(clientName) {
const token = process.env.HOODY_TOKEN;
const headers = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
};
// 1. Create project
const project = await fetch('https://api.hoody.com/api/v1/projects/', {
method: 'POST',
headers,
body: JSON.stringify({
alias: `client-${clientName}`,
color: '#3498db',
max_containers: 50
})
}).then(r => r.json());
// 2. Spawn 3 containers (frontend, backend, database)
const containers = await Promise.all([
'frontend', 'backend', 'database'
].map(name =>
fetch(`https://api.hoody.com/api/v1/projects/${project.data.id}/containers`, {
method: 'POST',
headers,
body: JSON.stringify({
name,
server_id: 'your-server-id',
hoody_kit: true,
dev_kit: true
})
}).then(r => r.json())
));
// 3. Create production alias
await fetch('https://api.hoody.com/api/v1/proxy/aliases', {
method: 'POST',
headers,
body: JSON.stringify({
container_id: containers[0].data.id,
alias: `${clientName}-app`,
program: 'http',
port: 80
})
});
// 4. Configure firewall for database
await fetch(`https://api.hoody.com/api/v1/containers/${containers[2].data.id}/firewall/ingress`, {
method: 'POST',
headers,
body: JSON.stringify({
action: 'allow',
protocol: 'tcp',
destination_port: '5432',
source: '10.0.1.30/32', // backend container's private IP/CIDR
description: 'Allow backend to database'
})
});
return {
projectId: project.data.id,
containers: containers.map(c => ({
name: c.data.name,
terminalUrl: `https://${project.data.id}-${c.data.id}-terminal-1.${c.data.server_name}.containers.hoody.com`,
displayUrl: `https://${project.data.id}-${c.data.id}-display-1.${c.data.server_name}.containers.hoody.com`
}))
};
}

Global API:

https://api.hoody.com

Realm-scoped API (for multi-tenant isolation):

https://{realmId}.api.hoody.com

When you use a realm-scoped URL:

  • The subdomain realm must be a 24-char hex ID
  • Read operations are scoped to resources in that realm
  • Create/update operations preserve or merge that realm where supported
  • Container realm_ids are set independently of the parent project’s realm_ids
  • API tokens can be restricted to specific realms
  • Realm-restricted tokens can bootstrap via GET /api/v1/auth/tokens/me on base host

Realm scoping is how a multi-tenant SaaS keeps each tenant’s API calls separated.

See: Realms → for realm-based API isolation.


All responses follow this structure:

{
"statusCode": 200,
"message": "Human-readable success message",
"data": {
// The actual response data
}
}

Errors include details:

{
"statusCode": 400,
"error": "Bad Request",
"message": "Container name must be unique within project"
}

Your first API calls:

POST Login with username and password to get JWT access token
/api/v1/users/auth/login
Click "Run" to execute the request

Gives you: JWT access token

Four API calls give you a running container with terminal, display, files, database, and 14 more HTTP services.


What’s the difference between the API and container URLs?

Section titled “What’s the difference between the API and container URLs?”

The Hoody API (api.hoody.com) manages your infrastructure: creating containers, configuring networks, and billing. Container service URLs ({project}-{container}-terminal-1.{server}.containers.hoody.com) are the containers themselves, where you execute commands, access files, and run applications.

Think of it like AWS: the AWS Console (Hoody API) vs. your EC2 instance (container URLs).

Can I use the Hoody API without the Hoody Kit?

Section titled “Can I use the Hoody API without the Hoody Kit?”

Yes. Set hoody_kit: false when creating a container to get a plain Linux container without the 18 HTTP services. You still use the Hoody API to manage it, but the container has no terminal, files, or display HTTP endpoints: only SSH and whatever you install yourself.

Do I need different auth tokens for different projects?

Section titled “Do I need different auth tokens for different projects?”

Not required, but recommended for blast-radius control. One token can cover multiple projects, while per-app/per-realm tokens are easier to audit and revoke.

How quickly can I spawn a container via the API?

Section titled “How quickly can I spawn a container via the API?”

Typically 1-5 seconds from API call to a running container with all services live. Prespawn Templates cut this to sub-second by keeping pools of pre-created containers.

Yes. An agent needs only a Hoody auth token, usually from an environment variable, and can then drive your whole infrastructure with standard HTTP requests. LLMs are trained on web data, so HTTP itself needs no explanation, and no SDK is required.

What happens if I delete a project via the API?

Section titled “What happens if I delete a project via the API?”

All containers in that project are immediately terminated and deleted. The deletion is permanent and cannot be undone, so snapshot anything you need before deleting a project. The CLI and MCP surfaces gate this with an interactive confirmation prompt; a direct HTTP call proceeds with no extra confirm parameter, so handle the prompt in your own tooling.

Can I automate infrastructure with GitHub Actions?

Section titled “Can I automate infrastructure with GitHub Actions?”

Yes. Store your Hoody auth token as a GitHub Secret (HOODY_TOKEN), then call the API with curl or any HTTP library from your workflow. A common pattern is deploying on push by creating or updating containers through the API.

The current limits are set for automation: you can spawn dozens of containers per minute. When you exceed one, the API returns 429 Too Many Requests with retry timing. Contact support if you need higher limits for enterprise-scale automation.

Can I scope operations to specific realms?

Section titled “Can I scope operations to specific realms?”

Yes. Use realm-scoped API URLs: https://{realmId}.api.hoody.com instead of https://api.hoody.com.

Key rules:

  • {realmId} must be a 24-hex realm ID.
  • Realm-restricted tokens (realm_ids non-empty or allow_no_realm: false) must use realm-scoped URLs for resource operations.
  • GET /api/v1/auth/tokens/me is the bootstrap endpoint for discovering allowed realms.

What’s the maximum number of containers I can create?

Section titled “What’s the maximum number of containers I can create?”

There’s no account-wide cap, but two limits are enforced at every creation. First, a per-server cap: free-pool servers default to 10 containers each (FREE_SERVER_MAX_CONTAINERS), and any server can carry an explicit max_containers. Second, a per-project quota: the project’s max_containers field (null = unlimited). Exceeding either fails the create with 400. Beyond those, the practical limits are server resources (CPU/RAM) and organization (managing hundreds of containers becomes complex). Use projects to organize, and consider prespawn templates for container pooling at scale.


Problem: All API requests return 401 Unauthorized

Solutions:

  1. Check token is included:

    Terminal window
    # Ensure Authorization header is present
    curl -v "https://api.hoody.com/api/v1/projects/" \
    -H "Authorization: Bearer $HOODY_TOKEN"
    # Look for: > Authorization: Bearer hdy_...
  2. Verify token format:

    Terminal window
    # JWT tokens start with: eyJ...
    # Auth tokens start with: hdy_...
    echo $HOODY_TOKEN
  3. Check token expiration:

GET List auth tokens to check expires_at field
/api/v1/auth/tokens
Click "Run" to execute the request
  1. Re-authenticate:
POST Login again to get fresh JWT
/api/v1/users/auth/login
Click "Run" to execute the request

Problem: Auth Token returns 403 Forbidden

Cause: Your current IP is not in the token’s IP whitelist

Check your IP:

GET Get token details to check IP whitelist
/api/v1/auth/tokens/{token_id}
Click "Run" to execute the request

Compare ip_whitelist with your current IP (run curl https://ifconfig.me in terminal).

Solutions:

  1. Update whitelist to include your IP:
PATCH Update token IP whitelist
/api/v1/auth/tokens/{token_id}
Click "Run" to execute the request
  1. Create new token without IP restrictions:
POST Create unrestricted token
/api/v1/auth/tokens
Click "Run" to execute the request

Problem: Resource not found errors

Common causes:

  1. Wrong ID format:

    Terminal window
    # IDs must be 24-character hex
    # Wrong: abc123
    # Correct: 507f1f77bcf86cd799439011
  2. Resource doesn’t exist:

    Terminal window
    # Verify resource exists
    GET /api/v1/projects/ # List all projects
    GET /api/v1/containers/ # List all containers
  3. Wrong endpoint path:

    /api/v1/project/507f1f77bcf86cd799439011
    # Correct: /api/v1/projects/507f1f77bcf86cd799439011

Problem: Can’t reach api.hoody.com

Solutions:

  1. Check internet connection:

    Terminal window
    ping api.hoody.com
  2. Verify DNS resolution:

    Terminal window
    dig api.hoody.com
    # Should return IP address
  3. Test with curl verbose:

    Terminal window
    curl -v "https://api.hoody.com/api/v1/projects/" \
    -H "Authorization: Bearer $HOODY_TOKEN"
    # Look for TLS handshake and connection details
  4. Check firewall/proxy:

    • Corporate firewall might block HTTPS
    • VPN might interfere with connections
    • Try from different network

Problem: 429 Too Many Requests

Solution: the Hoody API rate limits are set for automation. If you hit one:

  1. Add delays between requests:

    for (const item of items) {
    await fetch(apiUrl, options);
    await new Promise(r => setTimeout(r, 100)); // 100ms delay
    }
  2. Batch operations where possible:

    Terminal window
    # Instead of 10 separate container creates
    # Create them with delay or use prespawn pools

If the problem persists:

  1. Review error message - Hoody returns detailed error messages in JSON
  2. Contact support with:
    • Request method and endpoint
    • Request headers (mask auth token)
    • Error response
    • Timestamp of failure

Understand the foundation:

  1. Authentication → - How to authenticate (JWTs vs Auth Tokens)
  2. Projects & Containers → - How Hoody groups containers into projects
  3. Hoody Proxy → - How every container feature becomes a URL

See the complete endpoint documentation: