# Authentication

**Page:** foundation/hoody-api/authentication

[Download Raw Markdown](./foundation/hoody-api/authentication.md)

---

# Authentication

**The Hoody API requires authentication for all operations.** There are two authentication systems, each designed for different use cases.

After understanding [what the Hoody API does](./) (platform management), you need to understand **how to authenticate** to actually use it.

---

## API Endpoints Summary

**Official Technical Reference:**

This Foundation page explains authentication concepts and best practices. For complete endpoint documentation:

**User Authentication (JWT Tokens):**
- **[POST /api/v1/users/auth/login](/api/authentication/)** - Login with username/password
- **[POST /api/v1/users/auth/refresh](/api/authentication/)** - Refresh access token
- **[GET /api/v1/users/auth/me](/api/authentication/)** - Get current user profile
- **[POST /api/v1/users/auth/logout](/api/authentication/)** - Invalidate session

**Automation (Auth Tokens):**
- **[POST /api/v1/auth/tokens](/api/auth-tokens/)** - Create long-lived token
- **[GET /api/v1/auth/tokens](/api/auth-tokens/)** - List all tokens
- **[GET /api/v1/auth/tokens/\{id\}](/api/auth-tokens/)** - Get token details
- **[PUT /api/v1/auth/tokens/\{id\}](/api/auth-tokens/)** - Update token configuration
- **[DELETE /api/v1/auth/tokens/\{id\}](/api/auth-tokens/)** - Revoke token

---

## Two Authentication Systems

Hoody provides two distinct authentication methods:

<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin: 1.5rem 0;">

<div style="padding: 1.25rem; background: #f8f8f8; border: 1px solid #ddd; border-radius: 4px;">

**JWT Tokens** (User Sessions)

**Use for:** Browser sessions, interactive work

```bash
POST /api/v1/users/auth/login
```

**Returns:**
- `token` (access, 1 day)
- `refreshToken` (7 days)

**Characteristics:**
- ✅ Short-lived (secure)
- ✅ Refresh-able (no re-login)
- ✅ User-specific
- ❌ Not ideal for automation

</div>

<div style="padding: 1.25rem; background: #f8f8f8; border: 1px solid #ddd; border-radius: 4px;">

**Auth Tokens** (Automation)

**Use for:** Scripts, AI agents, CI/CD, integrations

```bash
POST /api/v1/auth/tokens
```

**Returns:**
- `hdy_...` token (long-lived)

**Characteristics:**
- ✅ Long-lived (configurable)
- ✅ IP whitelist support
- ✅ Revocable anytime
- ✅ Per-token permissions
- ✅ Ideal for automation

</div>

</div>


**Best Practice:** Use Auth Tokens for any automation, AI agents, or scripts. Never put user credentials in code. Create a dedicated token with appropriate expiration and IP restrictions.


---

## Authentication Workflow

### For Interactive Use (Browser/CLI)

**Step 1: Login**


The login endpoint accepts **either** an `email` or a `username` — only `password` is required. Use whichever identifier you have. The CLI exposes both as separate flags (`--email` and `--username`).



  
    ```bash
    # Login with email and password (or use --username instead of --email)
    hoody auth login --email you@example.com --password your_password
    ```
  
  
    ```typescript
    import { HoodyClient } from '@hoody-ai/hoody-sdk';

    const client = new HoodyClient({ baseURL: 'https://api.hoody.icu' });

    // Login with credentials — pass `email` OR `username` (not both required)
    const auth = await client.api.authentication.login({
      email: 'you@example.com',
      password: 'your_password'
    });
    console.log(auth.data.token);       // JWT access token
    console.log(auth.data.refreshToken); // Refresh token
    ```
  
  
    ```bash
    # Provide EITHER email OR username (only password is required)
    curl -X POST "https://api.hoody.icu/api/v1/users/auth/login" \
      -H "Content-Type: application/json" \
      -d '{"email": "you@example.com", "password": "your_password"}'
    # Alternative: -d '{"username": "your_username", "password": "your_password"}'
    ```
  




**Response:**
```json
{
  "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"
    }
  }
}
```

