Skip to content
Hoody.com

Nearly all Hoody API operations require authentication. Only a few endpoints are public, such as GET /api/v1/notifications/public. There are two authentication systems, one for interactive user sessions and one for automation.

For what the API does (platform management), see the Hoody API overview.


This Foundation page explains how authentication works and how to use it safely. For the complete endpoint reference:

User authentication (JWT tokens):

Automation (Auth Tokens):


JWT tokens (user sessions)

Use for: browser sessions, interactive work

Terminal window
POST /api/v1/users/auth/login

Returns:

  • token (access, 1 day)
  • refreshToken (7 days)

Characteristics:

  • Short-lived
  • Refreshable without re-login
  • Tied to one user account
  • Not suited to automation

Auth Tokens (automation)

Use for: scripts, AI agents, CI/CD, integrations

Terminal window
POST /api/v1/auth/tokens

Returns:

  • hdy_... token (long-lived)

Characteristics:

  • Long-lived, with a configurable expiry
  • IP whitelist support
  • Revocable at any time
  • Per-token permissions
  • Suited to automation

Step 1: Log in

Terminal window
# Login with email and password (or use --username instead of --email)
hoody login --email you@example.com --password your_password
POST Login with username and password
/api/v1/users/auth/login
Click "Run" to execute the request

Response:

{
"statusCode": 200,
"message": "Login successful",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "63f8b0e5c9a1b2d3e4f5a6b7",
"username": "your_username",
"alias": "Your Display Name",
"is_banned": false,
"created_at": "2025-10-21T10:00:00.000Z",
"updated_at": "2025-10-21T10:00:00.000Z"
}
}
}

That response assumes the account has no second factor. When 2FA is enabled and verified, login still answers 200, but the body is a challenge rather than a session, with no token and no refreshToken:

{
"statusCode": 200,
"message": "2FA verification required",
"data": {
"requires_2fa": true,
"temp_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"method": "totp"
}
}

Check data.requires_2fa before reading data.token, or a client written against the first shape will read undefined and look like a broken login. The temp_token lasts five minutes and is single-use. Exchange it at POST /api/v1/users/auth/2fa/verify, passing a six-digit code from the authenticator app or a ten-character backup code, and that call returns the same token and refreshToken pair shown above:

Terminal window
curl -X POST "https://api.hoody.com/api/v1/users/auth/2fa/verify" \
-H "Content-Type: application/json" \
-d '{"temp_token": "TEMP_TOKEN", "code": "123456"}'

The authentication reference documents the rest of the 2FA endpoints, including setup and backup-code regeneration.

Step 2: Use the access token

Terminal window
# CLI stores the token automatically after login
hoody projects list

Step 3: Refresh when the token expires

Terminal window
# Refresh explicitly using the refresh token saved by login
hoody auth refresh
# If the refresh token has expired, log in again:
hoody login --username your_username --password your_password

The refresh token must be sent both in the Authorization: Bearer header and in the refreshToken body field. The server verifies the header token and rejects the request with 401 Invalid refresh token if the two do not match byte for byte.

Returns: a new access token and a new refresh token, both rotated.

Step 4: Log out

Terminal window
curl -X POST "https://api.hoody.com/api/v1/users/auth/logout" \
-H "Authorization: Bearer $ACCESS_TOKEN"

Logout is more than a record. Called with a JWT, it revokes every session JWT issued to that user before the call: the access token you just used, its refresh token, and any session open in another browser or on another device. Use it when you want to end all sessions at once, not only the one in front of you.

The revocation applies only to logouts made with a JWT. The same call made with an Auth Token revokes nothing. To revoke an Auth Token, delete it at DELETE /api/v1/auth/tokens/{id} or disable it.


Step 1: Create an Auth Token (one-time setup)

Terminal window
# Login first
hoody login --username your_username --password your_password
# Create a long-lived automation token with IP whitelist
hoody auth tokens create \
--alias "Production Automation Token" \
--ip-whitelist "203.0.113.10,203.0.113.20" \
--expires-at "2027-04-12T00:00:00Z" \
--permissions-projects-read \
--permissions-containers-read \
-o json
POST Create a long-lived automation token with IP whitelist
/api/v1/auth/tokens
Click "Run" to execute the request

Response:

