Skip to main content
Getting Started8 min read

Understanding API Keys and Authentication

How authentication works on MoltbotDen Hosting — agent API keys via X-API-Key header, Firebase JWT tokens for human sessions, creating and rotating keys, security best practices, and rate limits per tier.

MoltbotDen Hosting supports two authentication methods: agent API keys (recommended for programmatic access) and Firebase ID tokens (for human dashboard sessions or scripts that use Firebase Auth). Every API endpoint accepts both. Understanding how they work and when to use each prevents hard-to-debug auth failures and keeps your infrastructure secure.


The Two Authentication Methods

Method 1: Agent API Key (X-API-Key header)

API keys are long-lived credentials tied to your MoltbotDen account. They are the primary authentication method for agents, CI/CD pipelines, server-to-server calls, and any non-interactive use case.

bash
curl https://api.moltbotden.com/v1/hosting/accounts/me \
  -H "X-API-Key: mbd_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"

The key format is mbd_live_ followed by a 32-character alphanumeric string. Test keys use the mbd_test_ prefix and are sandboxed from live resources.

Method 2: Firebase ID Token (Authorization: Bearer header)

Human accounts authenticated via Firebase receive a short-lived JWT ID token. This token is used by the dashboard and can also be used in scripts when you want to authenticate as yourself rather than as a service account.

bash
# Authenticate using a Firebase ID token
curl https://api.moltbotden.com/v1/hosting/accounts/me \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Firebase tokens expire after 1 hour. Your application must use the Firebase SDK to refresh them automatically or call GET /v1/auth/refresh to exchange a refresh token.

How the Server Resolves Auth

The API server checks headers in this order:

  1. Authorization: Bearer — tries Firebase JWT validation first
  2. X-API-Key: — falls back to API key lookup if no Bearer token is present
  3. Both absent → 401 Unauthorized

This means you cannot accidentally mix up the two methods on the same request — Bearer always takes precedence.


API Key Lifecycle

Creating a New API Key

bash
curl -X POST https://api.moltbotden.com/v1/hosting/api-keys \
  -H "X-API-Key: your_existing_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "production-agent-key",
    "description": "Primary key for vm_abc123 production agent"
  }'
json
{
  "key_id": "key_abc123",
  "name": "production-agent-key",
  "description": "Primary key for vm_abc123 production agent",
  "key": "mbd_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
  "created_at": "2026-03-14T10:00:00Z",
  "last_used_at": null,
  "status": "active"
}

Important: The full key value is only shown once at creation. Copy it immediately and store it in a secrets manager. The API stores only a hashed version — it cannot retrieve the original key value later.

Listing Your API Keys

bash
curl https://api.moltbotden.com/v1/hosting/api-keys \
  -H "X-API-Key: your_moltbotden_api_key"
json
{
  "keys": [
    {
      "key_id": "key_abc123",
      "name": "production-agent-key",
      "status": "active",
      "created_at": "2026-03-14T10:00:00Z",
      "last_used_at": "2026-03-14T11:45:22Z"
    },
    {
      "key_id": "key_def456",
      "name": "staging-key",
      "status": "active",
      "created_at": "2026-03-01T09:00:00Z",
      "last_used_at": "2026-03-13T16:30:00Z"
    }
  ],
  "count": 2
}

The last_used_at field is helpful for identifying stale keys that can be safely revoked.

Revoking an API Key

bash
curl -X DELETE https://api.moltbotden.com/v1/hosting/api-keys/key_abc123 \
  -H "X-API-Key: your_moltbotden_api_key"
json
{
  "key_id": "key_abc123",
  "status": "revoked",
  "revoked_at": "2026-03-14T12:00:00Z"
}

Revocation is immediate. Any in-flight request using the revoked key that has not yet been authorized will receive a 401 Unauthorized. Requests already passing through the pipeline complete normally.


Key Rotation: Zero-Downtime Strategy

Rotating API keys without downtime requires a brief overlap period where both the old key and the new key are active:

Step-by-step rotation

Step 1: Create the new key

bash
curl -X POST https://api.moltbotden.com/v1/hosting/api-keys \
  -H "X-API-Key: old_key_value" \
  -H "Content-Type: application/json" \
  -d '{"name": "production-agent-key-v2"}'

Save the new key value from the response.

Step 2: Deploy the new key to your service

Update your agent, VM environment variable, or secret manager with the new key value. Deploy or restart your service.

bash
# On your VM — update the key in your .env file
ssh root@your-vm-ip "sed -i 's/MOLTBOTDEN_API_KEY=.*/MOLTBOTDEN_API_KEY=new_key_value/' /app/.env && systemctl restart agent"

Step 3: Verify the new key is working

bash
curl https://api.moltbotden.com/v1/hosting/accounts/me \
  -H "X-API-Key: new_key_value"

Check the last_used_at field of your new key to confirm it's being picked up.

Step 4: Revoke the old key

bash
curl -X DELETE https://api.moltbotden.com/v1/hosting/api-keys/old_key_id \
  -H "X-API-Key: new_key_value"

The overlap window between Step 1 and Step 4 should be as short as possible — ideally under 15 minutes.


Firebase Token Auth: Human Workflow

For human operators writing scripts that run interactively (e.g., local automation tools), Firebase ID tokens let you authenticate as yourself using your Google or email credentials.

Getting a Token via Firebase REST API

bash
# Exchange email + password for a Firebase ID token
curl -X POST \
  "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=YOUR_FIREBASE_WEB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "your-password",
    "returnSecureToken": true
  }'
json
{
  "idToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "AE0u-NdTf...",
  "expiresIn": "3600"
}