**Step 2: Use Access Token**


  
    ```bash
    # CLI stores the token automatically after login
    hoody projects list
    ```
  
  
    ```typescript
    // Pass token to client constructor
    const client = new HoodyClient({
      baseURL: 'https://api.hoody.icu',
      token: auth.data.token
    });
    const projects = await client.api.projects.list();
    ```
  
  
    ```bash
    # Include token in Authorization header
    curl "https://api.hoody.icu/api/v1/projects" \
      -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    ```
  


**Step 3: Refresh When Expired**


  
    ```bash
    # CLI handles token refresh automatically
    # If your session expired, simply re-login
    hoody auth login --username your_username --password your_password
    ```
  
  
    ```typescript
    // Refresh the access token
    const refreshed = await client.api.authentication.refreshToken({
      refreshToken: auth.data.refreshToken
    });
    console.log(refreshed.data.token); // New access token
    ```
  
  
    ```bash
    curl -X POST "https://api.hoody.icu/api/v1/users/auth/refresh" \
      -H "Content-Type: application/json" \
      -d '{"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}'
    ```
  




**Returns:** New access token + new refresh token (both rotated for security)

---

### For Automation & AI (Recommended)

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


  
    ```bash
    # Login first
    hoody auth login --username your_username --password your_password

    # Create a long-lived automation token with IP whitelist
    hoody auth create \
      --alias "Production Automation Token" \
      --ip-whitelist "203.0.113.10,203.0.113.20" \
      --expires-at "2027-04-12T00:00:00Z"
    ```
  
  
    ```typescript
    import { HoodyClient } from '@hoody-ai/hoody-sdk';

    // Login first to get JWT
    const client = new HoodyClient({ baseURL: 'https://api.hoody.icu' });
    const auth = await client.api.authentication.login({
      username: 'your_username',
      password: 'your_password'
    });

    // Create auth token using JWT
    const jwtClient = new HoodyClient({ baseURL: 'https://api.hoody.icu', token: auth.data.token });
    const token = await jwtClient.api.authTokens.create({
      alias: 'Production Automation Token',
      ip_whitelist: ['203.0.113.10', '203.0.113.20'],
      expires_at: '2027-04-12T00:00:00Z'
    });
    console.log(token.data.token); // hdy_... — save this immediately!
    ```
  
  
    ```bash
    # Login to get JWT
    curl -X POST "https://api.hoody.icu/api/v1/users/auth/login" \
      -H "Content-Type: application/json" \
      -d '{"username": "your_username", "password": "your_password"}'

    # Create a long-lived automation token
    curl -X POST "https://api.hoody.icu/api/v1/auth/tokens" \
      -H "Authorization: Bearer $JWT" \
      -H "Content-Type: application/json" \
      -d '{
        "alias": "Production Automation Token",
        "ip_whitelist": ["203.0.113.10", "203.0.113.20"],
        "expires_at": "2027-04-12T00:00:00Z"
      }'
    ```
  




**Response:**
```json
{
  "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"],
    "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"
  }
}
```


**Save this token immediately!** The full `token` value (`hdy_abc123...`) is **only shown once** during creation. You cannot retrieve it again.


**Step 2: Use Auth Token** (forever, until it expires or is revoked)


  
    ```bash
    # Store token and use with CLI
    export HOODY_TOKEN="hdy_abc123XyZ456..."

    # All subsequent commands use this token
    hoody projects list
    hoody containers list
    ```
  
  
    ```typescript
    // Use auth token in SDK client
    const client = new HoodyClient({
      baseURL: 'https://api.hoody.icu',
      token: process.env.HOODY_TOKEN  // hdy_abc123XyZ456...
    });

    // All API calls are authenticated
    const projects = await client.api.projects.list();
    ```
  
  
    ```bash
    # Store in environment variable (never hardcode)
    export HOODY_TOKEN="hdy_abc123XyZ456..."

    # Use in all API requests
    curl "https://api.hoody.icu/api/v1/projects" \
      -H "Authorization: Bearer $HOODY_TOKEN"
    ```
  