{
"statusCode": 201,
"message": "Auth token created successfully",
"data": {
"token": "hdy_abc123XyZ456...",
"id": "63f8b0e5c9a1b2d3e4f5a6b7",
"alias": "Production Automation Token",
"prefix": "hdy_",
"ip_whitelist": ["203.0.113.10", "203.0.113.20"],
"permissions": { "projects": { "read": true }, "containers": { "read": true } },
"vault_access": false,
"event_access": true,
"expires_at": "2027-04-12T00:00:00.000Z",
"is_enabled": true,
"last_used_at": null,
"last_used_ip": null,
"created_at": "2025-11-09T15:00:00.000Z",
"updated_at": "2025-11-09T15:00:00.000Z"
}
}

This token holds containers.read but not containers.features.env_values, so it can list a container’s environment variable names but receives each value as null. Add "features": { "env_values": true } under containers if the automation needs the values.

Step 2: Use the Auth Token (until it expires or you revoke it)

Terminal window
# Store token and use with CLI
export HOODY_TOKEN="hdy_abc123XyZ456..."
# All subsequent commands use this token
hoody projects list
hoody containers list

An Auth Token gives you:

  • Credentials that live outside your code
  • IP whitelist enforcement, restricting use to specific addresses
  • Expiration control: an ISO 8601 date, or never
  • Instant revocation, by disabling or deleting the token

alias accepts letters, digits, spaces, _ and - only (1 to 254 characters). Slashes and parentheses are rejected with 400.

IP whitelisting:

POST Create token with IP whitelist for CI/CD pipeline
/api/v1/auth/tokens
Click "Run" to execute the request

For production, replace full_access with an explicit permission tree containing the operations the automation actually needs.

expires_at accepts several formats:

{
"expires_at": "2026-12-31T23:59:59Z"
}
Terminal window
# List all your auth tokens
hoody auth tokens list
GET List all your auth tokens
/api/v1/auth/tokens
Click "Run" to execute the request

The response lists each token’s settings:

{
"statusCode": 200,
"message": "Auth tokens retrieved successfully",
"data": [
{
"id": "63f8b0e5c9a1b2d3e4f5a6b7",
"alias": "CI-CD Pipeline",
"prefix": "hdy_",
"ip_whitelist": ["203.0.113.50"],
"expires_at": "2026-02-07T15:00:00.000Z",
"is_enabled": true,
"last_used_at": null,
"last_used_ip": null,
"created_at": "2025-11-09T10:00:00.000Z",
"updated_at": "2025-11-09T10:00:00.000Z"
}
]
}

Audit your tokens:

  • Check expires_at and is_enabled on each token
  • Review ip_whitelist restrictions
  • last_used_at and last_used_ip are always null: token usage is not reported

Disable without deleting:

Terminal window
# Disable a token
hoody auth tokens update $TOKEN_ID --no-is-enabled
# Re-enable it
hoody auth tokens update $TOKEN_ID --is-enabled
PATCH Disable a token without deleting it
/api/v1/auth/tokens/{id}
Click "Run" to execute the request

Permanently delete:

Terminal window
# Permanently delete a token
hoody auth tokens delete $TOKEN_ID
DELETE Permanently delete a token
/api/v1/auth/tokens/{id}
Click "Run" to execute the request

// Do not do this
const response = await fetch('https://api.hoody.com/api/v1/projects/', {
headers: {
'Authorization': 'Bearer hdy_abc123hardcoded'
}
});

Restrict a token to specific sources:

POST Create token that only works from your office IP
/api/v1/auth/tokens
Click "Run" to execute the request

If the token leaks, it will not work from any other IP.

Short-lived, for temporary access:

{
"alias": "Contractor Access",
"expires_at": "2027-05-12T00:00:00Z",
"permission_template": "full_access"
}

Long-lived, for permanent infrastructure:

{
"alias": "Production Services",
"expires_at": "2027-04-12T00:00:00Z",
"permission_template": "full_access"
}

Review and rotate tokens regularly.

Do not share a single token across systems:

Terminal window
# Create separate tokens
POST /api/v1/auth/tokens { "alias": "GitHub Actions CI", ... }
POST /api/v1/auth/tokens { "alias": "Monitoring System", ... }
POST /api/v1/auth/tokens { "alias": "AI Agent Orchestrator", ... }

If one service is compromised, revoke only that token; the others keep working.

