automagik-si/omni

omni

Universal event-driven omnichannel platform for AI agents to communicate across any messaging platform

待分类ai-agentsbundiscord

Omni — One API, Every Channel

Universal event-driven omnichannel messaging platform

One API to send, receive, route, and automate messages across WhatsApp, Discord, Slack, 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 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 every channel as one system.

Why Omni?

🔌 One API, every channelSend WhatsApp, Discord, Slack, Telegram, and other channel messages with one interface
⚡ Event-drivenEvery action produces an event. Subscribe, replay, automate
🧬 Identity graphResolve the same person across channels and instances
🤖 AI-nativeProvider bindings, response gates, session strategies, automations, and agent dispatch
🔧 Plugin architectureBuild channels and integrations with the Channel SDK
📦 SDKsTypeScript SDK, plus generated Python/Go clients for API consumers

Channels

ChannelStatusHighlights
WhatsApp (Baileys)✅ StableQR/phone pairing, media, reactions, groups, contacts, presence
WhatsApp Cloud API (Meta)✅ AvailableEmbedded Signup OAuth, templates HSM, webhook (HMAC-SHA256), media, location, reactions
Discord✅ StableBots, embeds, polls, buttons, threads, slash commands, voice channels
Slack✅ AvailableSocket Mode or HTTP, bot & user (xoxp) token modes, threads, scheduled messages, permalinks, pins, message search
Telegram✅ AvailableBot API, inline keyboards, groups, channels, threads, polls
A2A✅ AvailableAgent-to-agent channel integrations
Gupshup✅ AvailableCustom Integration webhook support
H3rmes (Mutant)✅ AvailableBrazilian WhatsApp gateway — JWT auth, media, templates, interactive, Pix-ready webhook shapes
ASC Brazil✅ AvailableBrazilian WhatsApp BSP — Cloud API mirror, Meta-format webhooks, typing indicator, templates, interactive
ASC Flow✅ AvailableBrazilian BSP via the ASC platform Flow — REST callbacks, URA buttons/lists, Genesys handoff
Twilio WhatsApp✅ AvailableTwilio sender, webhook, and signature validation support

Also in the box: Harness (--channel harness), an E2E agent-testing channel that captures outgoing messages verbatim so you can drive and assert on agent conversations from tests, and an internal channel used for agent-to-agent routing.

Install

Global CLI package

bun add -g @automagik/omni
omni install              # bootstrap the local server/runtime with PM2

Migrating from @omni/cli? Run bun remove -g @omni/cli first.

Via install script

curl -fsSL https://raw.githubusercontent.com/automagik-dev/omni/main/install.sh | bash

Full server from source

curl -fsSL https://raw.githubusercontent.com/automagik-dev/omni/main/install.sh | bash -s -- --server

Supported script modes: CLI only (--cli) and Full server (--server).

Non-interactive & manual install
# CLI only
curl -fsSL https://raw.githubusercontent.com/automagik-dev/omni/main/install.sh | bash -s -- --cli

# Full server
curl -fsSL https://raw.githubusercontent.com/automagik-dev/omni/main/install.sh | bash -s -- --server

# Connect the CLI to a remote server after install
omni auth login --api-url https://your-omni-server.example.com --api-key <your-api-key>

Manual:

git clone https://github.com/automagik-dev/omni.git && cd omni
make setup    # install deps, create .env, start services

API runs at http://localhost:8882 · Swagger docs at /api/v2/docs · API key is printed in the startup banner.

Quick Start

# Authenticate
omni auth login --api-key <your-api-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 --discord-token <discord-bot-token>
omni instances connect <id>

# Telegram — connect bot
omni instances create --name "my-telegram" --channel telegram --telegram-token <telegram-bot-token>
omni instances connect <id>

# Send messages
omni send --to "+15555550100" --text "Hello from the deep 🐙"

# Browse conversations
omni chats list --unread --sort unread

AI Agents

Connect an agent provider and your instances can respond to messages, react to events, and route conversations across channels.

Message received → NATS event → Agent Dispatcher → Provider → Response gate → Channel reply
# Create a provider
omni providers create \
  --name "my-agent" \
  --schema webhook \
  --base-url "https://agent.example.com/webhook" \
  --api-key <provider-api-key>

# Bind it to an instance
omni instances update <id> --agent-provider <provider-id>