**Benefits:**
- ✅ No credentials in code (secure)
- ✅ IP whitelist enforcement (restrict to specific IPs)
- ✅ Expiration control (ISO 8601 date, or never)
- ✅ Instant revocation (disable or delete token)
- ✅ Audit trail (last_used_at, last_used_ip)

---

## Auth Token Management

### Creating Tokens with Security Features

**IP Whitelisting:**



**Multiple formats for expiration:**


  
    ```json
    {
      "expires_at": "2026-12-31T23:59:59Z"
    }
    ```
  
  
    ```json
    {
      "expires_at": 1735689599
    }
    ```
  
  
    ```json
    {
      "expires_at": null
    }
    ```
  


### Listing and Auditing Tokens


  
    ```bash
    # List all your auth tokens
    hoody auth list
    ```
  
  
    ```typescript
    const tokens = await client.api.authTokens.list();
    tokens.data.forEach(t => {
      console.log(t.alias, t.is_enabled, t.last_used_at);
    });
    ```
  
  
    ```bash
    curl "https://api.hoody.icu/api/v1/auth/tokens" \
      -H "Authorization: Bearer $HOODY_TOKEN"
    ```
  




**Response shows usage tracking:**

```json
{
  "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": "2025-11-09T14:30:00.000Z",
      "last_used_ip": "203.0.113.50",
      "created_at": "2025-11-09T10:00:00.000Z",
      "updated_at": "2025-11-09T14:30:00.000Z"
    }
  ]
}
```

**Audit your tokens:**
- Check `last_used_at` to identify unused tokens
- Verify `last_used_ip` matches expected sources
- Review `ip_whitelist` restrictions

### Revoking Tokens

**Disable without deleting:**


  
    ```bash
    # Disable a token without deleting it
    hoody auth update $TOKEN_ID --enabled false
    ```
  
  
    ```typescript
    await client.api.authTokens.update(tokenId, { is_enabled: false });
    ```
  
  
    ```bash
    curl -X PUT "https://api.hoody.icu/api/v1/auth/tokens/$TOKEN_ID" \
      -H "Authorization: Bearer $HOODY_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"is_enabled": false}'
    ```
  




**Permanently delete:**


  
    ```bash
    # Permanently delete a token
    hoody auth delete $TOKEN_ID
    ```
  
  
    ```typescript
    await client.api.authTokens.delete(tokenId);
    ```
  
  
    ```bash
    curl -X DELETE "https://api.hoody.icu/api/v1/auth/tokens/$TOKEN_ID" \
      -H "Authorization: Bearer $HOODY_TOKEN"
    ```
  




---

## Security Best Practices

### 1. Never Hardcode Credentials


  
    ```javascript
    // NEVER do this
    const response = await fetch('https://api.hoody.icu/api/v1/projects', {
      headers: {
        'Authorization': 'Bearer hdy_abc123hardcoded'
      }
    });
    ```
  
  
    ```javascript
    // Use environment variables
    const response = await fetch('https://api.hoody.icu/api/v1/projects', {
      headers: {
        'Authorization': `Bearer ${process.env.HOODY_TOKEN}`
      }
    });
    ```
  


### 2. Use IP Whitelisting

**Restrict tokens to specific sources:**



**If token is leaked:** It won't work from other IPs.

### 3. Set Expiration Appropriately

**Short-lived for temporary access:**

```json
{
  "alias": "Contractor Access",
  "expires_at": "2026-05-12T00:00:00Z"
}
```

**Long-lived for permanent infrastructure:**

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

**Review and rotate regularly.**

### 4. Create Dedicated Tokens per Service

**Don't share one token across multiple systems:**

```bash
# 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, others continue working.

---

## For AI Agents

**The Auth Token system is designed for AI orchestration:**

```javascript
// AI agent configuration (environment variables)
const HOODY_TOKEN = process.env.HOODY_TOKEN;  // hdy_... token
const HOODY_API = 'https://api.hoody.icu';