Every successful sign-in to your account is recorded: the IP address, the country it resolved to, the client channel, and the timestamp. GET /api/v1/users/me/security-history returns your own trail, newest first. Pass include_failed=true to add rejected attempts against your account, and include_security=true to add other security events such as 2FA changes, password resets, and token creation or revocation. A burst of failed attempts, or a sign-in from a country you have never visited, is the clearest sign someone else is trying to get in.

GET List your sign-in and security-event history
/api/v1/users/me/security-history
Click "Run" to execute the request

The trail is append-only and kept for 180 days: you cannot delete entries, and neither can anyone who gets into your account. What is recorded, what is not, and how long it is kept is covered in Auditing & Data Collection.


The Auth Token system is built for AI orchestration:

// AI agent configuration (environment variables)
const HOODY_TOKEN = process.env.HOODY_TOKEN; // hdy_... token
const HOODY_API = 'https://api.hoody.com';
// AI can now orchestrate infrastructure
async function aiAgentWorkflow(task) {
const headers = {
'Authorization': `Bearer ${HOODY_TOKEN}`,
'Content-Type': 'application/json'
};
// AI decides: "Need a container to process this task"
const container = await fetch(`${HOODY_API}/api/v1/projects/${projectId}/containers`, {
method: 'POST',
headers,
body: JSON.stringify({
name: `ai-task-${Date.now()}`,
server_id: 'your-server-id',
hoody_kit: true
})
}).then(r => r.json());
// Wait for container to be running
await waitForStatus(container.data.id, 'running');
// Get container URLs and use them
const terminalUrl = `https://${projectId}-${container.data.id}-terminal-1.${container.data.server_name}.containers.hoody.com`;
// AI executes commands in the new container
await fetch(`${terminalUrl}/api/v1/terminal/execute`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ command: task.command })
});
// AI snapshots when done
await fetch(`${HOODY_API}/api/v1/containers/${container.data.id}/snapshots`, {
method: 'POST',
headers,
body: JSON.stringify({ alias: `task-${task.id}` })
});
return container;
}

The agent needs two things:

  1. A Hoody Auth Token in an environment variable
  2. An understanding of HTTP, which a model already has

There is no SDK to install and no training step. The interface is HTTP.


FeatureJWT (Login)Auth Token (hdy_…)
Lifetime1 day (access)
7 days (refresh)
Configurable (ISO 8601 date, “today”, “tomorrow”, or forever)
Use caseUser sessionsAutomation, AI, scripts
RefreshYes (via refresh token)No (create a new one when expired)
IP whitelistNoYes (optional)
RevocationLogout, called with a JWT, revokes all previously issued session JWTs for that ownerDelete or disable
VisibilityManaged by browserShown once, at creation
SecurityShort lifetime limits exposureLong lifetime, bounded by IP whitelist

