oqimDocs
API referenceConsole

Start here

  • Introduction
  • Getting started

Guides

  • Telegram accounts
  • Recipients
  • Campaigns
  • Scheduling
  • Webhooks
  • AI writing assistant

AI Seller

  • AI Seller
  • Channels
  • Live inbox
  • Past conversations and privacy
  • Business insights

API

  • Authentication
  • API reference
  • SDK
  • Errors
  • Rate limits

Trust and operations

  • Security
  • Compliance
  • Administration
Docs / AI writing assistant

Start here

  • Introduction
  • Getting started

Guides

  • Telegram accounts
  • Recipients
  • Campaigns
  • Scheduling
  • Webhooks
  • AI writing assistant

AI Seller

  • AI Seller
  • Channels
  • Live inbox
  • Past conversations and privacy
  • Business insights

API

  • Authentication
  • API reference
  • SDK
  • Errors
  • Rate limits

Trust and operations

  • Security
  • Compliance
  • Administration

AI writing assistant

The campaign composer can draft and edit messages with a model your organization connects: OpenAI, Anthropic, Gemini, DeepSeek, OpenRouter, or one you run yourself. The assistant never sends anything. You review each result and choose whether to use it.

Connect a provider#

  1. 1

    Open Settings → AI providers

    Owners and admins (the org:manage permission) choose Connect provider.
  2. 2

    Pick a provider and paste a key

    The built-in providers already know their endpoint. Get a key opens the provider's key page. The key is stored encrypted; Oqim shows only its last characters afterwards.
  3. 3

    Pick a model

    Load models asks the provider for its chat models, with search. You can also type a model ID.
  4. 4

    Connect and test

    Oqim saves the provider, sends a tiny request and shows Connected or the provider's error. The first provider becomes the default; Make default changes it.

In the campaign builder, the AI button in the message editor opens the assistant: write a draft from a brief, improve, shorten, make it friendlier or more formal, fix spelling and grammar, or translate to English, Русский or Oʻzbekcha. The result appears in a Telegram-style preview; Replace message or Insert at cursor puts it in the editor. People who can edit campaigns (campaigns:manage) can use it and switch between connected providers.

Supported providers#

ProviderEndpointProtocol
OpenAIhttps://api.openai.com/v1OpenAI Chat Completions
Anthropichttps://api.anthropic.com/v1Anthropic Messages
Geminihttps://generativelanguage.googleapis.com/v1beta/openaiGoogle's OpenAI-compatible endpoint
DeepSeekhttps://api.deepseek.comOpenAI Chat Completions
OpenRouterhttps://openrouter.ai/api/v1OpenAI Chat Completions
CustomYour base URLOpenAI- or Anthropic-compatible, your choice
  • OpenAI requests use max_completion_tokens, which its reasoning models require; the others use max_tokens. When a server refuses one of these parameters, Oqim adjusts the request and tries again, at most three times.
  • temperature is left out for models that reject it: OpenAI's o-series and GPT-5 models, and Claude, whose Opus 4.7 and later models refuse sampling parameters.
  • OpenRouter requests carry its optional attribution headers: HTTP-Referer (your PUBLIC_APP_URL) and the title Oqim.
  • Where a provider lists every kind of model (OpenAI, Gemini, OpenRouter), the model picker shows only chat models.

Custom endpoints and self-hosted models#

Custom connects any server that speaks OpenAI Chat Completions (POST {base}/chat/completions, GET {base}/models) or Anthropic Messages (POST {base}/messages). Enter the base URL: the part before /chat/completions. A key is optional.

ServerBase URL exampleBefore connecting
Ollamahttp://host.docker.internal:11434/v1Set OLLAMA_HOST so it listens beyond 127.0.0.1.
vLLMhttp://gpu-box:8000/v1Start with --host 0.0.0.0; add --api-key to require a key.
LM Studiohttp://192.168.1.20:1234/v1Turn on serving on the local network.
llama.cpp (llama-server)http://192.168.1.20:8080/v1It listens on 127.0.0.1:8080 by default; pass --host.
A gateway, such as LiteLLMhttps://llm.example.com/v1Choose the format it exposes.

A model on the same server

Inside Docker, localhost is the API container itself, so Oqim refuses it. Both compose files map host.docker.internal to the Docker host for the API service:

docker-compose.yml and deploy/docker-compose.yml
services:
  api:
    extra_hosts: ["host.docker.internal:host-gateway"]

A model on the same machine is then reachable at http://host.docker.internal:11434/v1 (Ollama). That is a private address, so a platform administrator must first turn on Allow private network endpoints (see below). The model server must listen on an address Docker can reach: the Docker bridge (usually 172.17.0.1) keeps it off the internet, while 0.0.0.0 needs a firewall rule.

Ollama on the Docker host
# On the Docker host: listen on the Docker bridge, not the internet.
OLLAMA_HOST=172.17.0.1:11434 ollama serve
ollama pull llama3.2

# Then connect a Custom provider in Oqim:
#   Base URL  http://host.docker.internal:11434/v1
#   Format    OpenAI-compatible
#   Model     llama3.2

What is sent to providers#