// 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.icu`;
  
  // AI executes commands in the new container
  await fetch(`${terminalUrl}/execute`, {
    method: 'POST',
    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 AI only needs:**
1. A Hoody Auth Token (environment variable)
2. Understanding of HTTP (it already has this)

No SDK. No training. Just HTTP.

---

## Token Comparison

| Feature | JWT (Login) | Auth Token (hdy_...) |
|---------|-------------|----------------------|
| **Lifetime** | 1 day (access)<br/>7 days (refresh) | Configurable (ISO 8601 date, "today", "tomorrow", or forever) |
| **Use Case** | User sessions | Automation, AI, scripts |
| **Refresh** | Yes (via refresh token) | No (create new when expired) |
| **IP Whitelist** | No | Yes (optional) |
| **Revocation** | Logout endpoint | Delete or disable |
| **Visibility** | Managed by browser | One-time show during creation |
| **Security** | Short-lived = more secure | Long-lived but IP-restricted |

---

## Complete Authentication Examples

### Example 1: User Login Flow

```bash
# 1. Login
curl -X POST "https://api.hoody.icu/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": { ... }
#   }
# }

# 2. Use access token (valid 1 day)
curl "https://api.hoody.icu/api/v1/projects" \
  -H "Authorization: Bearer eyJhbG..."

# 3. Refresh before expiration (within 7 days)
curl -X POST "https://api.hoody.icu/api/v1/users/auth/refresh" \
  -H "Content-Type: application/json" \
  -d '{"refreshToken": "eyJhbG..."}'

# 4. Logout (optional, invalidates session)
curl -X POST "https://api.hoody.icu/api/v1/users/auth/logout" \
  -H "Authorization: Bearer eyJhbG..."
```

### Example 2: Create Automation Token

```bash
# 1. Login to get JWT
curl -X POST "https://api.hoody.icu/api/v1/users/auth/login" \
  -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.icu/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"],
    "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 "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/start" \
#       -H "Authorization: Bearer $HOODY_TOKEN"
```

### Example 3: AI Agent Setup

```javascript
// AI agent configuration file
// .env
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.icu';
  }

  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.icu`;
    
    // Execute command (no auth needed if container permissions are open)
    return fetch(`${terminalUrl}/execute`, {
      method: 'POST',
      body: JSON.stringify({ command })
    });
  }
}

