Universal event-driven omnichannel messaging platform
One API to send and receive messages across WhatsApp, Discord, Telegram, and more.
CLI-first. Event-driven. Built for AI agents.
Install • Quick Start • AI Agents • CLI • API • SDKs • Development
Think of Omni as a deep-sea octopus. Each channel is a tentacle — WhatsApp, Discord, Telegram — reaching into a different messaging ecosystem. Events are nerve impulses flowing through NATS JetStream. The core is the brain — identity resolution, event routing, and a unified API that lets you treat all channels as one.
| 🔌 One API, every channel | Send a WhatsApp message, a Discord embed, and a Telegram photo with the same endpoint |
| ⚡ Event-driven | Every action produces an event. Subscribe, replay, automate |
| 🧬 Identity graph | Same person on WhatsApp and Discord? Omni knows |
| 🤖 AI-native | Agent providers, automations, built to be controlled by LLMs |
| 🔧 Plugin architecture | Build new channels with the Channel SDK |
| 📦 Multi-SDK | Auto-generated TypeScript, Python, and Go SDKs |
| Channel | Status | Highlights |
|---|---|---|
| WhatsApp (Baileys) | ✅ Stable | QR/phone pairing, media, reactions, groups, contacts, presence |
| Discord | ✅ Stable | Bots, embeds, polls, buttons, threads, slash commands |
| Telegram | ✅ New | Bot API, inline keyboards, groups, channels, threads, polls |
| WhatsApp Cloud API | 🔮 Planned | — |
| Slack | 🔮 Planned | — |
bun add -g @automagik/omni
omni install # interactive wizard: sets up server + PM2Migrating from
@omni/cli? Runbun remove -g @omni/clifirst.
curl -fsSL https://raw.githubusercontent.com/automagik-dev/omni/main/install-client.sh | bashcurl -fsSL https://raw.githubusercontent.com/automagik-dev/omni/main/install.sh | bashThree modes: CLI only · Full server · CLI + connect to remote
Non-interactive & manual install
# CLI only (tries npm first, falls back to git clone)
curl -fsSL https://raw.githubusercontent.com/automagik-dev/omni/main/install.sh | bash -s -- --cli
# CLI + connect to remote
curl -fsSL https://raw.githubusercontent.com/automagik-dev/omni/main/install.sh | bash -s -- --cli https://your-omni-server.com
# Full server
curl -fsSL https://raw.githubusercontent.com/automagik-dev/omni/main/install.sh | bash -s -- --serverManual:
git clone https://github.com/automagik-dev/omni.git && cd omni
make setup # Install deps, create .env, start servicesAPI runs at http://localhost:8882 · Swagger docs at /api/v2/docs · API key printed in startup banner.
# Authenticate
omni auth login --api-key <your-key>
# WhatsApp — scan QR
omni instances create --name "my-whatsapp" --channel whatsapp-baileys
omni instances qr <id> --watch
# Discord — connect bot
omni instances create --name "my-discord" --channel discord
omni instances connect <id> --token "BOT_TOKEN"
# Telegram — connect bot
omni instances create --name "my-telegram" --channel telegram
omni instances connect <id> --token "BOT_TOKEN"
# Send messages
omni send --to "+5511999999999" --text "Hello from the deep 🐙"
# Browse conversations
omni chats list --unread --sort unreadConnect any LLM provider and your instances become intelligent agents — responding to messages, reacting to events, across every channel.
Message received → NATS event → Agent Dispatcher → Provider (OpenAI/Agno/Webhook) → Humanized reply
# Connect a provider
omni providers create --name "my-llm" --schema openai --base-url "https://api.openai.com/v1" --api-key "sk-..."
# Bind to instance — it's now an agent
omni instances update <id> --agent-provider <provider-id>| Trigger | When | Use Case |
|---|---|---|
| DM | Direct message | Always reply |
| Mention | @bot in group | Respond to mentions |
| Reply | Reply to bot's message | Continue thread |
| Reaction | Emoji on message | 👍 approve, 🔥 prioritize |
Built-in: message debouncing · per-user rate limits · access control · smart response chunking · typing presence · cross-channel identity · self-chat detection
Automations & event-driven workflows
# Auto-reply
omni automations create --name "welcome" \
--trigger "message.received" \
--action send_message --action-config '{"text":"Got it! 🐙"}'
# Webhook on connection
omni automations create --name "notify" \
--trigger "instance.connected" \
--action webhook --action-config '{"url":"https://your-app.com/hook"}'
# Route VIPs to a special agent
omni automations create --name "vip" \
--trigger "message.received" \
--condition '{"field":"payload.from","op":"in","value":["+551199..."]}' \
--action call_agent --agent-id "vip-handler" --provider-id <id>┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ WhatsApp │ │ Discord │ │ Telegram │ Tentacles
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
└────────────────┼────────────────┘
┌─────────▼─────────┐
│ NATS JetStream │ Nerve System
└─────────┬─────────┘
┌─────────▼─────────┐
│ Omni Core │ Brain
└─────────┬─────────┘
┌──────────────┼──────────────┐
┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ REST API │ │ CLI │ │ Dashboard │ Interfaces
└───────────┘ └───────────┘ └───────────┘
Project structure
packages/
├── core/ # Events, identity, schemas
├── db/ # Drizzle ORM + PostgreSQL
├── api/ # Hono + tRPC + OpenAPI
├── channel-sdk/ # Plugin SDK
├── channel-whatsapp/ # Baileys
├── channel-discord/ # discord.js
├── channel-telegram/ # grammy
├── cli/ # `omni` command
├── media-processing/ # Media sync
├── sdk/ # TypeScript SDK
├── sdk-go/ # Go SDK
└── sdk-python/ # Python SDK
apps/
└── ui/ # React + Vite + Tailwind
Core commands — send, chats, messages, instances, channels, persons
omni send --to "+5511999999999" --text "Hello!"
omni send --to "+5511999999999" --media ./photo.jpg --caption "Check this out"
omni send --to "+5511999999999" --reaction "👍" --message <messageId>
omni send --to "+5511999999999" --sticker ./sticker.webp
omni send --to "+5511999999999" --contact --name "John" --phone "+1234567890"
omni send --to "+5511999999999" --location --lat -23.55 --lng -46.63 --address "São Paulo"
omni send --to "discord-channel-id" --poll "Favorite color?" --options "Red,Blue,Green"
omni send --to "discord-channel-id" --embed --title "Alert" --description "Server rebooting" --color "#ff0000"
omni send --to "+5511999999999" --presence typingomni chats list --unread --sort unread
omni chats messages <chatId> --limit 20 --rich
omni chats archive <chatId>
omni chats participants <chatId> --add "+5511999999999"Subcommands: list · get · create · update · delete · archive · unarchive · messages · participants · read
omni messages search "meeting tomorrow" --instance <id> --limit 10
omni messages read --chat <chatId> --instance <id>omni instances list
omni instances create --name "work-whatsapp" --channel whatsapp-baileys
omni instances qr <id> --watch
omni instances connect <id> --token "BOT_TOKEN" # Discord / Telegram
omni instances sync <id> --type all --depth 30
omni instances contacts <id> --limit 50
omni instances groups <id>Subcommands: list · get · create · delete · status · qr · pair · connect · disconnect · restart · logout · sync · syncs · update · contacts · groups · profile
omni persons search "John"
omni persons get <id>
omni persons presence <id>Management — keys, providers, automations, access, webhooks
omni keys create --name "agent-key" --scopes "messages:*,instances:read"
omni keys list --status active
omni keys revoke <id> --reason "Compromised"omni providers create --name "openai" --schema openai --api-key "sk-..."
omni providers list
omni providers test <id>Schemas: agnoos · a2a · openai · anthropic · webhook · custom
omni automations create --name "Auto-reply" --trigger "message.received" \
--action send_message --action-config '{"text":"Got it!"}'
omni automations list --enabled
omni automations test <id>Actions: webhook · send_message · emit_event · log · call_agent
omni access create --type deny --instance <id> --phone "+1234567890" --action block
omni access list --instance <id>
omni access check --instance <id> --user "+1234567890"Modes: disabled · blocklist · allowlist
omni webhooks create --name "github-events"
omni webhooks trigger --type "custom.event" --payload '{"key":"value"}'System — status, auth, config, events, batch, logs
omni status # Health check
omni auth login --api-key <key> # Authenticate
omni config set defaultInstance <id> # CLI settings
omni events list --type "message.*" --since 2h # Event history
omni events replay --start --since 2024-01-01 # Replay events
omni batch create --instance <id> --type targeted_chat_sync --chat <chatId>
omni resync --instance <id> # History backfill
omni logs list --level error --limit 50 # Server logs
omni dead-letters list --limit 20 # Failed events| Base URL | http://localhost:8882/api/v2 |
| Docs | /api/v2/docs (Swagger UI) |
| OpenAPI | /api/v2/openapi.json |
| Auth | x-api-key header |
20 endpoints: /auth · /instances · /messages · /chats · /events · /persons · /access · /settings · /providers · /automations · /webhooks · /keys · /logs · /batch-jobs · /dead-letters · /media · /metrics · /event-ops · /payloads
|
TypeScript import { createOmniClient } from '@omni/sdk';
const omni = createOmniClient({
baseUrl: 'http://localhost:8882',
apiKey: 'your-api-key',
});
await omni.messages.send({
instanceId: '...',
to: '+5511999999999',
text: 'Hello from SDK!',
}); |
Python from omni import OmniClient
client = OmniClient(
base_url="http://localhost:8882",
api_key="your-api-key"
)
instances = client.instances.list()Go client := omni.NewClient(
"http://localhost:8882",
"your-api-key",
)
instances, _ := client.Instances.List(ctx) |
Regenerate after API changes: make sdk-generate
make dev-ui # Dev → http://localhost:5173
make build-ui # Prod → served by API on :8882Pages: Dashboard · Instances · Chats · Chat View · Contacts · Persons · Providers · Automations · Access Rules · Batch Jobs · Dead Letters · Events · Logs · Settings
🔐 API Keys & Security
API key is generated on first boot (shown once in startup banner).
# Scoped keys
omni keys create --name "admin" --scopes "*"
omni keys create --name "reader" --scopes "messages:read,chats:read"
omni keys create --name "wa-only" --scopes "messages:*" --instances "uuid1,uuid2"Scopes: namespace:action pattern — messages:*, instances:read, * for full access
Namespaces: messages · chats · instances · persons · events · access · settings · providers · automations · webhooks · keys · logs · batch
⚙️ Configuration & environment
| Variable | Default | Description |
|---|---|---|
API_PORT |
8882 |
API server port |
DATABASE_URL |
postgresql://...localhost:8432/omni |
PostgreSQL |
NATS_URL |
nats://localhost:4222 |
NATS connection |
OMNI_API_KEY |
(auto) | Override primary key |
Set *_MANAGED=false for external services. Full list in .env.example.
| Service | PM2 Name | Port |
|---|---|---|
| PostgreSQL | omni-pgserve |
8432 |
| NATS | omni-nats |
4222 |
| API | omni-api |
8882 |
make dev # Start all services + API
make check # typecheck + lint + test
make dev-ui # UI dev server
make db-push # Push schema changes
make db-studio # Drizzle StudioAll make targets
make setup # Full setup (deps + env + services)
make dev-api # API only
make dev-services # PostgreSQL + NATS via PM2
make typecheck # TypeScript only
make lint # Biome linter
make lint-fix # Auto-fix
make test # All tests
make test-api # API tests
make test-file F= # Specific test
make build # Build all
make build-ui # Build UI
make status # Service status
make logs-api # API logs
make restart-api # Restart API
make db-reset # Reset DB (DESTRUCTIVE)
make sdk-generate # Regenerate SDKs
make cli ARGS="" # Run CLI from source
make cli-link # Install CLI globallyBuilding a channel plugin
import { BaseChannelPlugin } from '@omni/channel-sdk';
export class MyPlugin extends BaseChannelPlugin {
readonly id = 'my-channel';
readonly name = 'My Channel';
readonly version = '1.0.0';
readonly capabilities = { /* ... */ };
async connect(instanceId: string, config: InstanceConfig) { /* ... */ }
async disconnect(instanceId: string) { /* ... */ }
async sendMessage(instanceId: string, message: OutgoingMessage) { /* ... */ }
}| Runtime | Bun |
| HTTP | Hono |
| API | tRPC + OpenAPI |
| DB | PostgreSQL + Drizzle |
| Events | NATS JetStream |
| Validation | Zod |
| Frontend | React + Vite + Tailwind |
| Monorepo | Turborepo |
| Linter | Biome |
MIT — do whatever you want, just don't blame the octopus. 🐙
This README is maintained by Scroll 📜, an autonomous sub-agent of Omni.