AI integration

The gateway ships with a built-in OpenAI-compatible provider. Any endpoint that speaks the OpenAI /chat/completions shape works (OpenAI itself, OpenRouter, Groq, OpenAI-compatible local servers, Azure-OpenAI proxies, etc.).

Three layers of configuration

1. Global — applies to every account by default. Edit at /ai.
2. Per-account — overrides for one WhatsApp account. Edit at /accounts/:id.
3. Per-chatinherit | enabled | disabled toggle plus an optional custom system prompt. Edit by tapping the gear icon in any chat detail page.

Effective config is resolved at runtime — chat → account → global → env.

Configure globally (curl)

curl -X PUT "$PUBLIC_API_URL/v1/ai/global" \
  -H "Authorization: Bearer $ADMIN_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "base_url": "https://api.openai.com/v1",
    "api_key": "sk-...",
    "model_name": "gpt-4.1-mini",
    "default_system_prompt": "You are a helpful WhatsApp assistant.",
    "temperature": 0.3,
    "max_tokens": 700,
    "timeout_seconds": 60,
    "reply_delay_seconds": 2,
    "reply_to_private": true,
    "reply_to_groups": false,
    "reply_to_channels": false,
    "echo_mode": false
  }'

The api_key is encrypted at rest. Reads return has_api_key: true/false, never the raw key.

Auto-reply flow

1. Incoming message arrives → persisted in messages table.
2. Worker re-evaluates the rules (global enabled? account enabled? chat enabled? not blocked?).
3. Builds a conversation: system prompt + last 20 messages.
4. Calls ${base_url}/chat/completions.
5. Persists ai_log row, emits ai.reply.generated webhook.
6. Sleeps reply_delay_seconds, simulates typing, sends as the WhatsApp account.
7. Emits ai.reply.sent (or ai.reply.failed).

If api_key is missing the message is logged as skipped with reason no API key configured and never sent.

Test AI (token route)

curl -X POST "$PUBLIC_API_URL/v1/ai/chat/test" \
  -H "Authorization: Bearer YOUR_ACCOUNT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Hello, can you help me?",
    "chat_id": "optional-chat-id",
    "send_to_chat": false
  }'

When send_to_chat: true is set and chat_id is present, the AI reply is also sent through the WhatsApp session.

Test AI (admin route)

The dashboard's AI chat / test page uses this:

curl -X POST "$PUBLIC_API_URL/v1/ai/admin/test" \
  -H "Authorization: Bearer $ADMIN_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "account_id": "optional",
    "chat_id": "optional",
    "message": "Say hi"
  }'

Per-chat toggle

curl -X PUT "$PUBLIC_API_URL/v1/accounts/ACC/chats/CHAT_JID/ai" \
  -H "Authorization: Bearer $ADMIN_JWT" \
  -H "Content-Type: application/json" \
  -d '{"mode":"disabled","custom_system_prompt":null}'

Modes: inherit, enabled, disabled.

Security checklist