Skip to main content

API Security Model

This document explains how each server handles authentication for:

  1. Authenticated users (after login)
  2. Admin operations (from aventora-admin or automated scripts)

1. aventora-admin (Next.js Frontend)

Authenticated Users (After Login)

How it works:

  • Users log in via domain-chatbot API (POST /auth/login)
  • Receive JWT token in response
  • JWT token stored in cookies (accessible via getCookie())
  • All authenticated API calls include: Authorization: Bearer <jwt-token>

Used for:

  • domain-chatbot API: JWT tokens (domain management, user management, academy, etc.)
  • CMS API: JWT tokens (tenant management, pages, media, etc.) - CMS validates JWT by calling domain-chatbot
  • phone-server API: JWT tokens (billing, calls, accounts, etc.) - if phone-server supports JWT, otherwise API keys

Token lifecycle:

  • Tokens expire after configured time (default: session-based)
  • Frontend intercepts 401 errors and redirects to login
  • Token validation happens on the backend:
    • domain-chatbot validates its own JWT tokens
    • CMS validates JWT tokens by calling domain-chatbot's /auth/validate-token endpoint

Admin Operations

How it works:

  • Uses admin API keys from .env.local:
    • DOMAIN_CHATBOT_API_KEY - for domain-chatbot admin operations
    • HUB_API_KEY - for phone-server admin operations
  • NOTE: No CMS API key needed - CMS uses JWT tokens, not API keys
  • These are platform-wide admin keys (not domain-specific)

Used for:

  • Cross-domain operations (domain-chatbot and phone-server)
  • Creating/managing API keys (domain-chatbot and phone-server)
  • System-wide admin tasks
  • Automated scripts (e.g., ensure-domain-cms-api-keys.ts)

Important: aventora-admin is the ONLY component that stores admin API keys in environment variables. CMS does NOT use API keys - it validates JWT tokens from domain-chatbot.


2. CMS Backend (aventora-cms-backend)

Authenticated Users (After Login)

How it works:

  • Users log in via domain-chatbot API
  • Receive JWT token from domain-chatbot
  • CMS validates JWT token by calling domain-chatbot's /auth/validate-token endpoint
  • Endpoints use: Depends(get_current_user) or Depends(require_super_admin())
  • Request includes: Authorization: Bearer <jwt-token>

Used for:

  • Managing tenant content (pages, media, listings, etc.)
  • Tenant-specific operations

Token validation:

  • CMS does NOT validate JWT tokens directly
  • CMS calls domain-chatbot API to validate tokens: POST /auth/validate-token
  • Domain-chatbot returns user data if token is valid
  • CMS does NOT have its own JWT secret - it delegates to domain-chatbot

Key point: CMS uses JWT tokens from domain-chatbot, NOT API keys. There is no CMS API key.

Admin Operations

How it works:

  • Super admins use JWT tokens with is_admin=true and no domain assigned
  • Endpoints use: Depends(require_super_admin())
  • No API key authentication for admin operations - only JWT tokens

Special endpoint: /tenants/by-domain/{domain_name}/api-keys

  • Authenticated via platform-wide admin API key from domain-chatbot
  • This is the ONLY CMS endpoint that accepts API keys (not JWT)
  • Used by automated scripts to update tenant API keys
  • Validates API key by calling domain-chatbot API (no CMS env vars needed)

Server-to-Server Calls

When CMS needs to call other servers:

  • Calls domain-chatbot: Uses tenant.chatbot_api_key from database (per tenant)
  • Calls phone-server: Uses tenant.phone_api_key from database (per tenant)
  • No environment variables - keys must be set in database or operation fails

3. Domain-Chatbot (domain-chatbot)

Authenticated Users (After Login)

How it works:

  • Users log in via POST /auth/login with username/password
  • Receive JWT token in response
  • Endpoints use: Depends(get_current_user) or Depends(get_current_active_admin)
  • Request includes: Authorization: Bearer <jwt-token>

Token validation:

  • JWT token signed with JWT_SECRET
  • Validates signature, expiration, user exists and is active
  • Token payload contains username (sub field)

Used for:

  • Domain management
  • User management
  • Knowledge base operations
  • Academy operations

Admin Operations

How it works:

  • Two methods:
    1. JWT tokens: Admin users (with is_admin=true) log in and use JWT tokens
    2. Platform-wide admin API keys: API keys with no domain assigned (platform-wide)
  • Endpoints use: Depends(get_current_active_admin) or Depends(get_admin_user_or_api_key)