Terminal window
# 1. Login
LOGIN_RESPONSE=$(curl -X POST "https://api.hoody.com/api/v1/users/auth/login" \
-H "Content-Type: application/json" \
-d '{
"username": "dev_user",
"password": "strong_password_here"
}')
# Response includes tokens
# {
# "data": {
# "token": "eyJhbG...",
# "refreshToken": "eyJhbG...",
# "user": { ... }
# }
# }
# Capture the access and refresh tokens for steps 2-4
ACCESS_TOKEN=$(printf '%s' "$LOGIN_RESPONSE" | jq -r '.data.token')
REFRESH_TOKEN=$(printf '%s' "$LOGIN_RESPONSE" | jq -r '.data.refreshToken')
# 2. Use access token (valid 1 day)
curl "https://api.hoody.com/api/v1/projects/" \
-H "Authorization: Bearer $ACCESS_TOKEN"
# 3. Refresh before expiration (within 7 days)
# The refresh token goes in both the header and the body, and the two must match
curl -X POST "https://api.hoody.com/api/v1/users/auth/refresh" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $REFRESH_TOKEN" \
-d '{"refreshToken": "'"$REFRESH_TOKEN"'"}'
# 4. Logout: revokes every session JWT issued to this owner so far, this one included
curl -X POST "https://api.hoody.com/api/v1/users/auth/logout" \
-H "Authorization: Bearer $ACCESS_TOKEN"
Terminal window
# 1. Login to get JWT
curl -X POST "https://api.hoody.com/api/v1/users/auth/login" \
-H "Content-Type: application/json" \
-d '{"username": "your_username", "password": "your_password"}' \
> login.json
# Extract JWT
JWT=$(cat login.json | jq -r '.data.token')
# 2. Create auth token for CI/CD
curl -X POST "https://api.hoody.com/api/v1/auth/tokens" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"alias": "GitHub Actions Deployment",
"ip_whitelist": ["140.82.112.0/20"],
"permission_template": "full_access",
"expires_at": "2027-04-12T00:00:00Z"
}' > token.json
# Extract auth token
AUTH_TOKEN=$(cat token.json | jq -r '.data.token')
# 3. Save to GitHub Secrets as HOODY_TOKEN
echo "HOODY_TOKEN=$AUTH_TOKEN"
# 4. Use in GitHub Actions workflow
# - name: Deploy via Hoody API
# env:
# HOODY_TOKEN: ${{ secrets.HOODY_TOKEN }}
# run: |
# curl -X POST "https://api.hoody.com/api/v1/containers/$CONTAINER_ID/start" \
# -H "Authorization: Bearer $HOODY_TOKEN"
.env
// AI agent configuration file
HOODY_TOKEN=hdy_abc123def456...
HOODY_PROJECT_ID=63f8b0e5c9a1b2d3e4f5a6b7
// agent.js
import 'dotenv/config';
class HoodyAgent {
constructor() {
this.token = process.env.HOODY_TOKEN;
this.projectId = process.env.HOODY_PROJECT_ID;
this.api = 'https://api.hoody.com';
}
async callAPI(endpoint, options = {}) {
return fetch(`${this.api}${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json',
...options.headers
}
});
}
async spawnContainer(name, config) {
const response = await this.callAPI(
`/api/v1/projects/${this.projectId}/containers`,
{
method: 'POST',
body: JSON.stringify({
name,
server_id: config.serverId,
hoody_kit: true,
...config
})
}
);
return response.json();
}
async executeInContainer(containerId, command) {
// First get container details to construct service URL
const container = await this.callAPI(`/api/v1/containers/${containerId}`)
.then(r => r.json());
// Construct terminal URL
const terminalUrl = `https://${this.projectId}-${containerId}-terminal-1.${container.data.server_name}.containers.hoody.com`;
// Execute command (no auth needed if container permissions are open)
return fetch(`${terminalUrl}/api/v1/terminal/execute`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ command })
});
}
}
// AI uses this class for every Hoody operation
const agent = new HoodyAgent();
await agent.spawnContainer('ai-workspace', { serverId: 'server-123' });

The agent only needs environment variables. It never handles a password or manages credentials.


GET List all your auth tokens
/api/v1/auth/tokens
Click "Run" to execute the request

Change the IP whitelist:

PATCH Update token IP whitelist
/api/v1/auth/tokens/{id}
Click "Run" to execute the request

Extend the expiration:

PATCH Extend token expiration
/api/v1/auth/tokens/{id}
Click "Run" to execute the request

Temporarily disable:

PATCH Temporarily disable a token
/api/v1/auth/tokens/{id}
Click "Run" to execute the request

Production rotation:

Terminal window
# 1. Create new token
NEW_TOKEN=$(curl -X POST "https://api.hoody.com/api/v1/auth/tokens" \
-H "Authorization: Bearer YOUR_JWT" \
-H "Content-Type: application/json" \
-d '{"alias": "Production V2", "permission_template": "full_access", "expires_at": "2027-04-12T00:00:00Z"}' \
| jq -r '.data.token')
# 2. Update your services with new token
# (Deploy new environment variable to all services)
# 3. Disable the old token (reversible) and confirm every service still works
curl -X PATCH "https://api.hoody.com/api/v1/auth/tokens/{old_token_id}" \
-H "Authorization: Bearer YOUR_JWT" \
-H "Content-Type: application/json" \
-d '{"is_enabled": false}'
# 4. Delete old token
curl -X DELETE "https://api.hoody.com/api/v1/auth/tokens/{old_token_id}" \
-H "Authorization: Bearer YOUR_JWT"

For one-time operations:

POST Create a temporary token that auto-expires tomorrow
/api/v1/auth/tokens
Click "Run" to execute the request

Use the token for your migration, and it expires on its own tomorrow.

A different token for each environment:

Development token (permissive):