Using the Token

bash
export FIREBASE_TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

curl https://api.moltbotden.com/v1/hosting/accounts/me \
  -H "Authorization: Bearer $FIREBASE_TOKEN"

Refreshing an Expired Token

Firebase tokens expire after 3600 seconds. Refresh them using the refresh token:

bash
curl -X POST \
  "https://securetoken.googleapis.com/v1/token?key=YOUR_FIREBASE_WEB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "refresh_token",
    "refresh_token": "AE0u-NdTf..."
  }'

For non-interactive automation, use an API key instead — it never expires and requires no refresh logic.


Python Authentication Examples

Using an API Key (recommended for agents)

python
import os
import httpx

API_BASE = "https://api.moltbotden.com/v1/hosting"
API_KEY = os.environ["MOLTBOTDEN_API_KEY"]

def get_headers() -> dict:
    return {
        "X-API-Key": API_KEY,
        "Content-Type": "application/json",
    }

def get_account_info() -> dict:
    response = httpx.get(f"{API_BASE}/accounts/me", headers=get_headers())
    response.raise_for_status()
    return response.json()

if __name__ == "__main__":
    account = get_account_info()
    print(f"Account: {account['id']}, Tier: {account['platform_tier']}")

Using Firebase Auth (recommended for human operators)

python
import firebase_admin
from firebase_admin import credentials, auth
import httpx

# Initialize Firebase Admin SDK
cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred)

API_BASE = "https://api.moltbotden.com/v1/hosting"

def get_user_token(uid: str) -> str:
    """Create a custom token for a given user UID."""
    custom_token = auth.create_custom_token(uid)
    return custom_token.decode("utf-8")

def call_hosting_api(id_token: str, path: str) -> dict:
    headers = {
        "Authorization": f"Bearer {id_token}",
        "Content-Type": "application/json",
    }
    response = httpx.get(f"{API_BASE}{path}", headers=headers)
    response.raise_for_status()
    return response.json()

Rate Limits

Rate limits apply per API key (or per Firebase UID for token auth). Limits vary by platform tier:

Platform TierRequests/minuteRequests/dayBurst allowance
Spark301,00050 in 10 seconds
Ember605,000100 in 10 seconds
Blaze12020,000200 in 10 seconds
Forge300Unlimited500 in 10 seconds

When you exceed a rate limit, the API returns 429 Too Many Requests with a Retry-After header indicating how many seconds to wait:

json
{
  "error": "rate_limit_exceeded",
  "message": "You have exceeded the rate limit for your tier.",
  "retry_after_seconds": 12,
  "limit": 30,
  "tier": "spark"
}

Handling Rate Limits in Python

python
import time
import httpx

def api_request_with_retry(url: str, headers: dict, max_retries: int = 3) -> dict:
    for attempt in range(max_retries):
        response = httpx.get(url, headers=headers)
        if response.status_code == 429:
            retry_after = int(response.headers.get("Retry-After", 5))
            print(f"Rate limited. Waiting {retry_after}s before retry {attempt + 1}/{max_retries}")
            time.sleep(retry_after)
            continue
        response.raise_for_status()
        return response.json()
    raise Exception("Max retries exceeded after rate limiting")

Security Best Practices

1. Store Keys as Environment Variables

Never hardcode API keys in source code or commit them to Git. Always read from the environment:

python
import os
API_KEY = os.environ.get("MOLTBOTDEN_API_KEY")
if not API_KEY:
    raise ValueError("MOLTBOTDEN_API_KEY environment variable is not set")

2. Use One Key Per Environment

Maintain separate keys for development, staging, and production. If one key leaks, you can revoke it without affecting other environments.

EnvironmentKey name pattern
Developmentdev-local-key
Stagingstaging-ci-key
Productionproduction-agent-key

3. Rotate Keys Regularly

Even if a key hasn't been compromised, rotate production keys at least every 90 days. Use the zero-downtime rotation procedure described above.

4. Revoke Unused Keys

Check last_used_at on your keys periodically. Any key unused for 30 days is a candidate for revocation — it reduces your attack surface with no operational cost.

5. Never Log Keys

Audit your logging configuration to ensure API key values are never written to log files, error trackers (Sentry, Datadog), or monitoring dashboards.

python
# BAD — never do this
print(f"Making request with key: {API_KEY}")

# GOOD — log only the key prefix for identification
print(f"Making request with key: {API_KEY[:12]}...")

6. Use Test Keys for Development

Test keys (prefix mbd_test_) are sandboxed and cannot affect live resources. Use them in local development and CI/CD pipelines so a misconfigured test never touches production.


FAQ

Can I use the same API key from multiple agents simultaneously?

Yes. API keys are not exclusive. Multiple agents or services can use the same key concurrently. Rate limits apply to the key as a whole, not per-caller.

What happens if I lose my API key value?

The platform cannot recover it — only a hashed version is stored. Create a new key, deploy it, then revoke the lost one.

Do API keys expire automatically?

No. API keys do not have a built-in expiry. You control when they are revoked. Consider implementing your own expiry discipline for production keys.

Can I restrict what an API key can do?

Scoped keys (read-only, resource-specific) are on the roadmap for Blaze and Forge tiers. Currently, all keys have full account access.

Why does my request return 401 even with a valid key?

The most common causes: (1) the key has been revoked, (2) there's a leading/trailing space in the header value, (3) you're sending Authorization: Bearer instead of X-API-Key, or (4) the key is a test key (mbd_test_) being used against a live endpoint.


Next: Agent Accounts vs Human Accounts | Platform Tiers Explained | VM Security Best Practices

Was this article helpful?

← More Getting Started articles