API key authentication:

  • Platform-wide admin API keys stored in domain_api_keys table
  • Keys have no domain assigned (domain is NULL or empty)
  • Keys have admin permissions
  • Used by aventora-admin for cross-domain operations

Server-to-Server Calls

When domain-chatbot needs to call phone-server:

  • Uses domain.phone_api_key from database (per domain)
  • Retrieved via chat_db.get_domain_by_name(domain_name)
  • No environment variables - key must be set in database or operation fails (e.g., phone account creation skipped)

4. Phone-Server (AIventora-Phone)

Authenticated Users (After Login)

How it works:

  • Users authenticate using API keys (not JWT tokens)
  • API keys stored in api_keys table (hashed)
  • Endpoints use: Depends(require_call_management()), Depends(require_admin()), etc.
  • Request includes: Authorization: Bearer <api-key>

API key validation:

  • API key is hashed and looked up in api_keys table
  • Checks if key is active, not expired, has required permissions
  • Returns APIKey object with permissions, account_id, etc.

Used for:

  • Initiating calls (/start endpoint). For informational email, optional email_template_id, email_template_name, and email_template_params — see ENGAGEMENT_HUB_OUTBOUND_EMAIL.md.
  • Calendar operations
  • Call logs
  • Account management
  • Billing operations

Note: Phone-server does NOT use JWT tokens - it uses API key authentication exclusively.

Admin Operations

How it works:

  • Uses API keys with admin permission
  • Endpoints use: Depends(require_admin())
  • API key must have permissions: ["admin"] in database

Used for:

  • Creating/managing API keys
  • Account management operations
  • System-wide admin tasks

Server-to-Server Calls

When phone-server needs to call domain-chatbot:

  • Uses account.domain_chatbot_api_key from database (per account)
  • Retrieved via AccountManager.get_account(account_id)
  • No environment variables - key must be set in database or operation fails

When internal services need to call phone-server API:

  • bulk_call_service and email_pull_service use account.internal_api_key from database
  • Retrieved via AccountManager.get_account(account_id)
  • No environment variables - key must be set in database or operation fails

5. Aventora CRM (twenty-server + twenty-front)

Authenticated users (workspace members)

How it works:

  • Users log into CRM and receive a workspace JWT.
  • Aventora REST routes use JwtAuthGuard + WorkspaceAuthGuard (same as other /rest/... APIs).
  • Request includes: Authorization: Bearer <workspace-jwt>.

Aventora engagement endpoints (examples):

MethodPathBackend behavior
GET/rest/aventora/email-templatesProxies Hub GET /account-settings/email-templates with workspace AVENTORA_API_KEY
POST/rest/aventora/start-engagementProxies Hub POST /integration/start (phone, SMS, email)
GET/rest/aventora/engagementsLists engagements stored in CRM DB
POST/rest/aventora/pause-engagement / cancel-engagementForwards to Hub

Email-specific start body (camelCase JSON): emailTemplateId, emailTemplateName, emailSubject, emailTemplateParams, emailBodyHtml, clientName — see CRM_OUTBOUND_EMAIL_TEMPLATES.md.

Direct Hub integrators (snake_case): see ENGAGEMENT_HUB_START_API.md.

Hub credentials:

  • Stored as application variables on the Twenty Standard app (AVENTORA_API_KEY, assigned domain)—not in the CRM user’s JWT.
  • Server env: AVENTORA_BASE_URL points at Engagement Hub.

Important: End users never send the Hub API key from the browser for start/list; twenty-server adds Authorization: Bearer <AVENTORA_API_KEY> when calling Hub.

Hub → CRM (webhooks)

  • Hub posts engagement updates to CRM POST /rest/aventora/webhook with x-aventora-webhook-secret (server env AVENTORA_WEBHOOK_SECRET).
  • See Aventora-Assistant docs/twenty-integration.md.

Summary Table

