🧠 HeyCMO
API Reference

Authentication

API key authentication — generation, usage, and security.

Authentication

HeyCMO uses API key authentication. Every authenticated request must include your API key.

API Key Format

All API keys use the hcmo_live_ prefix followed by a 64-character hex string:

hcmo_live_a1b2c3d4e5f6789...

Authentication Methods

HeyCMO uses two authentication methods depending on the endpoint:

Bearer Token (REST API)

For REST API endpoints (/api/*), include the key as a Bearer token in the Authorization header:

curl -X GET https://heycmo.ai/api/customer/CUSTOMER_ID \
  -H "Authorization: Bearer hcmo_live_YOUR_KEY"

The API extracts the key from the Authorization header by stripping the Bearer prefix.

Token Query Parameter (MCP)

For MCP endpoints (/mcp/sse, /mcp/message), pass the key as a token query parameter:

https://heycmo.ai/mcp/sse?token=hcmo_live_YOUR_KEY

This is the format used in Claude Desktop and Cursor MCP configuration.

Security Architecture

Key Generation

  • Keys are generated using 32 bytes of cryptographically secure random data (crypto.randomBytes)
  • The full key is shown once at generation time and cannot be retrieved afterward

Key Storage

  • Keys are never stored in plaintext on the server
  • Only the SHA-256 hash of the key is stored in the database
  • Validation uses timing-safe comparison (crypto.timingSafeEqual) to prevent timing attacks

Encryption

  • Sensitive data at rest is encrypted with AES-256-GCM
  • Encryption uses scrypt-derived keys with a unique IV per ciphertext
  • Authentication tags prevent tampering

Customer Validation

After key validation, the system checks that the customer account is in an active or onboarding state. Inactive accounts are rejected even with a valid key.

Rate Limiting

All endpoints have rate limiting applied per API key or IP address:

ScopeLimit
REST API60 requests/minute
MCP30 requests/minute per customer
Pre-auth20 attempts/minute (brute-force protection)

Best Practices

  1. Never expose your API key in client-side code, public repositories, or logs
  2. Use environment variables to store your key in server applications
  3. Rotate keys periodically — generate a new key and revoke the old one
  4. Use separate keys for development and production environments
  5. Monitor usage via the dashboard to detect unauthorized access

For AI agents — programmatic key handling

If you're an AI agent (Claude, ChatGPT, Cursor, Devin, custom orchestrator) integrating with HeyCMO, see the dedicated For AI agents guide. The short version:

1. Obtain a key

There's no fully-programmatic key issuance today — a human still needs to complete Stripe checkout. The flow:

# Create a checkout session (no auth required)
curl -X POST https://heycmo.ai/api/checkout \
  -H "Content-Type: application/json" \
  -d '{"email":"agent-operator@example.com"}'
# → { "url": "https://checkout.stripe.com/c/pay/..." }

The customer completes checkout in a browser, and the API key is delivered via the Stripe-redirect handler. After completion:

curl https://heycmo.ai/api/session/SESSION_ID
# → { "customerId": "...", "apiKey": "hcmo_live_...", "mcpEndpoint": "..." }

Store the key in your agent's secret store and use it on every request.

For team-scoped agents, the customer can issue per-team-member keys from /team after signing in. Team-member keys validate the same way (Bearer header) and are restricted by role.

2. Discover auth requirements via well-known files

Per RFC 9728:

curl https://heycmo.ai/.well-known/oauth-protected-resource

Currently declares:

{
  "resource": "https://heycmo.ai",
  "authorization_servers": [],
  "bearer_methods_supported": ["header"],
  "scopes_supported": [
    "read:content", "write:content", "publish:social", "read:analytics", ...
  ]
}

OAuth 2.0 is on the roadmap; today the empty authorization_servers array is intentional.

3. Retry strategy for 429s

Every 429 response includes:

HTTP/1.1 429 Too Many Requests
Retry-After: 13
X-Request-ID: 6e2f....
Content-Type: application/json

{ "error": "Rate limit exceeded", "code": "E_RATE_LIMITED", "requestId": "6e2f..." }

Recommended backoff (Node):

async function call(url: string, init: RequestInit, attempt = 0): Promise<Response> {
  const res = await fetch(url, init);
  if (res.status === 429 && attempt < 5) {
    const retryAfter = Number(res.headers.get('retry-after') ?? 1);
    const jitter = Math.random() * 250;
    await new Promise(r => setTimeout(r, retryAfter * 1000 + jitter));
    return call(url, init, attempt + 1);
  }
  return res;
}

Recommended backoff (Python):

import time, random, requests

def call(method, url, **kwargs):
    for attempt in range(5):
        r = requests.request(method, url, **kwargs)
        if r.status_code == 429:
            retry = int(r.headers.get('Retry-After', '1'))
            time.sleep(retry + random.uniform(0, 0.25))
            continue
        return r
    return r

4. Quote requestId in support messages

Every response includes an X-Request-ID header (also returned in error JSON bodies). Quote this when contacting support so we can find the request in our logs without a needle-in-haystack search.

5. CLI

The official CLI handles login + per-call auth automatically:

npm install -g @heycmo/cli
heycmo auth login        # paste hcmo_live_… key when prompted
heycmo auth whoami       # confirms which key + endpoint
heycmo scan https://example.com
heycmo content list
heycmo mcp config        # prints Claude/Cursor/Windsurf snippet

Source: packages/cli/ in the repo.

Error Responses

Missing API Key

// HTTP 401
{
  "error": "Authorization header required"
}

Invalid API Key

// HTTP 401
{
  "error": "Invalid API key"
}

MCP Token Missing

// HTTP 401
{
  "error": "MCP token required. Include ?token=hcmo_live_..."
}

Inactive Account

// HTTP 401
{
  "error": "Customer account is not active"
}

Rate Limited

// HTTP 429
{
  "error": "Rate limit exceeded. Try again later."
}

On this page