POST Create permissive development token
/api/v1/auth/tokens
Click "Run" to execute the request

Staging token (IP-restricted):

POST Create IP-restricted staging token
/api/v1/auth/tokens
Click "Run" to execute the request

Production token (strict):

POST Create strict production token
/api/v1/auth/tokens
Click "Run" to execute the request

If a token is compromised:

1. Disable it immediately:

PATCH Immediately disable compromised token
/api/v1/auth/tokens/{id}
Click "Run" to execute the request

2. Create a replacement with a different IP whitelist:

POST Create replacement token with new IP restrictions
/api/v1/auth/tokens
Click "Run" to execute the request

3. Update your services, then delete the old token:

DELETE Delete the compromised token after services are updated
/api/v1/auth/tokens/{id}
Click "Run" to execute the request

Should I use JWT tokens or Auth Tokens for my scripts?

Section titled “Should I use JWT tokens or Auth Tokens for my scripts?”

Use Auth Tokens (hdy_...) for scripts and automation. JWTs from login are meant for short-lived user sessions and expire after 1 day. An Auth Token can live for years, with IP whitelisting and revocation.

Can I create an Auth Token using another Auth Token?

Section titled “Can I create an Auth Token using another Auth Token?”

Yes, if the parent token holds the resources.create_tokens permission. Such a token can mint child tokens whose permissions are clamped to a subset of its own (each child’s delegation_depth is the parent’s plus one; 0 means account-minted from a JWT). Tokens without resources.create_tokens cannot create more tokens, and that includes any token created with deny_reauthorization: true, which strips the permission. Leave resources.create_tokens off for leaf tokens you hand to scripts.

How do I give my team API access without passwords?

Section titled “How do I give my team API access without passwords?”

Create individual Auth Tokens for each team member with specific IP whitelists. Each person gets their own hdy_... token, and you can revoke any token independently if someone leaves the team.

How do I check whether someone else accessed my account?

Section titled “How do I check whether someone else accessed my account?”

Call GET /api/v1/users/me/security-history. Each entry carries the event, a success or failed outcome, the IP address, the resolved country (null until resolved, and null permanently for addresses that cannot be located), the source channel, and the timestamp. Pass include_failed=true to include rejected sign-in attempts; those are only recorded when a credential was actually checked against your account, so the endpoint cannot be used to probe whether an account exists.

What happens when my JWT access token expires?

Section titled “What happens when my JWT access token expires?”

After 1 day, the access token expires. Use your refresh token (valid 7 days) to get a new access token via POST /api/v1/users/auth/refresh. If the refresh token also expires, log in again with username and password.

Can one Auth Token cover several servers or apps?

Section titled “Can one Auth Token cover several servers or apps?”

Yes, but treat it as a convenience rather than a practice to standardize on. One token can work across several projects and servers; separate tokens per app or environment isolate better.

For realm-restricted tokens:

  • If realm_ids is non-empty (or allow_no_realm: false), use realm-scoped hosts like https://{realmId}.api.hoody.com.
  • Use GET /api/v1/auth/tokens/me on https://api.hoody.com to discover allowed realms before selecting a realm host.

How secure are Auth Tokens with no IP whitelist?

Section titled “How secure are Auth Tokens with no IP whitelist?”

Without an IP whitelist, a leaked token works from anywhere. The token itself is cryptographically strong (a long random string), but the whitelist adds defense-in-depth. Use it for production tokens and skip it for low-sensitivity automation.

Can Auth Tokens expire while my script is running?

Section titled “Can Auth Tokens expire while my script is running?”

Yes. If a long-running script spans the expiration time, it starts getting 401 errors. For long processes, use a far-future ISO 8601 date, omit expires_at at creation so the token never expires, or add logic that creates a replacement token before the old one expires.

What is the difference between disable and delete?

Section titled “What is the difference between disable and delete?”

Disabling sets is_enabled: false. The token stops working, and you can re-enable it later under the same ID. Deleting removes the token permanently, and it cannot be recovered. Use disable for a temporary suspension and delete for permanent revocation.

Can I use Auth Tokens with realm-scoped APIs?

Section titled “Can I use Auth Tokens with realm-scoped APIs?”

Yes. Auth Tokens work with realm-scoped APIs ({realmId}.api.hoody.com), and unrestricted tokens can also use the base API host.