// AI uses this class for ALL Hoody operations
const agent = new HoodyAgent();
await agent.spawnContainer('ai-workspace', { serverId: 'server-123' });
```

**The AI only needs environment variables.** No password handling. No credential management.

---

## Managing Tokens

### List All Tokens



### Update Token Configuration

**Change IP whitelist:**



**Extend expiration:**



**Temporarily disable:**



### Token Rotation Strategy

**Best practice for production:**

```bash
# 1. Create new token
NEW_TOKEN=$(curl -X POST "https://api.hoody.icu/api/v1/auth/tokens" \
  -H "Authorization: Bearer YOUR_JWT" \
  -d '{"alias": "Production V2", "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. Wait 24-48 hours for old token usage to drop

# 4. Check old token is unused
curl "https://api.hoody.icu/api/v1/auth/tokens/{old_token_id}" \
  -H "Authorization: Bearer YOUR_JWT" \
  | jq '.data.last_used_at'

# 5. Delete old token
curl -X DELETE "https://api.hoody.icu/api/v1/auth/tokens/{old_token_id}" \
  -H "Authorization: Bearer YOUR_JWT"
```

---

## Common Patterns

### Pattern 1: Short-Lived Scripts

**For one-time operations:**



Use the token for your migration, and it will auto-expire tomorrow.

### Pattern 2: Per-Environment Tokens

**Different tokens for different environments:**

**Development token (permissive):**



**Staging token (IP-restricted):**



**Production token (strict):**



### Pattern 3: Emergency Revocation

**If a token is compromised:**

**1. Immediately disable:**



**2. Create replacement with different IP whitelist:**



**3. Update services, then delete old token:**



---

## Useful Questions

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

**Always use Auth Tokens (`hdy_...`) for scripts and automation.** JWTs from login are designed for short-lived user sessions and expire after 1 day. Auth Tokens can live for years with IP whitelisting and revocation support.

### Can I create an Auth Token using another Auth Token?

No. You must use a JWT from user login to create Auth Tokens. This prevents token proliferation—if an Auth Token is compromised, it can't create more tokens. Always keep one user account secure for Auth Token management.

### How do I share API access with my team without sharing 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.

### 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, you must login again with username/password.

### Can I use the same Auth Token across multiple servers or applications?

Yes, but treat that as convenience, not best practice. A single token can work across multiple projects/servers, yet isolation is stronger with separate tokens per app/environment.

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

### How secure are Auth Tokens with no IP whitelist?

Without IP whitelist, a leaked token can be used from anywhere. While the token itself is cryptographically strong (60+ character random string), IP whitelisting adds defense-in-depth. Use it for production tokens, skip it for low-sensitivity automation.

### Can Auth Tokens expire while my script is running?

Yes. If a long-running script spans the expiration time, it will start getting 401 errors. For long processes, use generous expiration (a far-future ISO 8601 date or `null`) or implement token refresh logic that creates a new token before the old one expires.

### What's the difference between disabling and deleting an Auth Token?

**Disable** sets `is_enabled: false`—token stops working but you can re-enable it later with the same ID. **Delete** permanently removes the token—cannot be recovered. Use disable for temporary suspension, delete for permanent revocation.

### Can I use Auth Tokens with realm-scoped APIs?

Yes. Auth Tokens work with realm-scoped APIs (`{realmId}.api.hoody.icu`), 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 base host for resource operations.
- Realm-restricted tokens can still call `GET /api/v1/auth/tokens/me` on base host to bootstrap realm discovery.

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

Create the new token, deploy it to your services, verify the new token works, wait 24-48 hours, check old token's `last_used_at` is old, then delete the old token. Both tokens work simultaneously during the transition.

---

## Troubleshooting

### Login Fails (Invalid Credentials)

**Problem:** Login returns 401 with "Invalid credentials"

**Solutions:**

1. **Verify username/password:**



2. **Check account status:**
   - Account might be banned (`is_banned: true`)
   - Contact support if legitimate user

### JWT Token Expired

**Problem:** Requests return 401 after some time

**Cause:** Access token expires after 1 day

**Solution - Use refresh token:**



Returns new access token + new refresh token.

**Refresh token expired?** (after 7 days) - Login again

### Auth Token Not Working

**Problem:** `hdy_...` token returns 403 Forbidden

**Check IP whitelist:**



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

**Solution - Update whitelist:**



### Token Creation Returns 401

**Problem:** Can't create Auth Token, getting 401

**Cause:** You're using an Auth Token to create another Auth Token

**Solution:** Use a JWT from login instead:
```bash
# 1. Login first
curl -X POST "https://api.hoody.icu/api/v1/users/auth/login" \
  -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.icu/api/v1/auth/tokens" \
  -H "Authorization: Bearer $JWT" \
  -d '{"alias": "My Token", "expires_at": "2027-04-12T00:00:00Z"}'
```

### Lost Auth Token Value

**Problem:** Created token but didn't save the `hdy_...` value

**Reality:** Cannot retrieve token value after creation

**Solution:**

1. **Disable the old token:**



2. **Create new token:**



### Automation Breaking Randomly

**Problem:** Scripts work sometimes, fail other times with 403

**Likely cause:** IP whitelist + dynamic IP

**Check if your IP changed:**

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



**Solutions:**

1. **Use CIDR range instead of single IP:**
   Instead of `"203.0.113.50/32"`, use `"203.0.113.0/24"` (allows entire subnet)

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



3. **Use static IP for automation servers**

---

## What's Next

**Now that you can authenticate:**

1. **[Create Projects](/foundation/projects-containers/)** - Organize your containers
2. **[Spawn Containers](/api/containers/)** - Create your first HTTP computer
3. **[Configure Networking](/foundation/networking/network/)** - Set up routing and firewall
4. **[Create Proxy Aliases](/foundation/proxy/aliases/)** - Get clean URLs for production

**Everything starts with authentication. Everything else is HTTP.**

---

> **User sessions use JWTs.**  
> **Automation uses Auth Tokens.**  
> **Never hardcode credentials.**  
> **Use environment variables. Use IP whitelists. Use expiration.**

**This is how you securely control infinite computers.**