Provider schemas: agno · webhook · ag-ui · claude-code · a2a · nats-genie

TriggerWhenUse Case
DMDirect messageAlways reply
Mention@bot in groupRespond to mentions
ReplyReply to bot's messageContinue thread
ReactionEmoji on messageApprovals, priority, lightweight workflows

Built-in: message debouncing · per-user rate limits · access control · response gates · smart 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.example.com/hook"}'

# Route important senders to a dedicated agent
omni automations create --name "priority-route" \
  --trigger "message.received" \
  --condition '{"field":"payload.from","op":"in","value":["+15555550100"]}' \
  --action call_agent --agent-id "priority-handler" --provider-id <provider-id>

Event Backbone

Every state change lands in a total-ordered PostgreSQL journal and is published over NATS. The journal is the source of truth — you can filter it, trace it, wait on it, and consume it durably:

omni events list --type "message.*" --since 2h        # trailing-* glob type filters
omni events types --since 7d                          # what's flowing: volume, schema status, subscribers
omni events stream --pretty --exclude "custom.chat.*" # live tail, one line per event, minus the noise
omni events trace <event-id>                          # causation chain, rendered as a tree
omni events wait --type "custom.github.*" --timeout 60  # block until a matching event arrives

# Durable named consumers — resumable cursors over the journal
omni events consumers create deploy-watcher --type "custom.github.*"
omni events follow --consumer deploy-watcher          # at-least-once, acks as it goes

Webhook sources turn external systems into event emitters with configuration only — no code: raw-body signature verification (HMAC-SHA256/SHA1 or token match), per-source idempotency key templates, event-type mapping to custom.<source>.<event>, optional strict schema validation, and heartbeat-based connector liveness (system.connector.stalled / recovered). Working recipes: GitHub and ClickUp.

Event schemas are registered once (omni events schema register) and enforced at webhook ingress and automation emit_event — invalid payloads dead-letter instead of entering the journal.

Agent manifests declare what each agent consumes and publishes (omni agents manifest apply, omni agents graph): the publishes list is enforced at emission time, and accepts compiles into managed routing automations. Automations can also buffer their emitted events and flush them in order only when the whole run succeeds (--transactional-emissions).

Deep dives: event system · durable consumers · agent publish governance

Architecture

┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│  WhatsApp   │  │   Discord   │  │    Slack    │  │  Telegram   │  Tentacles
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       └────────────────┴────────────────┴────────────────┘
                         ┌─────────▼─────────┐
                         │   NATS JetStream  │             Nerve system
                         └─────────┬─────────┘
                         ┌─────────▼─────────┐
                         │     Omni Core     │             Brain
                         └─────────┬─────────┘
              ┌────────────────────┼────────────────────┐
        ┌─────▼─────┐        ┌─────▼─────┐        ┌─────▼─────┐
        │  REST API │        │    CLI    │        │  Desktop  │ Interfaces
        └───────────┘        └───────────┘        └───────────┘
Project structure
packages/
├── core/                    # Events, identity, schemas
├── db/                      # Drizzle ORM + PostgreSQL
├── api/                     # Hono + REST + OpenAPI
├── channel-sdk/             # Plugin SDK
├── channel-whatsapp/        # WhatsApp Baileys
├── channel-whatsapp-business/ # WhatsApp Cloud API (Meta)
├── channel-discord/         # Discord
├── channel-slack/           # Slack
├── channel-telegram/        # Telegram
├── channel-gupshup/         # Gupshup
├── channel-hermes/          # H3rmes (Brazilian WhatsApp gateway)
├── channel-twilio-whatsapp/ # Twilio WhatsApp
├── channel-a2a/             # A2A channel
├── channel-asc/             # ASC Brazil (ASCWhats GW, Cloud API mirror)
├── channel-asc-flow/        # ASC platform Flow (Brazilian BSP)
├── channel-harness/         # E2E agent-testing channel
├── channel-internal/        # In-process agent-to-agent routing
├── cli/                     # `omni` command
├── media-processing/        # Media sync and extraction
├── voice-client/            # Voice transport/codec library (Discord voice)
├── sdk/                     # TypeScript SDK
├── sdk-go/                  # Go SDK
└── sdk-python/              # Python SDK

CLI Reference

Core commands — send, chats, messages, instances, persons

send — Send messages