ServerUser AuthAdmin Auth (from aventora-admin)Server-to-Server Calls
aventora-adminJWT tokens (from domain-chatbot)Admin API keys from .env.local (for domain-chatbot & phone-server only)Uses admin API keys from .env.local for domain-chatbot & phone-server; JWT tokens for CMS
CMS BackendJWT tokens (validated via domain-chatbot API)JWT tokens (super admin)Database-stored keys (tenant.chatbot_api_key, tenant.phone_api_key)
Domain-ChatbotJWT tokens (own JWT_SECRET)JWT tokens OR platform-wide admin API keysDatabase-stored key (domain.phone_api_key)
Phone-ServerAPI keys (from api_keys table)API keys with admin permissionDatabase-stored keys (account.domain_chatbot_api_key, account.internal_api_key)
Aventora CRMWorkspace JWTN/A (members use CRM UI)Workspace AVENTORA_API_KEY → Hub; AVENTORA_BASE_URL on server

Key Principles

  1. aventora-admin is special: Only component that stores admin API keys in environment variables (for domain-chatbot & phone-server)
  2. CMS uses JWT tokens only: CMS does NOT use API keys - it validates JWT tokens via domain-chatbot
  3. Python servers use database-stored keys: No environment variable API keys, no fallbacks
  4. User authentication:
    • JWT tokens for CMS/domain-chatbot (CMS delegates to domain-chatbot)
    • API keys for phone-server
  5. Admin authentication:
    • JWT tokens for CMS/domain-chatbot (CMS delegates to domain-chatbot)
    • API keys for phone-server
    • Admin API keys from .env.local only for domain-chatbot & phone-server
  6. Server-to-server calls: Always use database-stored keys, fail immediately if not found

Example Flows

Flow 1: User logs into aventora-admin and manages CMS content

  1. User logs in → domain-chatbot returns JWT token
  2. aventora-admin stores JWT token in cookies
  3. User clicks "CMS Settings" → aventora-admin calls CMS API
    • Header: Authorization: Bearer <jwt-token>
    • CMS receives request and calls domain-chatbot's /auth/validate-token endpoint
    • Domain-chatbot validates JWT token and returns user data
    • CMS uses validated user data to process request
  4. User updates CMS content → CMS validates JWT via domain-chatbot and updates database

Flow 2: Super admin creates new tenant via aventora-admin

  1. Super admin logs into aventora-admin → receives JWT token
  2. Super admin clicks "Create Tenant" → aventora-admin calls CMS API
    • Header: Authorization: Bearer <jwt-token>
    • CMS validates JWT token (checks is_admin=true, no domain)
  3. CMS provision endpoint needs to call domain-chatbot and phone-server
    • Uses tenant.chatbot_api_key from database (or creates new keys)
    • Calls domain-chatbot: Authorization: Bearer <tenant.chatbot_api_key>
    • Calls phone-server: Authorization: Bearer <tenant.phone_api_key>

Flow 3: Automated script ensures API keys exist

  1. Script runs: npm run ensure-cms-api-keys -- --domain example.com
  2. Script uses DOMAIN_CHATBOT_API_KEY and HUB_API_KEY from .env.local (admin keys)
  3. Calls domain-chatbot API to create/get chatbot API key
    • Header: Authorization: Bearer <DOMAIN_CHATBOT_API_KEY>
  4. Calls phone-server API to create/get phone API key
    • Header: Authorization: Bearer <HUB_API_KEY>
  5. Updates CMS database with both keys
    • Calls CMS API: Authorization: Bearer <DOMAIN_CHATBOT_API_KEY>

Flow 4: Domain-chatbot creates phone account for user

  1. User activates phone_assistant service in domain-chatbot
  2. create_phone_account() function runs
  3. Gets domain's phone_api_key from database: domain.phone_api_key
  4. Calls phone-server API to create account
    • Header: Authorization: Bearer <domain.phone_api_key>
  5. Phone-server validates API key and creates account

Flow 5: Bulk call service processes queued calls

  1. bulk_call_service polls for queued calls
  2. For each call with account_id, gets account's internal API key
  3. Calls phone-server /start endpoint
    • Header: Authorization: Bearer <account.internal_api_key>
  4. Phone-server validates API key and initiates call

Error Handling

If database-stored API keys are missing, operations fail immediately with clear errors:

  • CMS: "Phone API key not configured for domain X. Please set phone_api_key in CMS tenant settings."
  • Domain-Chatbot: "Phone API key not configured for domain X - cannot create phone account"
  • Phone-Server: "Domain-chatbot API key not configured for account X. Please set domain_chatbot_api_key in account settings."
  • Phone-Server Internal Services: "No internal_api_key configured for account X - call will fail authentication"

No fallbacks - operations fail immediately if keys are not found in database.