SentNever sent
  • The assistant's instructions (below)
  • The brief you wrote, for drafts
  • The current message text, for edits and translations
  • The target language, the message format and the model ID
  • Your API key, to that provider's endpoint only
  • Recipients or any of their data: names, usernames, Telegram IDs, attributes, consent records
  • Values of {{variables}}: only the placeholders as written
  • Telegram accounts, sessions or phone numbers
  • Anything from other organizations

Your provider's terms apply

What you send is handled under your provider's terms and data retention. Choose a provider, or a self-hosted model, whose terms fit your data policy.

How the assistant writes#

  • It keeps {{variables}} and {{opt_out_url}} exactly as written. Oqim warns when a result drops one or adds one your recipients may not have.
  • It never invents facts, prices, dates or links: missing details become placeholders such as [date] or [price].
  • It writes only the message, in the composer's format (Formatted, HTML or Plain) and under 4,096 characters. Oqim warns when a result is longer.
  • It declines phishing, impersonation, deception, harassment and messages to people who didn't opt in. The API answers 422 ai_refused with the reason.
  • It doesn't produce per-recipient variations or spintax. Varying copies to get past spam detection is filter evasion, which Oqim doesn't support.

Private endpoints#

Organizations choose custom URLs, and the API runs next to Postgres, Redis and the worker. Oqim therefore checks every address when it connects, after the name is resolved, so a name can't later point somewhere internal.

  • Only http and https URLs, without a username or password in them.
  • Always refused: loopback (127.0.0.0/8, ::1), link-local including cloud metadata (169.254.0.0/16, fe80::/10), unspecified, multicast and other reserved addresses, and this stack's service names: postgres, redis, worker, api, web, edge, scheduler, oqim-* and localhost.
  • Refused unless allowed by a platform administrator: private ranges 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10 and fc00::/7, including host.docker.internal. Ports 5432, 6379 and 8090 on private addresses stay refused.
  • Redirects to another host aren't followed, and HTTP proxy settings aren't used.
  • The built-in providers always go to their fixed public endpoints.

Allow private network endpoints

This platform setting (Admin → Platform settings → AI assistant, or ai_allow_private_endpoints in PUT /admin/settings) is for self-hosted models on your own hardware or network. Turn it on only for a single-organization or trusted installation: any organization could then point a custom endpoint at services on your network.

Keys and security#

  • API keys are encrypted with APP_KEY like proxy passwords, bound to their organization and provider, and never returned. The console shows only the last characters, like •••• 1a2b.
  • A saved key is only sent to the endpoint it was saved for: changing the provider or base URL needs the key again.
  • Keys, briefs, messages and results aren't logged. Provider errors are shortened to a readable message; response bodies are never shown or stored.
  • Connecting, changing (including key rotation) and removing providers is in the audit log as AI_PROVIDER_ADDED, AI_PROVIDER_UPDATED and AI_PROVIDER_REMOVED. Individual compose requests aren't audited.
  • Requests time out after 45 seconds (20 for model lists); responses over 2 MB (8 MB for model lists) are refused.

Rate limits and usage#

Applies toLimitCounted per
Compose requests30 per hourPerson, or API key
Compose requests100 per hourOrganization
Provider checks: tests and model lists60 per hourOrganization

Windows are UTC hours. Over a limit, the API answers 429 ai_rate_limited with Retry-After in seconds. Your provider's own limits apply as well. Compose usage is metered per UTC day as ai_requests, ai_input_tokens and ai_output_tokens in GET /organization/usage.

Errors#

StatusCodeMeaning
409ai_not_configuredNo provider is connected.
422ai_refusedThe assistant declined the request; the message says why.
422ai_endpoint_blockedThe endpoint is an address Oqim won't connect to.
429ai_rate_limitedAn hourly limit above was reached.
502ai_provider_auth_failedThe provider rejected the key.
502ai_provider_rate_limitedThe provider throttled the key or its quota is used up.
502ai_provider_not_foundUnknown model or path.
502ai_provider_bad_request, ai_provider_error, ai_provider_unreachable, ai_provider_redirect, ai_provider_invalid_responseThe provider failed or couldn't be reached; the message quotes its error, shortened.
502ai_empty_outputThe model returned nothing, for example after spending its output limit on reasoning.
504ai_provider_timeoutNo answer within 45 seconds.

Endpoints#

All under /api/v1; see the API reference for bodies and responses.

EndpointPermissionWhat it does
GET/ai/catalogorg:readThe built-in providers with their fixed endpoints and where to get a key, plus CUSTOM.
GET/ai/providersorg:readConnected providers, the default first.
POST/ai/providersorg:manageConnect a provider.
PATCH/ai/providers/{id}org:manageChange a provider.
DELETE/ai/providers/{id}org:manageRemove a provider and delete its key.
POST/ai/providers/{id}/testorg:manageSend a tiny request and record the outcome in status (OK or ERROR), last_error and last_checked_at.
GET/ai/providers/{id}/modelsorg:manageThe provider's chat models, listed with the stored key.
POST/ai/modelsorg:manageList models for a configuration that isn't saved yet, so a form can offer a model picker.
POST/ai/composecampaigns:manageDraft or edit one message with a connected provider: provider_id, or the default.
PreviousWebhooksNext AI Seller