omni send --to "+15555550100" --text "Hello!"
omni send --to "+15555550100" --media ./photo.jpg --caption "Check this out"
omni send --to "+15555550100" --reaction "👍" --message <message-id>
omni send --to "+15555550100" --sticker ./sticker.webp
omni send --to "+15555550100" --contact --name "Example User" --phone "+15555550101"
omni send --to "+15555550100" --location --lat 37.7749 --lng -122.4194 --address "San Francisco"
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 "+15555550100" --presence typing

chats — Manage conversations

omni chats list --unread --sort unread
omni chats messages <chat-id> --limit 20 --rich
omni chats archive <chat-id>
omni chats participants <chat-id> --add "+15555550100"

messages — Search and manage

omni messages search "meeting tomorrow" --instance <id> --limit 10
omni messages read --chat <chat-id> --instance <id>

instances — Channel connections

omni instances list
omni instances create --name "work-whatsapp" --channel whatsapp-baileys
omni instances qr <id> --watch
omni instances create --name "ops-slack" --channel slack --slack-bot-token <bot-token> --slack-app-token <app-token>
omni instances connect <id>
omni instances sync <id> --type all --depth 30
omni instances contacts <id> --limit 50
omni instances groups <id>

Run omni instances --help for the full channel-specific command list.

persons — Contact directory

omni persons search "Example User"
omni persons get <id>
omni persons presence <id>
Management — keys, providers, automations, access, webhooks, schedule, tenants

keys — API key management

omni keys create --name "agent-key" --scopes "messages:*,instances:read"
omni keys list --status active
omni keys revoke <id> --reason "Compromised"

providers — AI/agent providers

omni providers create --name "webhook-agent" --schema webhook --base-url "https://agent.example.com" --api-key <provider-api-key>
omni providers list
omni providers test <id>

Schemas: agno · webhook · ag-ui · claude-code · a2a · nats-genie

automations — Event-driven workflows

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> --event <event-id>   # dry-run against a real journaled event: condition verdicts, no side effects

Actions: webhook · send_message · emit_event · log · call_agent

access — Access control

omni access create --type deny --instance <id> --phone "+15555550100" --action block
omni access list --instance <id>
omni access check --instance <id> --user "+15555550100"

Modes: disabled · blocklist · allowlist

webhooks — External event sources

omni webhooks create --name "github-events" \
  --signature-algorithm hmac-sha256 --signature-header "X-Hub-Signature-256" --signature-prefix "sha256=" \
  --signature-secret-env GITHUB_WEBHOOK_SECRET \
  --event-type-mapping '{"source":"header","header":"X-GitHub-Event"}' \
  --idempotency-key-template "github:{headers.x-github-delivery}"
omni webhooks heartbeat github-events            # connector liveness check-in
omni webhooks trigger --type "custom.event" --payload '{"key":"value"}'

Sources receive on the public POST /api/v2/webhooks/ingress/:source endpoint (signature required). See the GitHub and ClickUp recipes.

schedule — Scheduled messages

omni schedule send <instance> <chat-id> "Stand-up in 10" --at 2h   # ISO timestamp or 30m/2h/3d
omni schedule list <instance>
omni schedule cancel <id>

Delivery is native where the channel supports it (Slack, text-only); everywhere else a local sweeper sends at the scheduled time.

tenants — Platform tenant control plane

omni tenants create --slug acme --name "Acme" --max-key-ttl 7776000 --max-key-rate 100 --max-key-budget 1000 --reason "onboarding"
omni tenants keys issue-root <tenant-id> --principal <uuid> --membership <uuid> --role tenant-owner \
  --name root --scopes "tenant:*" --expires 2026-12-01 --rate-limit 100 --budget 1000 --reason "bootstrap"
omni multitenancy status

Requires a PLATFORM-class credential and OMNI_MULTITENANCY_ENABLED=true; every command takes an audited --reason. Omni runs single-tenant by default — see platform credential bootstrap.