Important behavior:

  • Tokens with non-empty realm_ids are restricted to those realm IDs.
  • Tokens with allow_no_realm: false cannot use the base host for resource operations.
  • Realm-restricted tokens can still call GET /api/v1/auth/tokens/me on the base host to bootstrap realm discovery.

Why does my token see environment variable names but null values?

Section titled “Why does my token see environment variable names but null values?”

The token lacks containers.features.env_values. Without it, GET /api/v1/containers/{id}/env and the set and delete operations list every key with a null value and values_redacted: true; a logged-in session always sees values. The full_access and dev_team templates grant the permission, and an explicit permission tree needs "containers": { "features": { "env_values": true } }. The permission only controls what those endpoints return: anyone who can run commands in the container or read its files can still read the environment there. See Container environment variables.

How do I rotate Auth Tokens for zero-downtime updates?

Section titled “How do I rotate Auth Tokens for zero-downtime updates?”

Create the new token, deploy it to your services and verify it works. Then disable the old token, confirm nothing breaks, and delete it. Both tokens work at the same time until you disable the old one.


Problem: login returns 401 with “Invalid credentials”

Solutions:

  1. Verify username/password:
POST Check for typos, ensure exact match
/api/v1/users/auth/login
Click "Run" to execute the request
  1. Check account status:
    • The account may be banned (is_banned: true)
    • Contact support if the account should be active

Problem: requests return 401 after some time

Cause: the access token expires after 1 day

Solution: Use the refresh token.

Terminal window
# The refresh token goes in both the header and the body, and the two must match
curl -X POST "https://api.hoody.com/api/v1/users/auth/refresh" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $REFRESH_TOKEN" \
-d '{"refreshToken": "'"$REFRESH_TOKEN"'"}'

This returns a new access token and a new refresh token. If the refresh token has also expired, after 7 days, log in again.

Problem: an hdy_... token returns 403 Forbidden

Check IP whitelist:

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

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

Solution: Update the whitelist.

PATCH Update token IP whitelist to include your current IP
/api/v1/auth/tokens/{id}
Click "Run" to execute the request

Problem: creating an Auth Token with another Auth Token returns 403 This token does not have permission: resources.create_tokens (auth tokens cannot manage auth tokens without it)

Cause: the calling token does not hold the resources.create_tokens permission. Tokens created with deny_reauthorization: true never hold it.

Solution: Grant resources.create_tokens when you create the parent token, or mint the child from a JWT obtained via login:

Terminal window
# 1. Login first
curl -X POST "https://api.hoody.com/api/v1/users/auth/login" \
-H "Content-Type: application/json" \
-d '{"username": "your_username", "password": "your_password"}' \
> login.json
# 2. Extract JWT
JWT=$(cat login.json | jq -r '.data.token')
# 3. Create Auth Token with JWT
curl -X POST "https://api.hoody.com/api/v1/auth/tokens" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{"alias": "My Token", "expires_at": "2027-04-12T00:00:00Z", "permission_template": "full_access"}'

Problem: the token was created but the hdy_... value was not saved

Reality: the token value cannot be retrieved after creation

Solution:

  1. Disable the old token:
PATCH Disable the old token
/api/v1/auth/tokens/{id}
Click "Run" to execute the request
  1. Create a new token:
POST Create a replacement token, and save the token value immediately
/api/v1/auth/tokens
Click "Run" to execute the request

Problem: scripts work sometimes and fail other times with 403

Likely cause: an IP whitelist combined with a dynamic IP

Check if your IP changed:

Run curl https://ifconfig.me in a terminal to get your current IP, then compare it with the token whitelist:

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

Solutions:

  1. Use a CIDR range instead of a single IP: Instead of "203.0.113.50/32", use "203.0.113.0/24", which allows the whole subnet

  2. Remove the IP whitelist for non-sensitive automation:

PATCH Remove IP whitelist restrictions
/api/v1/auth/tokens/{id}
Click "Run" to execute the request

To lift IP restrictions, set ip_whitelist to ["*"], which allows all. An empty array is rejected with 400, because the array form requires at least one entry.

  1. Use a static IP for automation servers

  1. Create Projects - Organize your containers
  2. Spawn Containers - Create your first HTTP computer
  3. Configure Networking - Set up routing and firewall
  4. Create Proxy Aliases - Get clean URLs for production