System — update, status, doctor, auth, config, events, logs
omni update -y                                  # update CLI; restart only services that were already online
omni update -y --no-restart                     # update CLI only; skip restarts and API-port health check
omni status                                     # verify runtime health
omni doctor                                     # diagnose embedded runtime issues
omni doctor --fix                               # repair safe runtime drift in-place
omni auth login --api-key <your-api-key>        # authenticate
omni config set defaultInstance <id>            # CLI settings
omni events list --type "message.*" --since 2h  # event history (trailing-* globs)
omni events types                               # observed event types: volume, schema, subscribers
omni events stream --pretty                     # live tail, one colored line per event
omni events trace <event-id>                    # causation chain
omni events wait --type "custom.x.*" --timeout 60  # block until a matching event
omni events schema register <type> --file s.json   # register a payload schema
omni events schema validate <type> --file p.json   # dry-run a payload, no event emitted
omni events consumers create <name> --type "custom.x.*"  # durable cursor
omni events follow --consumer <name>            # durable tail
omni events replay --start --since 2024-01-01   # replay events
omni journey show <correlation-id>              # per-message latency timeline
omni batch create --instance <id> --type targeted_chat_sync --chat <chat-id>
omni resync --instance <id>                     # history backfill
omni logs list --level error --limit 50         # server logs
omni dead-letters list --limit 20               # failed events

omni update can exit non-zero after install only when restart runs (services were already online): in that path it checks API health on the configured API port and fails on restart or health-check errors.

omni doctor --fix never deletes database data. It repairs safe runtime drift such as PM2 state, stored env, and CLI config; destructive cleanup remains manual.

REST API

Base URLhttp://localhost:8882/api/v2
Docs/api/v2/docs (Swagger UI)
OpenAPI/api/v2/openapi.json
Authx-api-key header

Main API groups include: /auth, /instances, /messages, /chats, /events (plus /events/schemas, /events/consumers, and per-event /trace), /persons, /access, /settings, /providers, /automations, /webhooks, /scheduled-messages, /keys, /logs, /batch-jobs, /dead-letters, /media, /metrics, /event-ops, /payloads, /agents, /agent-state, /agent-tasks, /conversations, /context, /turns, /trust, /voice, /follow-up, /handoffs, /journeys, /instances/:id/whatsapp-templates, and /platform (multitenancy control plane, flag-gated).

SDKs

TypeScript

import { createOmniClient } from '@omni/sdk';

const omni = createOmniClient({
  baseUrl: 'http://localhost:8882',
  apiKey: '<your-api-key>',
});

await omni.messages.send({
  instanceId: '<instance-id>',
  to: '+15555550100',
  text: 'Hello from SDK!',
});

Python and Go

Generated Python and Go clients live in packages/sdk-python and packages/sdk-go. They are useful for API consumers, but the TypeScript SDK is the primary maintained SDK for this release.

Regenerate SDKs after API changes:

make sdk-generate

Desktop app

Omni's UI is the Automagik desktop app (omni desktop opens it with Omni preloaded): it runs Omni on your machine or connects to a remote one. The API serves no web pages of its own.

🔐 API Keys & Security

API key is generated on first boot and shown once in the startup banner.

omni keys create --name "admin" --scopes "*"
omni keys create --name "reader" --scopes "messages:read,chats:read"
omni keys create --name "channel-only" --scopes "messages:*" --instances "uuid1,uuid2"

Scopes use the namespace:action pattern: messages:*, instances:read, * for full access.

Namespaces include: messages, chats, instances, persons, events, access, settings, providers, automations, webhooks, keys, logs, and batch.

⚙️ Configuration & environment
VariableDefaultDescription
API_PORT8882API server port
DATABASE_URLpostgresql://...localhost:8432/omniPostgreSQL
NATS_URLnats://localhost:4222Where clients connect to NATS
NATS_HOST127.0.0.1Address the source-checkout PM2 NATS listens on (installer: omni config set server.natsHost)
OMNI_API_KEY(auto)Override primary key

Set *_MANAGED=false for external services. Full list: .env.example.

Managed NATS listens on loopback only once omni-nats is (re)created by omni start, omni install or a later omni update (omni restart keeps the previous arguments and warns when they differ). After upgrading an existing install, run omni stop && omni start once. Set NATS_HOST / server.natsHost to 0.0.0.0 only when other hosts must connect, and protect it (NATS auth/TLS or a private network). Apply with omni stop && omni start. See docs/guides/install.md.

ServicePM2 Name (installer / source checkout)Port
APIomni-api / omni-v2-api8882
NATSomni-nats / omni-v2-nats4222
PostgreSQLautopg-server (canonical pgserve, managed by omni install)8432

Development

make dev           # Start all services + API
make test          # Run test suite
make typecheck     # TypeScript checks
make lint          # Biome lint
make sdk-generate  # Regenerate SDKs from OpenAPI