# Background Agents Source: https://docs.getomni.co/admin/background-agents Configure scheduled AI agents that run tasks automatically ## Overview Background agents are scheduled AI agents that execute tasks automatically on a recurring basis. They can search across your connected sources, look up people, and send emails — all without manual interaction. Background agents are disabled by default. Set `AGENTS_ENABLED=true` in your environment to enable this feature. ## Enabling Background Agents 1. Set the `AGENTS_ENABLED` environment variable to `true` in your `.env` file: ```bash theme={null} AGENTS_ENABLED=true ``` 2. Restart your services ## Agent Types | Type | Scope | Description | | -------------- | ------------ | -------------------------------------------------------------------------------- | | **User Agent** | Personal | Runs with the creator's document permissions and selected connector capabilities | | **Org Agent** | Organization | Runs with access to all data, with an explicit action whitelist | ### User Agents User agents are personal — document search uses the permissions of their creator. The **App Access** selections constrain source-specific connector and MCP capabilities; unified search can still use any indexed source the creator is permitted to read. User agents are useful for recurring personal tasks like weekly summaries or monitoring specific topics. ### Org Agents Org agents have broader access across all connected data sources. Core read-only tools such as search and people lookup remain available; an action whitelist controls optional capabilities such as email sending and connector actions. Only admins can create org agents. Regular users see and can open an **interactive chat** only with user agents they created. Admins can also access and chat with organization agents; another user's personal agent remains owner-only. Scheduled user-agent searches use the creator's document permissions, while scheduled org-agent searches use organization scope. ## Available Tools Agents can use the following tools during execution: | Tool | Description | | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | **search** | Search across connected sources using the same index as the UI | | **search\_people** | Search the people directory by name, email, title, department, location, employee type, or manager | | **search\_chats** | For user agents, find the creator's previous Omni chats by title and message content | | **read\_chat** | For user agents, read one branch of a previous chat, with pagination for long conversations | | **read\_document** | Fetch full text of a document (emails, Slack threads, Jira tickets, etc.) by ID | | **read\_file** / **write\_file** / **edit\_file** | Read, write, and edit files inside the sandbox workspace | | **run\_bash** | Execute a bash command inside the sandbox | | **run\_python** | Execute a Python snippet inside the sandbox | | **send\_email** | Send an email via the configured email provider (org agents only) | | **web\_search** | Search the public web with the admin-configured current search provider | | **fetch\_web\_page** | Fetch readable content from public HTTP(S) URLs with the admin-configured current fetch provider | | **Connector actions** | Source-specific actions exposed by connectors, including MCP-backed resources/prompts and provider API actions | Unlike interactive chat, background agents run without a human in the loop, so **there is no per-tool approval prompt**. Instead, what an agent is allowed to do is decided at creation time: org agents have an `allowed_actions` whitelist that controls which optional actions are registered (for example, `send_email` or a connector action is only available if it was added to the whitelist), and user agents have an `allowed_sources` list that scopes connector and MCP capabilities to specific connected sources. Document search remains permission-filtered to the creator rather than restricted by this list. `search_chats` and `read_chat` use the creator's user context. They can read a user agent creator's own previous chats, but scheduled org-agent runs have no user context and cannot access an individual user's chat history. Connector actions that require per-user OAuth need the relevant user credential to exist before a background run can use them; background runs cannot pause for a connection prompt the way interactive chat can. Web tools require providers to be configured in **Settings** → **Web Providers**; fetches reject localhost, private/internal IP targets, and domains or URL prefixes on the admin blocklist. `run_bash` and `run_python` execute inside the `omni-sandbox` container, which is isolated from the rest of your infrastructure. The sandbox has a preloaded set of CLI tools (including an Excel-to-text converter) and disposable file storage per run. The Python runtime also includes `pandas`, `numpy`, `openpyxl`, `xlsxwriter`, `python-docx`, `python-pptx`, `reportlab`, and `pypdf`, so agents can create Word, PowerPoint, Excel, and PDF files as artifacts. Files written to a chat's workspace are versioned automatically, so older versions are preserved. ## Configuration These environment variables control agent behavior. See [Configuration Reference](/deployment/configuration#background-agents-configuration) for details. | Variable | Default | Description | | ------------------------------- | ------- | ----------------------------------------------- | | `AGENT_SCHEDULER_POLL_INTERVAL` | `30` | Seconds between scheduler checks for due agents | | `AGENT_MAX_CONCURRENT_RUNS` | `3` | Maximum agents running simultaneously | | `AGENT_MAX_ITERATIONS` | `15` | Maximum tool calls per agent run | ## Memory When Memory is enabled and the organization default is **Full memory**, background agents can use memory from prior runs as context for future runs. Agent memory is stored under the agent's own namespace, separate from user chat memory. Admins configure the organization default and memory model from **Settings** → **Memory**. Users can lower their own memory mode for interactive chats, but the admin default is the maximum available mode. ## Creating an Agent 1. Navigate to the **Agents** section in the Omni sidebar 2. Click **Create Agent** 3. Configure the agent: * **Name**: A descriptive name for the agent * **Prompt**: Instructions for what the agent should do * **Schedule**: Cron expression or interval (e.g., every 6 hours, daily at 9am) * **Type**: User or Org * **Model**: Optionally select a specific LLM model 4. Click **Save** The agent will run automatically according to its schedule. You can view run history and results in the agent's detail page. ## Monitoring Each agent run is tracked with: * **Status**: Pending, Running, Completed, or Failed * **Summary**: An auto-generated summary of what the agent accomplished * **Duration**: How long the run took * **Tool calls**: Number of tool invocations during the run Navigate to an agent's detail page to view its run history and outputs. # Backup & Restore Source: https://docs.getomni.co/admin/backup-restore Backing up and restoring your Omni instance ## Overview All Omni data is stored in PostgreSQL. Backing up your database ensures you can recover: * User accounts and settings * Connector configurations * Indexed documents and search index * Chat history ## Backup ### Docker Compose If you haven't already, set up the alias: ```bash theme={null} alias omni-compose="docker compose -f docker/docker-compose.yml --env-file .env" ``` ```bash theme={null} omni-compose exec postgres pg_dump -U omni omni | gzip > backup_$(date +%Y%m%d).sql.gz ``` ### AWS (Terraform) Postgres runs as an ECS task. Use ECS Exec to run the backup: ```bash theme={null} aws ecs execute-command --cluster omni --task \ --container postgres --interactive \ --command "pg_dump -U omni omni" | gzip > backup_$(date +%Y%m%d).sql.gz ``` ## Restore ### Docker Compose ```bash theme={null} gunzip < backup.sql.gz | omni-compose exec -T postgres psql -U omni omni ``` ### AWS (Terraform) ```bash theme={null} gunzip < backup.sql.gz | aws ecs execute-command --cluster omni --task \ --container postgres --interactive \ --command "psql -U omni omni" ``` Restoring will overwrite existing data. Stop all Omni services before restoring. ## Recommendations * **Daily backups** for production instances * **Test restores** periodically to verify backup integrity * **Store backups offsite** or in a different region than your primary deployment # Connector Management Source: https://docs.getomni.co/admin/connector-management Monitor and manage your connected data sources ## Overview Each connector runs as an independent service (a Docker container in self-hosted deployments, or an ECS task on AWS) and syncs data independently. The **omni-connector-manager** service orchestrates all connector operations, including scheduling syncs, tracking progress, and managing concurrent operations. For instructions on adding a new connector, see the [Connectors Overview](/connectors/overview). ## Connector Manager The connector-manager service is the central orchestrator for all connector operations. It provides: | Capability | Description | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Sync Orchestration** | Triggers syncs on-demand or on schedule | | **Progress Tracking** | Real-time sync progress via Server-Sent Events | | **Concurrency Control** | Limits concurrent syncs (default: 10 global, 3 per type) | | **Stale Sync Detection** | Marks hung syncs as failed after `STALE_SYNC_TIMEOUT_MINUTES` of inactivity | | **Interrupted Sync Recovery** | Polls each connector's `/sync/{id}` endpoint on every scheduler tick; if a connector has lost the run (typically due to a restart), re-triggers it so it resumes from the last persisted checkpoint (up to 3 attempts before failing the run) | | **Per-source Scheduling** | Stores a sync interval on each source so admins can tune high-volume and low-volume integrations separately | | **SDK Endpoints** | API for custom connectors built with Python/TypeScript SDK | ### Configuration Key connector-manager settings (via environment variables): | Variable | Default | Description | | -------------------------------- | ------- | ---------------------------------------------------------------------------- | | `MAX_CONCURRENT_SYNCS` | `10` | Maximum concurrent syncs across all sources | | `MAX_CONCURRENT_SYNCS_PER_TYPE` | `3` | Maximum concurrent syncs per connector type | | `SCHEDULER_INTERVAL_SECONDS` | `30` | How often the scheduler checks for due syncs | | `STALE_SYNC_TIMEOUT_MINUTES` | `60` | Timeout to mark a sync as stale/failed | | `EXTRACTION_CONCURRENCY` | `2` | Maximum concurrent document extraction requests handled by connector-manager | | `EXTRACTION_RETRY_AFTER_SECONDS` | `30` | Retry delay advertised when extraction capacity is saturated | See [Configuration Reference](/deployment/configuration) for all options. ## Document Conversion Connectors that ingest binary files (PDFs, Office docs, images) can route their text extraction through the shared Docling service. Deploy the optional Docling container first, then toggle extraction on or off and pick a quality preset from **Settings** → **Document Conversion**: * **Fast** — text-heavy docs, basic tables, no OCR * **Balanced** *(default)* — accurate tables, no OCR; good tradeoff for most deployments * **Quality** — OCR on, higher image scale, code/formula enrichment The settings page is shown when `DOCLING_ENABLED=true`; Docker Compose deployments also need the `docling` profile running. Preset changes take effect immediately — no restart needed. See [Configuration Reference → Document Conversion](/deployment/configuration#document-conversion-docling) for environment-level tuning (CPU pinning, memory limits, concurrency). ## Viewing Connector Status Navigate to **Settings** → **Integrations** to manage data sources, OAuth app clients, and remote MCP servers. The page has three tabs: | Tab | Purpose | | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Sources** | View connected sources, connect new integrations, trigger manual syncs, and open source settings. Also hosts the **Windshift server** configuration card when the Windshift connector is registered | | **OAuth Apps** | Configure OAuth clients for connector actions that need per-user authorization | | **MCP** | Connect to remote MCP servers and manage them as Omni sources | The OAuth Apps tab also shows the shared redirect URI to register with providers that use Omni's generic OAuth flow. Some native connectors expose MCP tools as source-scoped actions. Atlassian (Rovo MCP) and Salesforce both configure their per-user OAuth client on the source's own settings page — for example, **Configure MCP OAuth client** on the Salesforce source — rather than on the OAuth Apps tab. Each user then authorizes the provider individually before the MCP tools become available to them. Omni Integrations For each source, you can see: * **Enabled/Disabled** status * **Last sync** timestamp * **Sync progress** when a sync is running, including scanned and updated counts * **Indexed document count** * **Unhealthy** status and latest error when scheduled syncs have been paused after repeated failures ## Remote MCP Servers The **MCP** tab lets admins connect to external MCP servers over HTTP(S) and use their tools and resources inside Omni, with no connector container required. Each server is created with a display name, an endpoint URL, an app/source slug (2-50 lowercase letters, numbers, hyphens, or underscores; cannot be changed later), and one of three auth modes: | Auth mode | Use case | | --------------------------- | ----------------------------------------------------------------------------------------------- | | **Public / no credentials** | Server is openly accessible | | **Shared bearer token** | Single token shared across the instance | | **Per-user OAuth** | Each user authorizes individually; OAuth is configured on the server's edit page after creation | Before saving, use **Test connection** to probe the server. Omni detects the server name, version, and its tool/resource counts, and suggests an app slug. You can also choose whether to expose write-capable tools; servers created without write tools show a **Read-only** badge and their destructive tools are withheld from the assistant. Each remote MCP server appears as a source on the **Sources** tab once created, with tool and resource counts surfaced from the probe, and can be opened from the MCP tab to manage its settings. ## Enabling and Disabling Connectors Each connector can be enabled or disabled independently. A disabled connector: * Stops syncing new content * Cancels any running sync for that source on the next scheduler tick * Retains previously indexed documents * Can be re-enabled at any time To toggle a connector's status, click **Settings** for that source and change the enabled setting. ## Sync Operations ### Automatic Sync Connectors automatically sync on a schedule. After the initial full sync, incremental syncs run periodically to capture new and updated content. Admins can adjust a source's **Sync Interval** from the source settings page. Presets range from every 30 minutes to weekly, and custom intervals can be set between 1 minute and 365 days. The scheduler uses the source's latest successful sync plus this interval to decide when the next scheduled sync is due. ### Manual Sync To trigger a manual sync: 1. Navigate to **Settings** → **Integrations** 2. Find the source 3. Click **Sync** You can only trigger a manual sync if no sync is currently running. If a sync is in progress, the button will be disabled. ## Removing a Connector To disconnect a data source: 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** for the source 3. Click **Delete Permanently** 4. Confirm the removal When you remove a source: * The source is marked for deletion * All indexed documents, credentials, and sync history for that source are deleted * Users will no longer see results from that source ## Troubleshooting ### Connector shows "Error" status Check the connector logs. For Docker Compose deployments: ```bash theme={null} # Check specific connector logs docker logs omni--connector # Check connector-manager logs for orchestration issues docker logs omni-connector-manager ``` For AWS deployments, check CloudWatch Logs for the connector's ECS task. Common issues: * Expired or revoked credentials * API rate limiting * Network connectivity problems * Connector-manager unable to reach connector service ### Source shows unhealthy or sync seems stuck If a source is marked **Unhealthy**, scheduled syncs have been paused after repeated scheduled failures. Manual sync failures are treated as user-initiated probes and do not trip the scheduled-sync circuit breaker. The source settings page includes a **Sync history** panel with the latest 10 scheduled runs, their type, duration, scanned/processed/updated counts, and errors. If a sync has been running for an unusually long time: 1. Check the connector-manager logs for heartbeat failures and auto-resume attempts: ```bash theme={null} docker logs omni-connector-manager | grep -iE "stale|timeout|lost|resume" ``` 2. Check the connector logs for errors 3. If the connector restarted, connector-manager will detect the lost run on its next scheduler tick and re-trigger it from the last checkpoint (up to 3 resume attempts). 4. Syncs that continue to make no progress are marked failed after `STALE_SYNC_TIMEOUT_MINUTES` of inactivity (default: 60 minutes). 5. Restart the connector service if it appears hung rather than progressing. ### Connector not receiving sync requests Verify connector-manager can reach the connector: 1. Check connector URL configuration: ```bash theme={null} # Ensure CONNECTOR__URL is set correctly echo $CONNECTOR_GOOGLE_URL ``` 2. Test connectivity from connector-manager: ```bash theme={null} docker exec omni-connector-manager curl http://:/health ``` 3. Check connector-manager logs for connection errors ### Too many concurrent syncs If syncs are queuing or timing out: 1. Check current sync count in connector-manager logs 2. Adjust concurrency limits: * `MAX_CONCURRENT_SYNCS` (default: 10) * `MAX_CONCURRENT_SYNCS_PER_TYPE` (default: 3) 3. Consider staggering sync schedules for different sources # Email Provider Source: https://docs.getomni.co/admin/email-provider Configure an email provider for transactional emails and AI-powered email actions ## Overview Omni uses an email provider to send transactional emails — notifications, password resets, and AI-initiated emails. Before any emails can be sent, an admin must configure a provider. Email provider configuration is managed entirely through the admin UI. No environment variables are needed. ## Supported Providers | Provider | Description | | -------------------------------------- | ------------------------------------------ | | **Azure Communication Services (ACS)** | Microsoft's cloud email service | | **Resend** | Developer-friendly transactional email API | | **SMTP** | Any standard SMTP server | Only one email provider can be active at a time. ## Setup ### Navigate to Email Settings 1. Go to **Settings** → **Email Provider** in the admin panel 2. Select the provider you want to configure 3. Enter the required configuration 4. Click **Save** ### Azure Communication Services (ACS) | Field | Description | | --------------------- | ---------------------------------------------------- | | **Connection String** | Your ACS connection string from the Azure Portal | | **Sender Address** | A verified sender email address in your ACS resource | ### Resend | Field | Description | | -------------- | ----------------------------------------------------- | | **API Key** | Your Resend API key | | **From Email** | The sender email address (must be verified in Resend) | ### SMTP | Field | Description | | -------------- | ----------------------------------- | | **Host** | SMTP server hostname | | **Port** | SMTP server port (default: 587) | | **Username** | SMTP authentication username | | **Password** | SMTP authentication password | | **Use TLS** | Enable TLS encryption (recommended) | | **From Email** | The sender email address | ## AI Email Sending In addition to transactional emails, the AI assistant can send emails on behalf of users during chat. When a user asks the AI to send an email in a conversation, the AI drafts the message and the user is asked to **approve or reject** before it is sent. [Background agents](/admin/background-agents) with the email action enabled can also send emails autonomously as part of their scheduled tasks. # Memory Source: https://docs.getomni.co/admin/memory Configure organization-wide AI memory behavior ## Overview Memory lets Omni retain selected context across future chats and background agent runs. It is disabled by default and must be enabled at deployment time with `MEMORY_ENABLED=true`. Memory requires an active embedding provider. Configure one in **Settings** → **Embedding Providers** before enabling memory for users. ## Admin Settings Navigate to **Settings** → **Memory** to configure: | Setting | Description | | ---------------- | ------------------------------------------------------------------------------------ | | **Default mode** | Organization-wide memory ceiling and default for users without a personal preference | | **Memory model** | LLM used to extract and recall memories. If unset, Omni uses the default chat model | ### Memory Modes | Mode | Behavior | | --------------- | -------------------------------------------------------------------------------- | | **Off** | Memory is disabled for all users and agents | | **Chat memory** | Completed chat turns can be summarized into memories and recalled in later chats | | **Full memory** | Chat memory plus agent run context for background agents | The organization default is a ceiling. Users can choose a lower personal mode from **Settings** → **Memory**, but cannot exceed the mode selected by an admin. ## User Controls Users can view and delete their own memories from **Settings** → **Memory**. They can also delete all stored memories for their account. Deleting memories is permanent. Omni will not be able to recall deleted context in future chats. ## Deployment Configuration Set these environment variables before starting Omni: | Variable | Default | Description | | ----------------- | ------- | ------------------------------------------------------------------ | | `MEMORY_ENABLED` | `false` | Enables Memory settings pages and AI-service memory initialization | | `MEMORY_PROVIDER` | `mem0` | Memory backend provider. `mem0` is currently supported | See [Configuration Reference](/deployment/configuration#memory-configuration) for the full environment variable reference. # Single Sign-On (SSO) Source: https://docs.getomni.co/admin/sso Configure SSO for your Omni instance using Okta or Microsoft Entra ID **Google OAuth** is supported in the community edition and does not require a commercial license. Configure it from **Settings** → **Authentication** in the admin panel. **Okta** and **Microsoft Entra ID** SSO are **enterprise features**. They are free for evaluation with up to 10 users. For org-wide rollout, a commercial license is required — contact [hello@getomni.co](mailto:hello@getomni.co) for pricing. ## Overview Omni supports Single Sign-On via Google OAuth (community), Okta, and Microsoft Entra ID. Once SSO is configured, you can optionally disable password-based authentication. All SSO provider configurations — including client secrets — are stored encrypted in the database and managed through the admin UI, not environment variables. ## Enterprise Prerequisites * An Okta or Entra ID admin account with permission to create applications * A commercial Omni license (or evaluation with fewer than 10 users) * Omni deployment using the `omni-web-enterprise` Docker image ## Enable Enterprise SSO ### Step 1: Switch to the Enterprise Image Replace the `omni-web` Docker image with `omni-web-enterprise` in your `docker-compose.yml`: ```yaml theme={null} web: image: ghcr.io/getomnico/omni-web-enterprise:${OMNI_VERSION:-latest} ``` Restart services after making this change. The enterprise image pulls in the Okta and Entra ID provider packages, which the community image falls back to `null` for. ### Step 2: Create an Okta Application 1. Log in to your Okta Admin Console 2. Go to **Applications** → **Create App Integration** 3. Select **OIDC - OpenID Connect** and **Web Application** 4. Configure the application: * **App integration name**: Omni * **Sign-in redirect URI**: `https:///auth/okta/callback` * **Sign-out redirect URI**: `https://` 5. Under **Assignments**, assign the app to the users or groups who should have access 6. Save the application 7. Note the **Client ID** and **Client Secret** from the application settings ### Step 3: Configure Okta in Omni 1. Navigate to **Settings** → **Authentication** in the Omni admin panel 2. Under **Okta SSO**, enter: * **Okta Domain**: Your Okta domain (e.g., `yourcompany.okta.com`) * **Client ID**: From Step 2 * **Client Secret**: From Step 2 3. Click **Save** SSO is now configured. Users will see a "Sign in with Okta" option on the login page. ## Microsoft Entra ID Entra ID (formerly Azure AD) is configured the same way as Okta — register an application, add a client secret, and enter the tenant ID, client ID, and client secret in **Settings** → **Authentication** → **Microsoft Entra ID**. ### Step 1: Register an Enterprise Application in Entra ID 1. Sign in to the [Azure Portal](https://portal.azure.com) as a Global Administrator 2. Go to **Microsoft Entra ID** → **App registrations** → **New registration** 3. Set the name: **Omni SSO** 4. Supported account types: **Single tenant** 5. Redirect URI: **Web** → `https:///auth/entra/callback` 6. Click **Register** 7. Note the **Application (client) ID** and **Directory (tenant) ID** ### Step 2: Create a Client Secret 1. Go to **Certificates & secrets** → **New client secret** 2. Set an expiry period and click **Add** 3. Copy the secret value immediately — it will not be shown again ### Step 3: Configure Entra ID in Omni 1. Navigate to **Settings** → **Authentication** 2. Under **Microsoft Entra ID**, enter the **Tenant ID**, **Client ID**, and **Client Secret** 3. Click **Save** ## Disabling Password Authentication Once at least one SSO provider is active, you can disable password-based login: 1. Navigate to **Settings** → **Authentication** 2. Toggle off **Password Authentication** Before disabling password auth, ensure at least one admin has signed in via the other auth method (Google, Okta, or Entra ID). This prevents account lockout. Password auth cannot be disabled unless another authentication method is active. # Upgrades Source: https://docs.getomni.co/admin/upgrades Upgrading your Omni instance to a new version ## Docker Compose If you haven't already, set up the alias: ```bash theme={null} alias omni-compose="docker compose -f docker/docker-compose.yml --env-file .env" ``` ### Upgrade to Latest Pull the latest images and restart: ```bash theme={null} omni-compose pull omni-compose up -d ``` Database migrations run automatically on startup. ### Pin a Specific Version To pin all Omni services to a specific release, set `OMNI_VERSION` in your `.env` file: ```bash theme={null} OMNI_VERSION=v0.1.9 ``` Then pull and restart: ```bash theme={null} omni-compose pull omni-compose up -d ``` When `OMNI_VERSION` is unset, all services default to `latest`. Pinning a version is recommended for production deployments to avoid unexpected changes during restarts. ## AWS (Terraform) Force a redeployment of the ECS tasks to pull the latest images: ```bash theme={null} aws ecs update-service --cluster omni --service omni-web --force-new-deployment aws ecs update-service --cluster omni --service omni-indexer --force-new-deployment aws ecs update-service --cluster omni --service omni-ai --force-new-deployment aws ecs update-service --cluster omni --service omni-searcher --force-new-deployment ``` Repeat for any connector services you have running. # User Management Source: https://docs.getomni.co/admin/user-management Managing users in your Omni instance ## Overview Omni provides basic user management through the Admin panel. Users are stored in the database and can be managed via the UI or API. Omni supports **Google OAuth** out of the box, and optional enterprise [Single Sign-On (SSO)](/admin/sso) via Okta or Microsoft Entra ID. With SSO enabled, you can optionally disable password-based authentication. ## Managing Users ### Creating Users 1. Navigate to **Admin** → **Users** 2. Click **Add User** 3. Enter the user's email and name 4. Set a temporary password 5. Click **Create** The user will be able to log in immediately with the temporary password. ### Editing Users Select a user from the list to: * Update their name or email * Change their role (Admin or User) * Disable or re-enable their account ### Resetting Passwords To reset a user's password: 1. Select the user from the list 2. Click **Reset Password** 3. Enter a new temporary password 4. Share the temporary password with the user securely Users can change their password after logging in via their profile settings. ### Deleting Users 1. Select the user from the list 2. Click **Delete** 3. Confirm the deletion Deleting a user is permanent and cannot be undone. Consider disabling the account instead if you may need to restore access later. ## User Roles | Role | Permissions | | ---------- | --------------------------------------------------------------------------------- | | **Viewer** | Search and chat only — read-only access | | **User** | Search, chat, and manage their own sources | | **Admin** | All user permissions plus: manage users, configure connectors, access admin panel | At least one admin must exist at all times. ## API Keys Each user can create API keys for programmatic access to Omni's HTTP API. Keys are managed from the user's profile in **Settings** → **API Keys** (or via the `/api/v1/api-keys` endpoint). | Scope | Access | | ---------- | ----------------------------------------------------------- | | **Public** | Search and document read endpoints only (default) | | **User** | User-level operations — chats, agents, own sources | | **Admin** | Org-level administrative endpoints; requires the admin role | When creating a key, you can optionally restrict it to an **allowed sources** list, which is intersected with the user's own access at request time. Keys are shown in full exactly once at creation time. Omni stores a SHA-256 hash and a short prefix for later identification. Each user is capped at 25 active keys. ## Disabling Password Authentication If [SSO is configured](/admin/sso), admins can disable password-based login: 1. Navigate to **Settings** → **Authentication** 2. Toggle off **Password Authentication** At least one other authentication method (Google OAuth, Okta, or Entra ID) must be active before password auth can be disabled. Additionally, the admin performing this action must have signed in via the other method at least once to prevent lockout. # Web Providers Source: https://docs.getomni.co/admin/web-providers Configure public web search and page fetch providers for Omni agents ## Overview Web providers let Omni's AI assistant and background agents look up public web information live during a chat or agent run. They are optional and configured independently from the [Web Connector](/connectors/web), which crawls a fixed website into Omni's internal index. Configure web providers from **Settings** → **Web Providers**. Provider API keys are encrypted at rest using Omni's normal service-credential encryption. ## Provider Types | Provider kind | Supported providers | Agent tool | Purpose | | ------------- | ------------------------- | ---------------- | ---------------------------------------------------------- | | **Search** | Exa, Serper, Brave Search | `web_search` | Search the public web and return citation-friendly results | | **Fetch** | Exa, Firecrawl | `fetch_web_page` | Fetch readable content from a specific public HTTP(S) URL | You can connect more than one provider of each kind, but only one search provider and one fetch provider are marked **Current** at a time. ## Setup 1. Navigate to **Settings** → **Web Providers** 2. In **Search Providers** or **Fetch Providers**, click **Connect** on the provider you want to use 3. Enter the provider API key 4. Optionally set a custom **Base URL** if you use a proxy or compatible endpoint 5. Click **Connect** 6. If multiple providers are connected for the same kind, click **Set current** on the provider Omni should use `fetch_web_page` is only available when a fetch provider is configured. `web_search` is available when a search provider is configured. ## URL Blocklist The same settings page includes a **URL Blocklist** for public web fetches. Add one pattern per line: ```text theme={null} example.com *.example.com https://example.com/private ``` | Pattern | Behavior | | ----------------------------- | -------------------------------------------- | | `example.com` | Blocks exactly that hostname | | `*.example.com` | Blocks subdomains such as `docs.example.com` | | `https://example.com/private` | Blocks URLs starting with that prefix | The fetch tool also rejects localhost URLs and URLs that resolve to private/internal IP addresses. ## How Agents Use Web Providers When web providers are configured, the assistant may use: | Tool | Description | | ---------------- | ------------------------------------------------------------------------------------------------------------ | | `web_search` | Searches public web results for current or external information not expected to be in Omni's workplace index | | `fetch_web_page` | Fetches readable content from a specific public HTTP(S) URL and treats it as untrusted context | Fetched page content is wrapped as untrusted web content so the model treats it as data, not instructions. ## Troubleshooting Confirm a search provider is connected and marked **Current**. Also check that the user's model has tool use enabled and that the question actually requires external/current information. Check the URL blocklist. Omni also blocks localhost and private/internal network targets to prevent server-side request forgery. Verify the API key, optional base URL, account quota, and provider status. Remove and reconnect the provider if the key was rotated. ## Security Considerations * **Public HTTP(S) only**: Fetches must use `http://` or `https://` * **Network safety**: Localhost and private/internal IP ranges are blocked * **Admin policy**: Use the URL blocklist for sensitive domains or URL prefixes * **Untrusted content**: Fetched pages are treated as context only, not instructions * **Separate from indexing**: Live web results are not automatically added to Omni's search index # Architecture Source: https://docs.getomni.co/architecture Understanding how Omni works under the hood ## Overview ### Philosophy Omni bets on PostgreSQL. Instead of stitching together separate systems for storage, search, and message queuing, Omni uses PostgreSQL for all three. Services communicate through a message queue backed by a PostgreSQL table. The same database handles full-text search and vector search. One database to deploy, monitor, back up, and understand. ### Why Postgres? Enterprise search typically means running a primary database alongside a dedicated search engine like Elasticsearch and a message broker like Kafka, plus the glue code to keep them in sync. That's significant operational overhead for most organizations. Omni takes a different approach by building on [ParadeDB](https://paradedb.com) - PostgreSQL with the pg\_search extension, powered by Tantivy. Tantivy is a Rust-based inverted index architecturally similar to Lucene, the engine behind Elasticsearch. Combined with pgvector for embeddings, you get hybrid search (keyword + semantic) with sub-second latency at millions of documents - all in PostgreSQL. For message queuing, Omni uses PostgreSQL tables with row locking. This adds polling overhead compared to Kafka, but document indexing workloads measure in thousands of events per hour, not millions per second. The simplicity tradeoff is worth it. A single generic `tasks` table now backs multiple workloads — including document embeddings — with claim/lease, retry, deduplication, and dead-letter handling shared across the Rust and Python services. Omni is designed for organizations with up to \~5 million documents. Beyond that scale, you may want to introduce read replicas or evaluate a dedicated search tier, but most enterprises never get there. Omni Architecture Diagram ## Components | Service | Purpose | | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **omni-web** | SvelteKit frontend and API gateway. Handles auth, serves the UI, and routes requests to backend services. | | **omni-searcher** | Rust service that executes hybrid search (full-text + vector) against PostgreSQL and applies permission filtering. | | **omni-indexer** | Rust service that consumes events from the message queue, extracts document content, queues embedding work on the generic task queue, and writes to PostgreSQL. | | **omni-ai** | Python/FastAPI service that generates queued embeddings and orchestrates LLM interactions. Supports Anthropic, OpenAI, Google Gemini, AWS Bedrock, Vertex AI, Azure AI Foundry, and any OpenAI-compatible endpoint (e.g. self-hosted `llama.cpp`). Includes conversation compaction, token usage tracking, background agents, and public web search/fetch tools when admins configure web providers. | | **omni-connector-manager** | Rust service that orchestrates connector sync operations, manages scheduling, tracks progress, and provides SDK endpoints for custom connectors. | | **omni-sandbox** | Rust service providing an isolated execution environment for agent tools — bash, Python, and file manipulation — invoked by the AI service during chats. | | **omni-docling** | Optional Python service (Docling) used by the indexer to extract structured text from PDFs, Office documents, and common image formats. Toggled on or off per-instance from the admin UI. | | **Connector Services** | Microservices that sync data from external sources (Google Drive, Gmail, Google Chat, Google Ads, Microsoft 365, Slack, HubSpot, Salesforce, Confluence, JIRA, Darwinbox, Nextcloud, Paperless-ngx, filesystem, and more) and publish events to the queue. Built using the Python or TypeScript SDK, or as native Rust services. | ## Permissions Omni inherits permissions from source systems. Users only see documents they have access to in the original source. 1. **Sync**: Connectors pull permission metadata alongside documents 2. **Store**: Permissions are stored in PostgreSQL, linked to each document 3. **Query**: Every search filters results by the requesting user's access 4. **Refresh**: Permissions re-sync periodically or via webhooks ## Next Steps Choose your deployment strategy Connect your data sources # Atlassian (Confluence & JIRA) Source: https://docs.getomni.co/connectors/atlassian Connect Confluence and JIRA to Omni The Atlassian connector enables Omni to index and search content from your organization's Confluence and JIRA instances. ## Overview ### What Gets Indexed | Source | Content | | ---------- | ----------------------------------------------- | | Confluence | Pages, blog posts, comments, and attachments | | JIRA | Issues, comments, descriptions, and attachments | ### How It Works 1. An Atlassian service-account token accesses the Atlassian REST APIs through the Atlassian API gateway 2. The connector syncs content based on project and space permissions 3. Permission inheritance ensures users only see content they have access to in Atlassian 4. Optional Atlassian organization-admin credentials improve account and group resolution when Atlassian Guard is available The connector uses read-only access. Omni cannot modify any content in your Atlassian instance. ## Prerequisites Before setting up the Atlassian connector, ensure you have: * **Atlassian Cloud account** (Confluence and/or JIRA) * **Admin access** to your Atlassian organization * **Atlassian service account** with access to the Confluence spaces and JIRA projects you want to index * **Service account token** created from Atlassian Administration * Optional: **Atlassian Guard** and an organization admin API key for richer directory sync ## Setup ### Step 1: Create a Service Account Token 1. Open [Atlassian Administration](https://admin.atlassian.com) 2. Go to **Security** → **Service accounts** 3. Create a service account for Omni 4. Generate a service account API token 5. Copy the generated token immediately — you won't be able to see it again Use a service account token, not a personal Atlassian API token. Service account tokens usually start with `ATSTT`; personal account tokens usually start with `ATATT` and are not the supported auth model for this connector. Grant the service account read access to the Confluence spaces and JIRA projects you want Omni to index. Omni filters the service account itself out of indexed permission lists so it does not become an end-user access grant. ### Step 2: Gather Your Atlassian Details You'll need the following information: | Field | Description | Example | | -------------------------- | ------------------------------------------------------------------ | -------------------------------------- | | Atlassian Domain | Your Atlassian Cloud domain | `your-company.atlassian.net` | | Service Account Token | The token you created in Step 1 | `ATSTT...` | | Organization ID | Optional. Atlassian organization UUID for org-admin directory sync | `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` | | Organization Admin API Key | Optional. Admin API key for Atlassian Guard directory APIs | `...` | Your Atlassian domain is the URL you use to access Confluence or JIRA, without the `https://` prefix. The optional organization-admin fields require Atlassian Guard. When they are absent, Omni falls back to per-site Atlassian APIs for account-to-email and group resolution. ### Step 3: Connect to Omni 1. Navigate to **Settings** → **Integrations** in Omni 2. Find **Atlassian** and click **Connect** 3. Enter your details: * **Atlassian Domain**: Your Atlassian Cloud domain (e.g., `your-company.atlassian.net`) * **Service Account Token**: The token you created in Step 1 * Optional **Organization ID** and **Organization Admin API Key** 4. Click **Connect** ### Step 4: Configure Sources After connecting, you need to configure which Atlassian products to index: 1. Click **Settings** next to Confluence or JIRA 2. Choose which spaces (Confluence) or projects (JIRA) to include or exclude 3. Save the source settings 4. Click **Sync** and wait for the initial sync to complete Your Atlassian connector is now configured. Initial indexing may take a while depending on the amount of content in your Confluence and JIRA instances. *** ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view the sync status for each source directly on the list, including last sync time, number of indexed items, and any errors. Click **Settings** on a source for more details and sync history. ### Rovo MCP Actions The connector includes a native integration with Atlassian's Rovo MCP server. After a user authorizes their own Atlassian account, Rovo's tools become source-scoped Omni actions that run with **that user's** Atlassian identity rather than the indexing service account. * Omni registers itself as a public OAuth client with Rovo, uses S256 PKCE, and binds every token to the Rovo MCP resource — there is no client secret to configure. Because Rovo's OAuth flow has no userinfo endpoint, Omni binds the authorization to the signed-in Omni session. * Read tools request `read:confluence-content.all` and `read:jira-work`. Write tools request the matching `write:confluence-content` / `write:jira-work` scope and require explicit confirmation in chat. * Write and destructive tools are withheld when the source is read-only, and every MCP-backed action is validated against the live tool catalog before it runs. Rovo MCP requires each user to authorize Atlassian separately. If a user has no per-user credential, the MCP actions are unavailable to them until they connect. ### Rotating API Tokens It's recommended to rotate API tokens periodically for security. 1. Create a new service account token in Atlassian Administration 2. Update the token in Omni settings 3. Revoke the old token in Atlassian ### Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the Atlassian source 3. Click **Delete Permanently** 4. Optionally, revoke the API token in [Atlassian Account Settings](https://id.atlassian.com/manage-profile/security/api-tokens) *** ## Troubleshooting Common causes: * Service account token was copied incorrectly — create a new one and try again * A personal API token was used instead of a service account token * Token has been revoked or expired Verify your credentials by testing the API directly: ```bash theme={null} curl -H "Authorization: Bearer YOUR_SERVICE_ACCOUNT_TOKEN" \ "https://api.atlassian.com/ex/jira/YOUR_CLOUD_ID/rest/api/3/myself" ``` Ensure you're using the correct Atlassian Cloud domain: * Use `your-company.atlassian.net` (without `https://`) * Check that you're using Atlassian Cloud, not a self-hosted Data Center instance If some content isn't appearing: * Verify the space or project is included in your configuration * Check that the admin account has access to the content * Archived spaces and projects may need to be explicitly included * Wait for the sync cycle to complete (check sync status) The service account must be granted access to the Atlassian content you want indexed: * Ensure the service account has access to all spaces and projects you want to index * Consider using a dedicated service account with broad read access Initial sync duration depends on: * Number of Confluence pages and JIRA issues * Amount of attachments * API rate limits For large instances (10,000+ items), initial sync may take several hours. Monitor progress in the Omni admin panel. ## Security Considerations * **Read-only access**: Use a dedicated service account with read access to the Atlassian content you want indexed * **Permission inheritance**: Users only see content they can access in Atlassian * **Token security**: Service account tokens and org-admin API keys should be treated like passwords * **Token rotation**: Rotate tokens periodically (recommended every 90 days) * **Audit logging**: Monitor API token usage in Atlassian's admin panel ## What's Next Learn how to search across Confluence and JIRA content Ask questions about your documentation and issues Connect additional data sources # ClickUp Source: https://docs.getomni.co/connectors/clickup Connect ClickUp to Omni for unified search across tasks and documents The ClickUp connector enables Omni to index and search content from your ClickUp workspaces, including tasks, comments, and documents. ## Overview ### What Gets Indexed | Source | Content | | ------ | ----------------------------------------------------------------------- | | Tasks | Task title, description, status, priority, assignee, tags, and comments | | Docs | Document title and content (optional — can be toggled on/off) | ### How It Works 1. You create a ClickUp personal API token 2. The connector syncs tasks across all workspaces 3. Optionally enable document syncing 4. Workspace and space-based permissions ensure appropriate access control The connector uses read-only access. Omni cannot create, modify, or delete any content in your ClickUp workspace. ## Prerequisites Before setting up the ClickUp connector, ensure you have: * **ClickUp account** with permission to create API tokens * **Omni deployment** with the ClickUp connector service running ## Setup ### Step 1: Create a ClickUp API Token 1. Go to [ClickUp Settings > Apps](https://app.clickup.com/settings/apps) 2. Under **API Token**, click **Generate** 3. Copy the generated token Keep your API token secure. Anyone with access to this token can read tasks and documents from your ClickUp workspace. ### Step 2: Connect in Omni 1. Navigate to **Settings** → **Integrations** in Omni 2. Find **ClickUp** and click **Connect** 3. Enter your API token 4. Optionally enable **Include Docs** to sync ClickUp documents alongside tasks 5. Click **Connect** 6. Click **Sync** to start the initial sync Your ClickUp connector is now configured. Initial indexing may take a while depending on the number of tasks and documents in your workspaces. ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view the sync status for each source directly on the list, including last sync time, number of indexed items, and any errors. Click **Settings** on a source for more details and sync history. ### Space Filters By default, the connector indexes tasks and documents across all workspaces your API token can access. Use **space filters** in the source settings to limit task indexing to specific ClickUp spaces. ClickUp Docs are indexed with workspace-level permissions, so space filters do not limit their permissions. ### Sync Modes The ClickUp connector supports two sync modes: | Mode | Description | | --------------- | ----------------------------------------------------------------------- | | **Full** | Syncs all tasks and documents across workspaces (used for initial sync) | | **Incremental** | Syncs only items modified since last sync (used for updates) | After the initial full sync, the connector automatically performs incremental syncs to capture new and updated tasks and documents. Sync checkpoints are persisted safely, so interrupted runs resume from where they left off. ### MCP OAuth (Remote Tools) The ClickUp connector can also serve as an MCP bridge, exposing ClickUp as a set of Omni agent tools for reading and writing tasks and documents. This requires per-user OAuth authorization: 1. Navigate to **Settings** → **Integrations** → **ClickUp** 2. Under the **MCP OAuth** section, click **Connect** 3. You'll be guided through a ClickUp OAuth flow with PKCE — no admin-created OAuth app is needed 4. Once connected, Omni gains access to ClickUp actions scoped to your OAuth grant MCP tools are layered on top of the existing REST-based sync — the search index remains driven by the API token sync, while MCP actions use the per-user OAuth token for live read/write operations. ## Troubleshooting Common causes: * Sync has not completed yet * Your account does not have access to the workspace containing the tasks **Solution**: Verify the sync has completed and check that your API token has access to the relevant workspaces. The token may be expired or revoked. **Solution**: 1. Go to [ClickUp Settings > Apps](https://app.clickup.com/settings/apps) 2. Regenerate your API token 3. Delete the existing ClickUp source in Omni and reconnect it with the new token ClickUp has API rate limits. The connector handles these automatically with exponential backoff. **Solution**: Wait for the connector to retry. For large workspaces, initial sync may take longer due to rate limits. Document syncing is an optional feature that must be explicitly enabled. **Solution**: Ensure **Include Docs** is enabled during setup. You can update this setting in **Settings** → **Integrations** → **ClickUp**. ## Security Considerations * **Read-only access**: The connector only reads tasks and documents * **Workspace and space permissions**: Tasks retain private-space grouping when applicable; Docs use workspace-level permissions * **Token security**: API tokens should be treated like passwords * **Encrypted storage**: Credentials are encrypted at rest in Omni ## What's Next Learn how to search across ClickUp tasks and documents Ask questions about your ClickUp tasks and projects Connect additional data sources # Darwinbox Source: https://docs.getomni.co/connectors/darwinbox Connect Darwinbox to Omni for HR search and workflow actions The Darwinbox connector syncs the employee directory into Omni's people directory and exposes tightly scoped, interactive HR workflow actions. ## Overview ### What We Sync Each module maps to a Darwinbox API endpoint that must be enabled for your API credential — enable the endpoints for the modules you want to use: | Module | Content | Darwinbox API to allow | | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | People directory | Employees with an ID and company email returned by the employee master, stored as **person records** in the Omni people directory. Fixed approved field set: name (first/middle/last), employee ID, company email, department, designation, office location, manager employee ID, employee type, cost center, work country, grade, band, confirmation status, employment start/end dates, phone (mobile number), employment status (as active/inactive), and top department. Each authoritative snapshot reconciles records no longer returned | `POST /masterapi/employee` | | Org masters | Departments, designations, office locations, business units, divisions, cost centers, and group companies, synced as searchable documents | `POST /orgmasterapi/departmentlist`, `/orgmasterapi/designationlist`, `/orgmasterapi/officelocationlist`, `/orgmasterapi/businessunitlist`, `/orgmasterapi/divisionlist`, `/orgmasterapi/costcenterlist`, `/orgmasterapi/groupcompanylist` | | Job levels | Job-level definitions, synced as searchable documents | `POST /orgmasterapi/joblevellist` | | Employee-scoped org masters | Per-employee job level, office location, and manager records, synced as searchable documents | `POST /orgmasterapi/employeeJobLevel`, `/orgmasterapi/employeeLocation`, `/orgmasterapi/employeeManager` | | Positions | Synced as searchable documents | `POST /orgmasterapi/getpositionMaster` | | Holidays | Synced as searchable documents from the holiday list endpoint | `POST /leavesactionapi/holidaylist` | | ATS jobs | Synced as searchable documents | `POST /JobsApiv3/Joblist` | ### What We Deliberately Do NOT Sync The connector is designed around a curated **approved field set** that excludes sensitive and confidential data: * **Compensation, payroll, payslips, and tax documents** * **Personal/financial fields** (e.g. personal email, bank details) * **Employee documents** (offer/appointment/experience letters) * **HR policy documents, announcements, and learning content** (no read APIs exposed) * **Appraisal, goal, and performance data** Unknown fields returned by Darwinbox are silently ignored, so nothing outside the approved set is ever indexed. Records with a status other than `Active` remain indexed for reconciliation but are hidden from search. Employees explicitly marked with a non-active employment status are also omitted from people-directory search. ### How It Works 1. You provide your Darwinbox base URL, tenant credentials, and dataset key 2. The connector syncs the employee directory, org masters, job levels, employee-scoped org masters, positions, holidays, and ATS jobs, storing a checkpoint for resumable syncs 3. Employees become searchable person records in Omni; org-master, position, holiday, and ATS data becomes searchable documents 4. Interactive self-service and manager actions resolve the authenticated Omni user to their Darwinbox employee record and operate within the permitted audience ## Prerequisites Before setting up the Darwinbox connector, ensure you have: * **Darwinbox API access** for your tenant * **Base URL** for your Darwinbox instance, such as `https://acme.darwinbox.in` * **Dataset key** for the APIs Omni should call * One supported credential set: * Basic auth username, password, and API key * OAuth2 client credentials * Legacy dynamic-token credentials with an authorization code or refresh token * Omni deployment with the Darwinbox connector service running (profile: `darwinbox`) ## Setup ### Step 1: Connect in Omni 1. Navigate to **Settings** → **Integrations** 2. Find **Darwinbox** and click **Connect** 3. Enter your **Darwinbox base URL** 4. Choose an authentication mode: * **Basic auth + API key**: API username, password, API key, and dataset key * **OAuth2 client credentials**: Client ID, client secret, optional API key, and dataset key * **Legacy dynamic token**: Client ID, client secret, grant type, authorization code or refresh token, optional API key, and dataset key 5. Choose participant handling for actions: * **All users** (default) — any authenticated Omni user may invoke actions * **Allowlist** — restrict actions to specific participant emails 6. The connector starts in read-only mode. Enable write mode only after configuring participant and action authorizations. 7. Click **Connect** 8. Click **Sync** to start the initial sync Your Darwinbox connector is now configured. Initial indexing time depends on the number of employees and enabled modules. ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view sync status, indexed counts, and errors. Click **Settings** on the source for sync history and source configuration. ### Sync Modes | Mode | Description | | --------------- | -------------------------------------------------------------------------------- | | **Full** | Rebuilds the scoped employee directory and reconciles removed records | | **Incremental** | Performs the same authoritative snapshot so deletion reconciliation remains safe | ### Search Operators The connector registers source-specific operators that work in Search and Chat once the source is running: | Operator | Meaning | | --------------------- | ----------------------------------- | | `location:` | Filter by office location | | `position:` | Filter by position | | `department:` | Filter by department | | `designation:` | Filter by designation | | `division:` | Filter by division | | `business_unit:` | Filter by business unit | | `cost_center:` | Filter by cost center | | `company:` | Filter by group company | | `job:` | Filter by job title | | `job_level:` | Filter by job level | | `employee_job_level:` | Filter by employee-scoped job level | | `employee_location:` | Filter by employee-scoped location | | `employee_manager:` | Filter by employee-scoped manager | To look employees up by name, email, title, or direct manager, ask the assistant in chat — it queries the people directory with its `search_people` tool rather than an inline document operator. ### AI Assistant Actions Darwinbox actions are grouped by module: | Module | Available actions | | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Directory | `find_employee` — query the Darwinbox employee master by name, employee ID, email, or department and return approved profile fields | | Employee self-service | `get_my_profile`, `get_my_leave_balance`, `get_holiday_calendar`, `get_my_leave_requests`, `get_my_pending_tasks`, `get_my_attendance`, `get_my_timesheet`, `apply_my_leave`, `revoke_my_leave` | | Manager workflows | `list_pending_leave_approvals`, `get_team_leave_calendar`, `approve_leave_request`, `reject_leave_request` | Self-service actions resolve the authenticated Omni user's email to their Darwinbox employee record (`company_email_id` match), and identity parameters are rejected. Manager actions first verify that the caller has direct reports; approval and rejection actions can target only those direct reports. The source must not be read-only for write actions, and writes require confirmation in interactive chat. Darwinbox has no per-user OAuth. Actions use the org-wide credential, and connector-manager automatically falls back to it when a user has no per-user credential. Self-service and manager actions still resolve the caller against the directory. `find_employee` is a directory lookup and does not require the caller to be an employee, but it remains subject to the configured participant policy. ### Actions Not Currently Exposed The following registered actions remain disabled. They are not included in the connector manifest and are rejected at dispatch: | Module | Actions | | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Employee self-service | `regularize_my_attendance` | | Manager workflows | `get_team_attendance_exceptions`, `get_direct_report_profile` | | Reports | `fetch_report_ids`, `run_report` | | ATS | `list_jobs`, `get_job_detail`, `get_candidates`, `tag_candidate`, `reject_candidate`, `create_requisition`, `archive_requisition` | | HR operations | `fetch_employee_history`, `add_pending_employee`, `activate_pending_employee`, `update_employee_record`, `update_employment_details`, `deactivate_employee`, `reactivate_employee`, `upload_employee_document` | ## Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the Darwinbox source 3. Click **Delete Permanently** 4. Revoke or rotate the Darwinbox API credentials if needed ## Troubleshooting Verify the base URL, authentication mode, credentials, API key, and dataset key. Remove any trailing slash from the base URL. Confirm the dedicated credential can call the employee master endpoint and that the configured dataset key returns the approved directory fields. Check the participant mode/allowlist and whether the action is currently exposed. For write actions, also check read-only mode. Self-service and manager actions fail with "no Darwinbox employee found for caller email" when the caller's email does not match the directory; `find_employee` does not require that match. ## Security Considerations * **Read-only mode**: The connector is read-only by default. While enabled, no write operation (e.g. apply/revoke leave) can change data in Darwinbox. Disable read-only only after configuring participant and action authorizations. * **Participants**: By default every authenticated Omni user can invoke actions. An allowlist (opt-in) restricts actions to specific participant emails. * **Identity cannot be spoofed**: Self-service identity is derived from the authenticated Omni user's email, matched against the Darwinbox employee directory, and identity parameters are rejected. Manager actions accept a target employee only after verifying that the employee directly reports to the caller. * **Field allowlisting**: A fixed approved field set (name, employee ID, company email, department, designation, office location, manager, employee type, cost center, work country, grade, band, confirmation status, employment dates, phone, and top department) is the maximum the connector ever writes. Compensation, personal, financial, and other sensitive fields are excluded. * **Credential security**: Darwinbox credentials are encrypted at rest. Use a dedicated integration account with access only to the required API endpoints. * **Write confirmation**: All write operations require explicit user confirmation in the AI assistant. * **Kill switch**: Administrators can disable the Darwinbox source to remove its actions from the assistant. * **Token rotation**: Rotate API keys, passwords, and client secrets according to your Darwinbox security policy. ## What's Next Learn how to search HR and organization data Understand document and action authorization Monitor syncs and manage the source # Filesystem Source: https://docs.getomni.co/connectors/filesystem Index local files and directories for unified search across your file system The Filesystem connector enables Omni to index and search content from local directories, making your organization's file repositories searchable alongside other data sources. ## Overview ### What Gets Indexed | Content Type | Description | | -------------- | -------------------------------------------------------------------------- | | **Text files** | Full content extraction for text-based files (`.txt`, `.md`, `.csv`, etc.) | | **Documents** | Office documents (`.docx`), PDFs, and other structured text | | **Metadata** | File name, path, size, timestamps for all files | Source code files (`.py`, `.js`, `.ts`, `.rs`, `.go`, `.java`, etc.) and config files (`.json`, `.yaml`, `.toml`, `.xml`, `.html`, `.css`, etc.) are **excluded by default**. The filesystem connector is designed for indexing documents and knowledge content, not code repositories. ### Supported File Formats **Full content indexing** (text extracted and searchable): * Plain text files (`.txt`, `.md`, `.rst`, `.csv`) * Office documents (`.docx`) * Other text-based formats **Excluded by default:** * Source code files * Config and markup files **Metadata-only indexing** (searchable by name, path, date): * Binary files (images, etc.) * Files larger than 10MB ### How It Works 1. The connector scans configured directories recursively 2. Text files are read and content is extracted for full-text search 3. File metadata (name, path, size, timestamps) is indexed for all files 4. A file watcher detects changes in real-time between full scans The connector uses read-only access. Omni cannot modify or delete any files in your filesystem. ## Prerequisites Before setting up the Filesystem connector, ensure you have: * **Omni deployment** with the Filesystem connector service running * **Directory access** to the files you want to index * **Docker volume mounting** configured (for containerized deployments) ## Setup ### Step 1: Configure Docker Volume Mounts For Docker Compose deployments, add volume mounts to the Filesystem connector service in your `docker-compose.override.yml`: ```yaml theme={null} services: filesystem-connector: volumes: - /path/to/your/documents:/data/documents:ro - /path/to/another/folder:/data/shared:ro ``` Always use read-only mounts (`:ro`) for security. The connector only needs read access to index files. ### Step 2: Add Environment Variables Add the following to your `.env` file: ```bash theme={null} FILESYSTEM_CONNECTOR_PORT=4013 ``` ### Step 3: Connect in Omni 1. Navigate to **Settings** → **Integrations** in Omni 2. Find **Filesystem** and click **Connect** 3. Configure the source settings: | Setting | Required | Default | Description | | ----------------------- | -------- | ---------- | ------------------------------------------------------------------ | | `base_path` | Yes | - | Root directory to scan (inside container, e.g., `/data/documents`) | | `scan_interval_seconds` | No | `300` | Full scan interval (5 minutes) | | `file_extensions` | No | - | Whitelist of extensions (e.g., `["txt", "md", "json"]`) | | `exclude_patterns` | No | - | Patterns to exclude (e.g., `[".git", "node_modules"]`) | | `max_file_size_bytes` | No | `10485760` | Max file size for content extraction (10MB) | ### Step 4: Start Initial Sync 1. Click **Connect** to create the source 2. Click **Sync** to start the initial scan 3. Monitor progress in the admin panel Your Filesystem connector is now configured. The connector will scan and index files from the configured directory. ## Example Configurations ### Documentation Repository Index a documentation folder with Markdown and text files: ```json theme={null} { "base_path": "/data/documents", "scan_interval_seconds": 600, "file_extensions": ["md", "txt", "rst"], "exclude_patterns": [".git", "_build", "node_modules"] } ``` ### Shared File Server Index a shared network drive with all text content: ```json theme={null} { "base_path": "/data/shared", "scan_interval_seconds": 1800, "exclude_patterns": ["Thumbs.db", ".DS_Store", "~$*"], "max_file_size_bytes": 52428800 } ``` ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view the sync status for each source directly on the list, including last sync time, number of indexed items, and any errors. Click **Settings** on a source for more details and sync history. ### Sync Behavior The Filesystem connector uses two synchronization mechanisms: | Mechanism | Frequency | Description | | ---------------- | --------------------- | ---------------------------------- | | **Full Scan** | Every 5 min (default) | Walks entire directory tree | | **File Watcher** | Real-time | Detects file changes between scans | The file watcher polls for changes every 2 seconds and batches events with a 30-second idle timeout. ### Adding More Directories To index additional directories: 1. Add another volume mount in Docker Compose 2. Create a new Filesystem source in Omni with the new `base_path` ### Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the Filesystem source 3. Click **Delete Permanently** ## Troubleshooting Common causes: * Volume not mounted correctly in Docker * File extension not in whitelist * File matches an exclude pattern * File larger than max size (content-only issue) **Solution**: 1. Verify the volume mount: `docker exec omni-filesystem-connector ls /data/documents` 2. Check configuration for file\_extensions and exclude\_patterns 3. Trigger a manual sync The container doesn't have read access to the files. **Solution**: * Ensure files are readable by the container user * Check that SELinux/AppArmor isn't blocking access * Try mounting with `:ro,z` (for SELinux) or `:ro,Z` (for private mount) Files larger than 10MB (default) are indexed for metadata only. **Solution**: Increase `max_file_size_bytes` in the source configuration. Be aware this increases memory usage. Large directories with many files will take longer to scan. **Factors affecting scan time**: * Number of files in the directory tree * Average file size * Disk I/O speed **Optimization tips**: * Use `file_extensions` to limit which files are processed * Use `exclude_patterns` to skip unnecessary directories * Increase `scan_interval_seconds` for stable directories The file watcher may not detect all changes. **Solution**: The full scan (every 5 minutes by default) will catch any missed changes. You can also trigger a manual sync. By default, symlinks are not followed to prevent infinite loops. **Solution**: Currently, symlink following is disabled. Place actual files in the indexed directory. ## Security Considerations * **Read-only mounts**: Always use `:ro` flag for volume mounts * **File permissions**: The connector respects filesystem permissions * **No network access**: The connector only reads local files * **Container isolation**: Files are accessed through Docker volumes Ensure sensitive files (credentials, private keys, etc.) are excluded using `exclude_patterns` or not mounted at all. ## What's Next Learn how to search across your files Ask questions about your documents Connect additional data sources # Fireflies Source: https://docs.getomni.co/connectors/fireflies Connect Fireflies.ai to Omni for unified search across meeting transcripts, summaries, and action items The Fireflies connector enables Omni to index and search content from your Fireflies.ai account, including meeting transcripts, summaries, and action items. ## Overview ### What Gets Indexed | Source | Content | | ------------ | ---------------------------------------------------------- | | Transcripts | Full meeting transcripts with speaker names and timestamps | | Summaries | AI-generated overviews, outlines, and bullet points | | Action Items | Tasks and follow-ups extracted from meetings | | Metadata | Meeting title, date, duration, participants, and keywords | ### How It Works 1. You provide a Fireflies API key with read access 2. The connector fetches transcripts via the Fireflies GraphQL API 3. Transcripts are formatted with metadata, summaries, and full conversation text 4. Incremental sync fetches only meetings added since the last sync The connector uses read-only access. Omni cannot modify any content in your Fireflies account. ## Prerequisites Before setting up the Fireflies connector, ensure you have: * **Fireflies.ai account** with API access * **Omni deployment** with the Fireflies connector service running ## Setup ### Step 1: Generate a Fireflies API Key 1. Log in to [Fireflies.ai](https://app.fireflies.ai) 2. Go to **Settings** → **Developer Settings** 3. Generate a new **API key** 4. Copy the API key Keep your API key secure. Anyone with access to this key can read meeting transcripts from your Fireflies account. ### Step 2: Connect in Omni 1. Navigate to **Settings** → **Integrations** in Omni 2. Find **Fireflies** and click **Connect** 3. Enter your Fireflies API key 4. Click **Connect** 5. Click **Sync** to start the initial sync Your Fireflies connector is now configured. Initial indexing may take a while depending on the number of transcripts in your account. ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view the sync status for each source directly on the list, including last sync time, number of indexed items, and any errors. Click **Settings** on a source for more details and sync history. ### Sync Modes The Fireflies connector supports two sync modes: | Mode | Description | | --------------- | ------------------------------------------------------------------- | | **Full** | Syncs all available transcripts (used for initial sync) | | **Incremental** | Syncs only transcripts added since the last sync (used for updates) | After the initial full sync, the connector automatically performs incremental syncs to capture new transcripts. ### Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the Fireflies source 3. Click **Delete Permanently** ## Troubleshooting Common causes: * Sync has not completed yet * API key does not have access to the transcripts **Solution**: Verify your API key permissions and trigger a manual sync. The API key may be expired or revoked. **Solution**: 1. Go to your Fireflies developer settings 2. Generate a new API key 3. Delete the existing Fireflies source in Omni and reconnect it with the new key Fireflies has API rate limits. If you hit a rate limit, the sync will fail. **Solution**: Wait a few minutes and trigger a new sync. For accounts with many transcripts, the initial sync may need multiple attempts. ## Security Considerations * **Read-only access**: The connector only reads transcript data * **Token security**: API keys should be treated like passwords * **Encrypted storage**: Credentials are encrypted at rest in Omni ## What's Next Learn how to search across meeting transcripts Ask questions about your meetings Connect additional data sources # GitHub Source: https://docs.getomni.co/connectors/github Connect GitHub repositories to Omni for unified search across code, issues, pull requests, and discussions The GitHub connector enables Omni to index and search content from your GitHub organization, including repositories, issues, pull requests, and discussions. ## Overview ### What Gets Indexed | Source | Content | | ------------- | ---------------------------------------------------- | | Repositories | Repository names, descriptions, and README files | | Issues | Issue titles, descriptions, comments, and labels | | Pull Requests | PR titles, descriptions, review comments, and status | | Discussions | Discussion threads and replies | ### How It Works 1. A GitHub Personal Access Token (PAT) grants Omni read-only access to your repositories 2. The connector syncs content from repositories the authenticated user has access to 3. Permission inheritance ensures users only see content from repositories they can access in GitHub Indexing uses read-only access. If you later enable GitHub OAuth actions for the AI assistant, write-capable actions require a user's own authorization and are gated by the connector's action mode. ## Prerequisites Before setting up the GitHub connector, ensure you have: * **GitHub account** with access to the repositories you want to index * **GitHub organization admin access** (recommended for org-wide indexing) * **Omni deployment** with the GitHub connector service running ## Setup ### Step 1: Create a Personal Access Token 1. Go to **GitHub Settings** → **Developer settings** → **Personal access tokens** 2. Create either a classic token or a fine-grained token for the repositories you want Omni to index 3. For the simplest setup, grant the classic token the `repo` and `read:org` scopes. If you use a fine-grained token, grant read access for repository contents, issues, pull requests, discussions, metadata, and organization membership as needed. 4. Click **Generate token** 5. Copy the token immediately — you won't be able to see it again Store your Personal Access Token securely. Anyone with access to this token can read data from your GitHub repositories. ### Step 2: Connect in Omni 1. Navigate to **Settings** → **Integrations** in Omni 2. Find **GitHub** and click **Connect** 3. Paste your Personal Access Token into the token field 4. Optionally set: * **API URL** for GitHub Enterprise (leave blank for github.com) * **Include Discussions** to index repository discussions * **Include Forks** to include forked repositories * **Read-only mode** to expose only read operations to AI tools 5. Click **Connect** 6. Click **Sync** to start the initial sync Your GitHub connector is now configured. Initial indexing may take a while depending on the number of repositories and issues in your organization. ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view the sync status for each source directly on the list, including last sync time, number of indexed items, and any errors. Click **Settings** on a source for more details and sync history. ### Sync Modes The GitHub connector supports two sync modes: | Mode | Description | | --------------- | -------------------------------------------------------------------------- | | **Full** | Syncs all content from all accessible repositories (used for initial sync) | | **Incremental** | Syncs only new or updated content since last sync (used for updates) | After the initial full sync, the connector automatically performs incremental syncs to capture new activity. ### OAuth for AI Tools The Personal Access Token handles org-wide indexing. GitHub also declares an OAuth app manifest for connector actions that need a user's own authorization. Configure the GitHub client from **Settings** → **Integrations** → **OAuth Apps** using the shared redirect URI shown on that tab. When a tool needs user authorization, the chat UI prompts the user to connect GitHub before continuing. ### Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the GitHub source 3. Click **Delete Permanently** 4. Optionally, delete the Personal Access Token in your [GitHub settings](https://github.com/settings/tokens) ## Troubleshooting Common causes: * The authenticated user doesn't have access to the repository * Sync has not completed yet * The repository is archived and may need explicit inclusion **Solution**: Verify repository access and trigger a manual sync. The Personal Access Token may be expired or revoked. **Solution**: 1. Delete the existing GitHub source from **Settings** → **Integrations** → **GitHub** → **Delete Permanently** 2. Create a new Personal Access Token and reconnect GitHub from the Integrations page GitHub has API rate limits. The connector handles these automatically with backoff. **Solution**: Wait for the connector to retry. For large organizations, initial sync may take longer due to rate limits. Ensure the Personal Access Token has access to private repositories. **Solution**: Verify the token has `Contents` (read) permission and that the repository is included in the token's repository access scope. If needed, regenerate the token with the correct permissions and update it in Omni. Discussions must be enabled on the repository for them to be indexed. **Solution**: Verify that GitHub Discussions is enabled in the repository settings. ## Security Considerations * **Read-only access**: The connector only has read permissions * **Repository-based permissions**: Users only see content from repositories they can access in GitHub * **Token security**: Personal Access Tokens should be treated like passwords * **Audit logging**: Monitor token usage in GitHub's security log ## What's Next Learn how to search across GitHub content Ask questions about your code and issues Connect additional data sources # Google Ads Source: https://docs.getomni.co/connectors/google-ads Connect Google Ads to Omni for searchable account structure and live campaign analysis actions The Google Ads connector indexes account structure from one or more Google Ads customer accounts and exposes live reporting actions for the AI assistant. ## Overview ### What Gets Indexed | Entity | Content | | ------------------------------------ | -------------------------------------------------------------------------- | | Customers | Customer ID, descriptive name, currency, time zone, and status | | Campaign budgets | Budget names, amounts, delivery methods, and status | | Bidding strategies | Strategy names, types, status, and related metadata | | Campaigns | Campaign names, IDs, status, advertising channel, labels, and budget links | | Ad groups and ads | Ad group/ad IDs, names, status, campaign links, and creative metadata | | Assets and keywords | Asset IDs, keyword criteria, status, labels, and related entity metadata | | Shared sets, user lists, conversions | Audience, shared set, and conversion action metadata | Numeric performance metrics are fetched live by connector actions instead of being synced into the index. ### How It Works 1. You configure a Google Ads OAuth client in Omni's OAuth Apps tab 2. You provide a Google Ads developer token and one or more customer IDs 3. Omni sends you through Google's OAuth flow to authorize Ads API access 4. The connector syncs configured account entities and checkpoints progress per customer/entity pair 5. The AI assistant can run live GAQL and curated report actions when the source is allowed for the chat or agent The connector uses the Google Ads API. Your Google Ads developer token must have access to the customer accounts you configure. ## Prerequisites Before setting up the Google Ads connector, ensure you have: * **Google Ads API access** and a developer token from Google Ads API Center * **Customer IDs** for the accounts you want to index, without hyphens * Optional **manager/login customer ID** if you access client accounts through an MCC/manager account * A Google OAuth client configured in **Settings** → **Integrations** → **OAuth Apps** for the Google Ads provider * Omni deployment with the Google Ads connector service running (profile: `google_ads`) ## Setup ### Step 1: Configure the OAuth App 1. Navigate to **Settings** → **Integrations** → **OAuth Apps** 2. Configure the **Google Ads** OAuth client 3. Use the redirect URI shown in Omni when creating the OAuth client in Google Cloud 4. Ensure the OAuth client can request the Google Ads scope: `https://www.googleapis.com/auth/adwords` ### Step 2: Connect in Omni 1. Navigate to **Settings** → **Integrations** 2. Find **Google Ads** and click **Connect** 3. Enter: * **Source name**: Display name for the source * **Developer token**: Your Google Ads API developer token * **Customer IDs**: Comma- or space-separated customer IDs to index * **Manager/login customer ID**: Optional manager account ID used for API calls 4. Click **Continue with Google** and complete the OAuth authorization flow 5. Click **Sync** to start the initial sync Your Google Ads connector is now configured. Initial indexing time depends on the number of customer accounts and enabled entity types. ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view sync status, indexed counts, and errors. Click **Settings** on the source for sync history and source configuration. ### Sync Modes | Mode | Description | | --------------- | ------------------------------------------------------------------- | | **Full** | Syncs all configured Google Ads entity types for each customer ID | | **Incremental** | Uses change-status capable resources where available to reduce work | ### Search Operators The connector registers source-specific operators that work in Search and Chat once the source is running: | Operator | Meaning | | ------------- | -------------------------------------- | | `customer:` | Filter by Google Ads customer ID | | `campaign:` | Filter by campaign ID | | `ad_group:` | Filter by ad group ID | | `asset:` | Filter by asset ID | | `criterion:` | Filter by criterion ID | | `shared_set:` | Filter by shared set ID | | `status:` | Filter by entity status | | `channel:` | Filter by campaign advertising channel | | `entity:` | Filter by synced entity type | | `label:` | Filter by label | ### AI Assistant Actions When Google Ads actions are available to a chat or background agent, the assistant can: * Run live GAQL queries * Export GAQL results as CSV or XLSX * Fetch curated campaign, ad group, keyword, search term, creative, asset, landing page, budget pacing, conversion, segmented performance, change history, and policy diagnostics reports * Fetch account summaries, recommendations, account hierarchy, and recent changes Some actions return downloadable CSV/XLSX artifacts for deeper analysis. ## Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the Google Ads source 3. Click **Delete Permanently** 4. Optionally revoke the OAuth grant in your Google account or Google Workspace admin controls ## Troubleshooting Configure the Google Ads OAuth client from **Settings** → **Integrations** → **OAuth Apps** before connecting a source. The setup dialog will not proceed without it. Verify the developer token, customer IDs, login customer ID, and Google account authorization. The authorized Google account must have access to the configured customer accounts. Confirm the customer IDs are entered without hyphens and that the developer token has access to those accounts. For manager-account setups, set the login customer ID. Reconcile with the same customer, date range, filters, attribution settings, and conversion settings used in the Google Ads UI. ## Security Considerations * **Credential storage**: Developer tokens and OAuth tokens are encrypted at rest in Omni * **OAuth scope**: The connector uses the Google Ads API scope `https://www.googleapis.com/auth/adwords` * **Action access**: Live report actions only run when the source and action are available to the current chat or agent * **Token rotation**: Reauthorize the source if access is revoked or expires ## What's Next Learn how to search indexed Google Ads entities Use live report actions in chat Monitor syncs and manage the source # Google Workspace Source: https://docs.getomni.co/connectors/google-workspace Connect Google Drive, Docs, Sheets, Slides, Gmail, and Google Chat to Omni The Google Workspace connector enables Omni to index and search content from your organization's Google Workspace environment, including Drive, Docs, Sheets, Slides, optionally Gmail, and optionally Google Chat. ## Overview ### What Gets Indexed | Source | Content | | ---------------------- | ---------------------------------------------------------------------------- | | Google Drive | Files, folders, and metadata | | Google Docs | Full document text and comments | | Google Sheets | Sheet names, cell content, and formulas | | Google Slides | Slide text and speaker notes | | Gmail (optional) | Email subjects, bodies, and supported attachments | | Google Chat (optional) | Spaces, messages, threads, membership metadata, and linked Drive attachments | ### How It Works 1. Organization-wide sources use a service account with domain-wide delegation; a plain service account can sync selected shared drives directly (see [Service Account Direct (Shared Drives)](#service-account-direct-shared-drives-no-domain-wide-delegation)). Personal connections use the user's Google OAuth authorization. 2. The connector syncs content within the configured Drive scope and source permissions 3. Permission inheritance ensures users only see content they have access to in Google Workspace Google Workspace syncing uses read-only access. Some agent actions can request separate user OAuth authorization for write-capable Google APIs; see [User OAuth for Tools](#user-oauth-for-tools). ## Prerequisites Before setting up the Google Workspace connector, ensure you have: * **Google Cloud Platform account** with billing enabled * **Google Workspace admin access** (Super Admin or delegated admin) * **Google Cloud CLI** (`gcloud`) installed and authenticated (optional, for enabling APIs via CLI) ## Setup ### Step 1: Create a Google Cloud Project 1. Go to the [Google Cloud Console](https://console.cloud.google.com) 2. Click **Select a project** → **New Project** 3. Enter project name: `omni-workspace-integration` 4. Select your organization 5. Click **Create** ### Step 2: Enable Required APIs In your new project, enable these APIs: ```bash theme={null} gcloud services enable admin.googleapis.com gcloud services enable drive.googleapis.com gcloud services enable gmail.googleapis.com gcloud services enable docs.googleapis.com gcloud services enable sheets.googleapis.com gcloud services enable slides.googleapis.com gcloud services enable chat.googleapis.com ``` Or enable via Console: **APIs & Services** → **Library** → Search and enable each API. ### Step 3: Create Service Account 1. Go to **IAM & Admin** → **Service Accounts** 2. Click **Create Service Account** 3. Name: `omni-workspace-connector` 4. Description: `Service account for Omni Google Workspace integration` 5. Click **Create and Continue** 6. Skip the optional steps and click **Done** ### Step 4: Get the Client ID 1. Click on the newly created service account 2. Go to the **Details** tab 3. Under **Advanced settings**, copy the **Client ID** (also shown as the service account's Unique ID) — you'll need this in Step 6 to authorize domain-wide delegation ### Step 5: Create Service Account Key 1. Go to the **Keys** tab 2. Click **Add Key** → **Create new key** 3. Select **JSON** format 4. Click **Create** 5. Save the downloaded key file securely This key file provides access to your entire Google Workspace domain. Store it securely and never commit it to version control. ### Step 6: Configure Domain-Wide Delegation in Admin Console 1. Open the [Google Workspace Admin Console](https://admin.google.com) 2. Navigate to **Security** → **Access and data control** → **API controls** 3. Click **Manage Domain Wide Delegation** 4. Click **Add new** 5. Enter the **Client ID** from Step 4 6. Add the following OAuth scopes: ``` https://www.googleapis.com/auth/admin.directory.user.readonly, https://www.googleapis.com/auth/admin.directory.group.readonly, https://www.googleapis.com/auth/drive.readonly, https://www.googleapis.com/auth/gmail.readonly, https://www.googleapis.com/auth/documents.readonly, https://www.googleapis.com/auth/spreadsheets.readonly, https://www.googleapis.com/auth/presentations.readonly, https://www.googleapis.com/auth/chat.spaces.readonly, https://www.googleapis.com/auth/chat.memberships.readonly, https://www.googleapis.com/auth/chat.admin.memberships.readonly, https://www.googleapis.com/auth/chat.messages.readonly ``` 7. Click **Authorize** If Gmail or Google Chat access is not needed, exclude the corresponding Gmail or Chat scopes. Google Chat sync requires the Chat API to be enabled and domain-wide delegation for the Chat scopes above. ### Step 7: Configure Omni 1. Navigate to **Settings** → **Integrations** in Omni 2. Find **Google Workspace** and click **Connect** 3. Choose whether to create **Google Drive**, **Gmail**, **Google Chat**, or any combination of those sources 4. Paste the contents of the service account key file from Step 5 5. Enter your Google Workspace domain 6. Enter the admin email address 7. Click **Connect** 8. Use **Settings** on each created source to adjust source settings, then click **Sync** to start the initial sync Your Google Workspace connector is now configured. Initial indexing may take a while, depending on the amount of content in your Google account. ## Personal Google OAuth After an admin configures the Google OAuth client under **Settings** → **Integrations** → **OAuth Apps**, users can connect their own Google account from **My Integrations** → **Connect your Google account**. OAuth can create personal **Google Drive** and **Gmail** sources using read-only Google scopes; it does not require domain-wide delegation or Google Workspace admin scopes. A personal Drive source starts in a pending state until its owner chooses either **Entire Drive** or **Selected folders**. Folder discovery can include the user's My Drive, accessible shared drives, and folders beneath them. Sync runs with that user's credential and indexes only content the account can access. *** ## Service Account Direct (Shared Drives, no Domain-Wide Delegation) The setup dialog has two tabs: **Domain-wide delegation** (the flow above) and **Shared drive (no DWD)**. The second tab is Drive-only and uses a plain service account to sync selected shared drives with their effective permissions. It needs no domain-wide delegation or `sub` impersonation, but it does require the Admin Directory group-read scope for group membership sync. ### Google-Side Setup 1. Create a service account key (**IAM & Admin** → **Service Accounts** → **Keys**) 2. Ensure the service account itself can list Workspace groups and group members through the Admin SDK without impersonation, for example through an appropriate customer-level admin role. Omni validates this access before creating the source 3. In each shared drive → **Manage members**, add the service account email (`@.iam.gserviceaccount.com`) as **Content manager** (`fileOrganizer`) or **Manager** (`organizer`). Without one of these roles the service account cannot read the drive's ACLs, and the connector will not sync the drive 4. Verify your Workspace external-sharing policy permits adding `*.iam.gserviceaccount.com` to shared drives. The service account is an external principal, so a scoped OU / trust-rule exception may be needed. If the policy cannot allow the service account at all, use OAuth or domain-wide delegation instead ### Behavior * `drive.readonly` and `admin.directory.group.readonly` are required; the source config selects this mode with `auth_mode: "service_account_direct"`, a Workspace `domain`, and one or more `folder_path_filters` entries of kind `shared_drive_root` * The drive member list supplies each document's baseline `public`/`users`/`groups` permissions; the service account's own email is excluded * Effective permissions mirror Google's shared-drive model: direct grants on ancestor folders and files are added, and a limited-access folder boundary replaces inherited access with drive organizers plus direct grants at that boundary * Drive and relevant folder ACLs are fingerprinted; a change triggers a full drive re-traversal so existing documents get the new permissions * Polling only; no webhook registration. Google Groups on the drive map to `groups`. Setup verifies that the service account can list Workspace groups and members, and the connector emits group-membership events during sync. If group sync later fails, group-granted documents are visible only to users whose memberships are supplied by another source * Switching an existing domain-wide-delegation source to service-account-direct is not supported; create a new source instead The setup dialog validates Workspace group/member access and the service account's role on every selected drive before the source is created, and the drive settings page repeats the shared-drive access validation before Save. ## Managing the Integration ### Gmail Attachments When Gmail is enabled, Omni indexes supported message attachments in addition to message bodies. Supported attachment formats include PDFs, DOCX, XLSX, PPTX, XLS, text, HTML, CSV, and Markdown. Unsupported binary formats are skipped. ### Google Chat When Google Chat is enabled, Omni indexes accessible spaces and messages as a separate **Google Chat** source. Chat messages retain space/thread context and permission metadata from Chat memberships. Linked Drive attachments are indexed through the same Drive access path when available. ### Drive Folder Filtering Drive indexing scope depends on how the source is authenticated: * **Domain-wide delegation**: Admins can select shared drives or folders within them. If no folders are selected, all content available through the source is indexed. Personal My Drive folder scopes are not supported for service-account sources; use personal OAuth instead. * **Personal Google OAuth**: The account owner can choose **Entire Drive** or selected folders from My Drive and accessible shared drives. The owner manages this scope from **My Integrations**. * **Service-account direct**: The source must select at least one whole shared drive. Folder-level restrictions are not supported in this mode. For existing organization sources, navigate to **Settings** → **Integrations**, find the Drive source, and click **Settings**. For personal sources, open **My Integrations** and choose **Manage folders** or **Choose folders** on the Drive connection. When a scope is selected, the connector enforces it during full and incremental syncs using ancestry checks. Changing a scope starts a full sync so newly selected content is discovered; files removed from a selection may remain searchable until cleanup completes. Deselecting folders in a mode that permits an empty selection restores index-all behavior. ### User OAuth for Tools The service account handles org-wide indexing. Some Omni agent tools require a user's own Google authorization instead of the service account. Configure the Google client from **Settings** → **Integrations** → **OAuth Apps**, then use the shared redirect URI shown there when creating the OAuth client in Google Cloud. When a tool needs user authorization, the chat UI prompts the user to connect their Google account. ### AI Tools and Skills The Google connector exposes the entire Google Workspace API surface as MCP tools for Omni through two actions: * **`google_workspace_schema`** — fetches API method schemas so the assistant can discover available operations and their parameters * **`google_workspace_call`** — executes API calls against Drive, Docs, Sheets, Slides, and Gmail The connector also ships domain-aware skills that guide the assistant through common Workspace tasks: * **Google Drive skill** — search and read files, manage permissions, create and update Docs/Sheets/Slides via the API * **Gmail skill** — search, read, and manage messages and threads with Gmail query syntax These skills are automatically loaded into Omni when the connector is enabled. They ensure the agent follows best practices — searching before reading, using stable IDs over fragile names, and choosing the right API surface for each task. ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view the sync status for each source directly on the list, including last sync time, number of indexed items, and any errors. Click **Settings** on a source for more details and sync history. ### Rotating Service Account Keys It's recommended to rotate service account keys every 90 days. 1. Create a new key in Google Cloud Console 2. Update the key in Omni settings 3. Delete the old key ### Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the Google Workspace source 3. Click **Delete Permanently** 4. Optionally, delete the service account in Google Cloud and remove the domain-wide delegation entry in Admin Console *** ## Troubleshooting The setup requires: * **Project Creator** - to create the GCP project * **Google Workspace Super Admin** - for domain-wide delegation Some permissions may take 10-15 minutes to propagate. APIs may take a few minutes to propagate after enabling. Check status: ```bash theme={null} gcloud services list --enabled --project=YOUR_PROJECT_ID ``` Common causes: * Client ID mismatch - verify the ID in Admin Console matches the service account * Missing scopes - ensure all required scopes are added * Propagation delay - wait 5-10 minutes for changes to take effect * Wrong admin email - ensure the admin email has Google Workspace admin privileges Initial sync duration depends on: * Number of users in your organization * Amount of content in Drive and Gmail * API quota limits For large organizations (1000+ users), initial sync may take 24-48 hours. You can monitor progress in the Omni admin panel. ## Security Considerations * **Read-only access**: The service account only has read permissions * **Permission inheritance**: Users only see content they can access in Google Workspace * **Key security**: Service account keys should be treated like passwords * **Audit logging**: Enable Cloud Audit Logs to monitor API access * **Key rotation**: Rotate service account keys every 90 days ## What's Next Learn how to search across Google Workspace content Ask questions about your documents and emails Connect additional data sources # HubSpot Source: https://docs.getomni.co/connectors/hubspot Connect HubSpot CRM to Omni for unified search across contacts, companies, deals, and activities The HubSpot connector enables Omni to index and search content from your HubSpot CRM, including contacts, companies, deals, tickets, and engagement activities. ## Overview ### What Gets Indexed | Object Type | Content | | ------------- | ------------------------------------------------------- | | **Contacts** | Name, email, phone, company, job title, lifecycle stage | | **Companies** | Name, domain, industry, employee count, annual revenue | | **Deals** | Deal name, amount, pipeline, stage, close date | | **Tickets** | Subject, content, priority, pipeline, stage | | **Calls** | Call title, body, duration, direction | | **Emails** | Subject, text/HTML content, direction | | **Meetings** | Title, body, start/end times | | **Notes** | Note content and timestamps | | **Tasks** | Subject, body, status, priority | ### How It Works 1. A HubSpot app access token provides read-only API access 2. The connector syncs CRM objects and engagement activities 3. Full sync mode indexes all accessible records The connector uses read-only access. Omni cannot create, modify, or delete any records in your HubSpot account. ## Prerequisites Before setting up the HubSpot connector, ensure you have: * **HubSpot account** with API access (Professional or Enterprise plan recommended) * A [**HubSpot developer account**](https://developers.hubspot.com) to create and install an app ## Setup ### Step 1: Create a HubSpot App Create an app on HubSpot's developer platform. See [HubSpot's app creation guide](https://developers.hubspot.com/docs/apps/developer-platform/build-apps/overview) for full instructions. 1. Install the HubSpot CLI and run `hs project create` 2. Choose **App** as the project type 3. Select **static token** authentication (for single-account access) ### Step 2: Configure Scopes Add the following scopes to your app's `app-hsmeta.json` under `requiredScopes`: | Scope | Purpose | | ---------------------------- | ------------------------------------------------------------------------------- | | `crm.objects.contacts.read` | Read contacts and engagement activities (calls, emails, meetings, notes, tasks) | | `crm.objects.companies.read` | Read company records | | `crm.objects.deals.read` | Read deal records | | `tickets` | Read ticket records | ### Step 3: Install and Get Your Access Token 1. Upload your app with `hs project upload` 2. In HubSpot, go to the project's **Distribution** tab and install the app on your account 3. Copy the **access token** from the app settings Keep your access token secure. Anyone with access to this token can read your HubSpot CRM data. You can rotate tokens at any time from your app settings. ### Step 4: Connect in Omni 1. Navigate to **Settings** → **Integrations** in Omni 2. Find **HubSpot** and click **Connect** 3. Enter your access token 4. Click **Connect** 5. Click **Sync** to start the initial sync. Your HubSpot connector is now configured. Initial indexing depends on the number of records in your CRM. If you have an existing [legacy Private App](https://developers.hubspot.com/docs/apps/legacy-apps/private-apps/overview), its access token will also work. However, HubSpot recommends the new developer platform for new integrations. To create a legacy Private App: 1. Go to **Settings** → **Integrations** → **Private Apps** 2. Click **Create a private app** and configure the same scopes listed above 3. Copy the access token (starts with `pat-na1-`) and use it in the Omni connection step ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view the sync status for each source directly on the list, including last sync time, number of indexed items, and any errors. Click **Settings** on a source for more details and sync history. ### Sync Behavior The HubSpot connector currently supports **full sync** mode: * Syncs all accessible CRM objects and engagements * Processes records in batches of 100 (HubSpot API limit) * Handles rate limiting automatically with exponential backoff ### Record Properties The connector fetches specific properties for each object type. All object types include `hubspot_owner_id` for ownership tracking. **CRM Objects** | Object Type | Properties | | ----------- | --------------------------------------------------------------------------------------- | | Contacts | `firstname`, `lastname`, `email`, `phone`, `company`, `jobtitle`, `lifecyclestage` | | Companies | `name`, `domain`, `industry`, `phone`, `numberofemployees`, `annualrevenue` | | Deals | `dealname`, `amount`, `pipeline`, `dealstage`, `closedate`, `hs_deal_stage_probability` | | Tickets | `subject`, `content`, `hs_ticket_priority`, `hs_pipeline`, `hs_pipeline_stage` | All CRM objects also include: `hubspot_owner_id`, `createdate`, `hs_lastmodifieddate` **Engagement Activities** | Object Type | Properties | | ----------- | ------------------------------------------------------------------------------------- | | Calls | `hs_call_title`, `hs_call_body`, `hs_call_duration`, `hs_call_direction` | | Emails | `hs_email_subject`, `hs_email_text`, `hs_email_html`, `hs_email_direction` | | Meetings | `hs_meeting_title`, `hs_meeting_body`, `hs_meeting_start_time`, `hs_meeting_end_time` | | Notes | `hs_note_body` | | Tasks | `hs_task_subject`, `hs_task_body`, `hs_task_status`, `hs_task_priority` | All engagement activities also include: `hubspot_owner_id`, `hs_timestamp`, `hs_createdate`, `hs_lastmodifieddate` ### Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the HubSpot source 3. Click **Delete Permanently** 4. Optionally, uninstall the app in HubSpot via your project's **Distribution** tab ## Security Considerations * **Read-only access**: The access token only has read permissions * **Token security**: Access tokens should be treated like passwords * **Token rotation**: Rotate tokens periodically for security from your app settings * **Data visibility**: All users can search indexed HubSpot records (no per-user permissions) HubSpot data indexed by Omni is visible to all Omni users. Consider this when connecting accounts with sensitive CRM data. ## What's Next Learn how to search across HubSpot records Ask questions about your CRM data Connect additional data sources # IMAP Email Source: https://docs.getomni.co/connectors/imap Connect email accounts via IMAP for unified search across messages and attachments The IMAP connector enables Omni to index and search content from any IMAP-compatible email account, including messages, attachments, and threaded conversations. ## Overview ### What Gets Indexed | Source | Content | | ----------- | ------------------------------------------------- | | Emails | Subject, body, sender, recipients, and dates | | Attachments | Text-based file attachments | | Threads | Grouped email conversations via message threading | ### How It Works 1. You provide IMAP server credentials (host, port, username, password) 2. The connector syncs emails from configured folders 3. Folder allow/deny lists control which mailbox folders are indexed 4. Email threads are grouped for efficient search The connector uses read-only access — it only reads email via IMAP. Omni cannot send, modify, or delete any emails. This is a general-purpose email connector that works with any IMAP-compatible provider (Gmail, Outlook, Yahoo, self-hosted mail servers, etc.). ## Prerequisites Before setting up the IMAP connector, ensure you have: * **IMAP server access credentials** (username and password) * **For Gmail/Outlook**: an app-specific password is recommended * **Omni deployment** with the IMAP connector service running ## Setup ### Step 1: Gather IMAP Server Details Common IMAP settings for popular providers: | Provider | Host | Port | Encryption | | ------------------ | ----------------------- | ---- | ---------- | | Gmail | `imap.gmail.com` | 993 | TLS | | Outlook/Office 365 | `outlook.office365.com` | 993 | TLS | | Yahoo | `imap.mail.yahoo.com` | 993 | TLS | For Gmail, you'll need to create an App Password at [https://myaccount.google.com/apppasswords](https://myaccount.google.com/apppasswords). ### Step 2: Connect in Omni 1. Navigate to **Settings** → **Integrations** in Omni 2. Find **IMAP Email** and click **Connect** 3. Enter connection details: * **Display name** (optional): A label for this account * **Host**: IMAP server hostname * **Port**: Server port (default 993 for TLS) * **Encryption**: TLS (recommended), STARTTLS, or None * **Username**: Your email address * **Password**: Your password or app-specific password 4. Optionally configure advanced settings: * **Folder allowlist**: Only sync specific folders (comma-separated) * **Folder denylist**: Skip specific folders (comma-separated) * **Max message size**: Limit for individual messages (0 = unlimited) * **Webmail URL template**: Template for linking back to the source in webmail, with `{folder}`, `{uid}`, and `{message_id}` placeholders (e.g. `https://mail.example.com/?folder={folder}&msg={uid}`) 5. Click **Connect** 6. Click **Sync** to start the initial sync Using "None" for encryption sends credentials in plaintext. Only use this for trusted local networks. Your IMAP connector is now configured. Initial indexing may take a while depending on the number of emails in your mailbox. ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view the sync status for each source directly on the list, including last sync time, number of indexed items, and any errors. Click **Settings** on a source for more details and sync history. ### Sync Modes The IMAP connector supports two sync modes: | Mode | Description | | --------------- | ---------------------------------------------------------------- | | **Full** | Syncs all emails from configured folders (used for initial sync) | | **Incremental** | Syncs only new emails since last sync (used for updates) | After the initial full sync, the connector automatically performs incremental syncs to capture new emails. ### Default Excluded Folders By default, the following folders are excluded from indexing: **Trash**, **Spam**, **Junk**, **Junk Email**, **Deleted Items**, **Deleted Messages**, **\[Gmail]/Trash**, and **\[Gmail]/Spam**. You can override this behavior by configuring the folder denylist in the connector settings. ### Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the IMAP source 3. Click **Delete Permanently** ## Troubleshooting Common causes: * Folder allow/deny lists may be excluding the target folder * Sync has not completed yet **Solution**: Check your folder allow/deny list configuration and verify the sync has completed in **Settings** → **Integrations** → **IMAP Email**. The credentials may be incorrect, or your provider may require an app-specific password. **Solution**: 1. Verify your username and password are correct 2. For Gmail, create an App Password at [https://myaccount.google.com/apppasswords](https://myaccount.google.com/apppasswords) 3. For Outlook, check if your organization requires app-specific passwords 4. Delete the existing IMAP source in Omni and reconnect it with the new credentials The server may be unreachable or the connection settings may be incorrect. **Solution**: Verify the host, port, and encryption settings match your provider. Refer to the common IMAP settings table in Step 1. Mailboxes with many emails can take a long time to sync initially. **Solution**: Consider using the folder allowlist to limit the scope of the sync, or set a max message size to skip very large emails. ## Security Considerations * **Read-only access**: The connector only reads email via IMAP * **TLS encryption**: Always use TLS or STARTTLS for production deployments * **App passwords**: Use app-specific passwords instead of your main account password * **Encrypted storage**: Credentials are encrypted at rest in Omni ## What's Next Learn how to search across your emails Ask questions about your email conversations Connect additional data sources # Linear Source: https://docs.getomni.co/connectors/linear Connect Linear to Omni for unified search across issues, projects, and documents The Linear connector enables Omni to index and search content from your Linear workspace, including issues, projects, and documents. ## Overview ### What Gets Indexed | Source | Content | | --------- | -------------------------------------------------------------------------- | | Issues | Issue title, description, status, priority, assignee, labels, and comments | | Projects | Project name and description, plus separately indexed project updates | | Documents | Document title and content | ### How It Works 1. You create a Linear API key with read access 2. The connector syncs issues, projects, and documents from your workspace 3. Optionally filter issue fetching by specific teams using team keys 4. Linear permission metadata is preserved where available; projects and documents without retained team keys are indexed as public The connector uses read-only access. Omni cannot create, modify, or delete any content in your Linear workspace. ## Prerequisites Before setting up the Linear connector, ensure you have: * **Linear account** with permission to create API keys * **Omni deployment** with the Linear connector service running ## Setup ### Step 1: Create a Linear API Key 1. Go to [Linear Settings](https://linear.app/settings/api) 2. Click **Create key** under Personal API keys 3. Give the key a label (e.g., "Omni Search") 4. Copy the generated API key Keep your API key secure. Anyone with access to this key can read content from your Linear workspace. ### Step 2: Connect in Omni 1. Navigate to **Settings** → **Integrations** in Omni 2. Find **Linear** and click **Connect** 3. Enter your API key 4. Optionally enter team keys to limit issue syncing to specific teams (comma-separated) 5. Click **Connect** 6. Click **Sync** to start the initial sync Your Linear connector is now configured. Initial indexing may take a while depending on the amount of content in your workspace. ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view the sync status for each source directly on the list, including last sync time, number of indexed items, and any errors. Click **Settings** on a source for more details and sync history. ### Sync Modes The Linear connector supports two sync modes: | Mode | Description | | --------------- | ----------------------------------------------------------------- | | **Full** | Syncs all issues, projects, and documents (used for initial sync) | | **Incremental** | Syncs only items modified since last sync (used for updates) | After the initial full sync, the connector automatically performs incremental syncs to capture new and updated content. ### Team Filtering To limit issue syncing to specific teams, provide team keys during setup. Team keys are the short identifiers visible in your Linear URLs (e.g., the "ENG" in ENG-123). Projects and documents are fetched workspace-wide; items without a retained team key are indexed as public. ### Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the Linear source 3. Click **Delete Permanently** 4. Optionally, revoke the API key from your Linear settings ## Troubleshooting Common causes: * Team filtering is excluding the relevant team * Sync has not completed yet * The item was not returned by the connector's issue filter **Solution**: Check your team filtering configuration and verify that the sync has completed successfully. The token may be expired or revoked. **Solution**: 1. Go to [Linear Settings](https://linear.app/settings/api) 2. Regenerate your API key 3. Delete the existing Linear source in Omni and reconnect it with the new key Linear has API rate limits. The connector handles these automatically with backoff. **Solution**: Wait for the connector to retry. For large workspaces, initial sync may take longer due to rate limits. ## Security Considerations * **Read-only access**: The API key only has read permissions * **Team filtering**: Team keys limit issue fetching; they do not limit workspace-wide project and document fetching * **Token security**: API keys should be treated like passwords * **Encrypted storage**: Credentials are encrypted at rest in Omni ## What's Next Learn how to search across Linear issues and documents Ask questions about your Linear projects and issues Connect additional data sources # Microsoft 365 Source: https://docs.getomni.co/connectors/microsoft365 Connect Microsoft 365 to Omni for unified search across OneDrive, SharePoint, Outlook, Calendar, and Teams The Microsoft 365 connector enables Omni to index and search content from your Microsoft 365 tenant, including OneDrive files, SharePoint documents, Outlook emails, Calendar events, and Teams chats. ## Overview ### What Gets Indexed | Source | Content | | ---------------- | ----------------------------------------------------------------------------- | | OneDrive | Files and documents from user drives | | SharePoint | Documents from site libraries | | Outlook Mail | Email subjects, bodies, senders, and recipients | | Outlook Calendar | Calendar events with titles, descriptions, and attendees | | Teams | Channel messages plus 1:1, group, and meeting chats, with attachment metadata | ### How It Works 1. You register an Entra ID application with the required Microsoft Graph API permissions 2. The connector syncs content from all five services in a single setup 3. Group membership-based permissions ensure users only see content they have access to 4. Supports both full and incremental sync modes, with state checkpointing so large syncs can resume 5. The same app registration can also support per-user OAuth for AI tool calls that require delegated Microsoft access The connector uses read-only access. Omni cannot modify, delete, or create any content in your Microsoft 365 tenant. ## Prerequisites Before setting up the Microsoft 365 connector, ensure you have: * **Entra ID admin access** to register applications and grant admin consent * **Microsoft 365 tenant** with the services you want to index * **Omni deployment** with the Microsoft connector service running ## Setup ### Step 1: Register an Entra ID Application 1. Go to the [Azure Portal](https://portal.azure.com) 2. Navigate to **Microsoft Entra ID** → **App registrations** 3. Click **New registration** 4. Set the name: **Omni MS365 Connector** 5. Supported account types: Single-tenant (this directory only) 6. Leave **Redirect URI** blank 7. Click **Register** 8. Note the **Application (client) ID** and **Directory (tenant) ID** ### Step 2: Configure API Permissions 1. Go to **API permissions** in your app registration 2. Click **Add a permission** → **Microsoft Graph** 3. Add the following **Application permissions** for org-wide sync: | Permission | Type | Purpose | | ------------------------- | ----------- | --------------------------------------------------- | | `Files.Read.All` | Application | Read files in OneDrive and SharePoint | | `Sites.Read.All` | Application | Read items in all SharePoint site collections | | `Mail.Read` | Application | Read mail in all mailboxes | | `Calendars.Read` | Application | Read calendars in all mailboxes | | `Chat.Read.All` | Application | Read all Teams 1:1 and group chat messages | | `ChatMessage.Read.All` | Application | Read all chat messages | | `Team.ReadBasic.All` | Application | List all teams | | `Channel.ReadBasic.All` | Application | Read channel names and descriptions | | `ChannelMember.Read.All` | Application | Read members of all channels | | `ChannelMessage.Read.All` | Application | Read all channel messages | | `User.Read.All` | Application | Read user profiles (for permission mapping) | | `Group.Read.All` | Application | Read groups (for permission resolution) | | `GroupMember.Read.All` | Application | Read group memberships (for permission inheritance) | 4. Add delegated permissions for user-authorized tool calls. At minimum, include `User.Read`; add any additional delegated scopes your enabled Microsoft tools require. 5. Click **Grant admin consent** and confirm Admin consent is required. Application permissions apply across the entire tenant; delegated permissions are used only when an individual user connects their Microsoft account for tools. ### Step 3: Create a Client Secret 1. Go to **Certificates & secrets** 2. Click **New client secret** 3. Set an expiry period and click **Add** 4. Copy the secret value immediately — it won't be shown again Store the client secret securely. Rotate it before expiry to avoid service disruption. ### Step 4: Connect in Omni 1. Navigate to **Settings** → **Integrations** in Omni 2. Find **Microsoft 365** and click **Connect** 3. Enter your **Tenant ID**, **Client ID**, and **Client Secret** 4. Click **Connect** 5. Click **Sync** on each created Microsoft source to start the initial sync All five source types (OneDrive, SharePoint, Outlook Mail, Calendar, Teams Chats) are created automatically in a single setup flow. Omni also saves tenant-specific OAuth endpoints from this app registration so users can authorize delegated Microsoft tool access when needed. Your Microsoft 365 connector is now configured. Initial indexing may take a while depending on the amount of content in your tenant. ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view: * Last sync time for each Microsoft source type * Number of indexed items per source * Any sync errors * Detailed sync history from each source's **Settings** page ### Sync Modes The Microsoft 365 connector supports two sync modes: | Mode | Description | | --------------- | ------------------------------------------------------------------------------------------------------ | | **Full** | Syncs all content across OneDrive, SharePoint, Mail, Calendar, and Teams Chats (used for initial sync) | | **Incremental** | Syncs only items modified since last sync (used for updates) | After the initial full sync, the connector automatically performs incremental syncs to capture new and updated content. ### Per-User Tool Authorization Org-wide sync uses application credentials. Interactive AI tool calls run as the current user when delegated Microsoft access is required. If a user has not connected their Microsoft account, Omni returns a user-auth prompt instead of falling back to org credentials. ### Removing the Integration 1. Navigate to **Settings** → **Integrations** and click **Settings** against the source you wish to remove 2. Click **Delete Permanently** 3. Optionally, delete the app registration from Entra ID ## Troubleshooting Common causes: * Admin consent was not granted for all required permissions * Sync has not completed yet for the relevant source type **Solution**: Verify admin consent was granted in Entra ID, then check the sync status for each source type in **Settings** → **Integrations** → **Microsoft 365**. The credentials may be incorrect or the client secret may have expired. **Solution**: 1. Verify the **Tenant ID**, **Client ID**, and **Client Secret** are correct 2. Check if the client secret has expired in Entra ID 3. If expired, create a new client secret, delete the existing Microsoft 365 sources in Omni, and reconnect Microsoft 365 with the new secret API permissions may be missing or admin consent was not granted. **Solution**: Go to your app registration in Entra ID, verify all required API permissions are added, and ensure **Grant admin consent** has been clicked and confirmed for each permission. The connector uses application permissions. If a user's content is not appearing, the issue may be related to group membership sync. **Solution**: Check group membership sync status and verify the user exists in the tenant. Ensure `User.Read.All` and `GroupMember.Read.All` permissions are granted. ## Security Considerations * **Read-only access**: The connector only has read permissions * **Application permissions**: Uses app-level access (not delegated), requiring admin consent * **Group-based permissions**: Users only see content based on their Microsoft 365 group memberships * **Secret rotation**: Monitor client secret expiry and rotate before it expires * **Encrypted storage**: Credentials are encrypted at rest in Omni ## What's Next Learn how to search across Microsoft 365 content Ask questions about your documents and emails Connect additional data sources # Nextcloud Source: https://docs.getomni.co/connectors/nextcloud Connect a Nextcloud instance to Omni for unified search across files and documents The Nextcloud connector enables Omni to index and search files stored in a Nextcloud instance via WebDAV. It's designed for self-hosted and managed Nextcloud deployments alike. ## Overview ### What Gets Indexed | Source | Content | | -------- | ------------------------------------------------------------------------------- | | Files | Text, documents, and other extractable file content from configured directories | | Metadata | File name, path, size, and modification timestamps | ### How It Works 1. You provide a Nextcloud server URL, username, and app password 2. The connector walks the configured directory tree via WebDAV `PROPFIND` 3. Full and incremental syncs use ETag-based change detection 4. Removed files are detected automatically on each sync 5. Extension allow/deny lists and a max file size filter control what gets indexed The connector uses read-only WebDAV access — it cannot modify or delete any files in your Nextcloud instance. ## Prerequisites Before setting up the Nextcloud connector, ensure you have: * **Nextcloud instance** reachable from the Omni network * **A Nextcloud user account** with read access to the directories you want to index * **Omni deployment** with the Nextcloud connector service running (profile: `nextcloud`) ## Setup ### Step 1: Create an App Password 1. Sign in to Nextcloud as the user Omni will connect as 2. Go to **Settings** → **Security** → **Devices & sessions** 3. Enter a name (e.g. `omni`) and click **Create new app password** 4. Copy the generated password — it will not be shown again Create a dedicated service user with read-only access to the directories you want indexed. This keeps Omni's access scoped and easy to revoke. ### Step 2: Connect in Omni 1. Navigate to **Settings** → **Integrations** in Omni 2. Find **Nextcloud** and click **Connect** 3. Enter the connection details: * **Server URL**: Your Nextcloud instance (e.g. `https://cloud.example.com`) * **Username**: The Nextcloud username * **App Password**: The app password from Step 1 4. Optionally configure advanced settings: | Setting | Default | Description | | --------------------- | --------------- | ------------------------------------------------- | | `base_path` | `/` | Subdirectory to sync (e.g. `/Shared/Engineering`) | | `extension_allowlist` | all | Only sync files with these extensions | | `extension_denylist` | none | Skip files with these extensions | | `max_file_size` | `0` (unlimited) | Maximum file size in bytes | | `sync_enabled` | `true` | Enable scheduled sync | 5. Click **Connect** 6. Click **Sync** to start the initial sync Your Nextcloud connector is now configured. Initial indexing may take a while depending on the size of the directory tree. ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view last sync time, the number of indexed items, and any errors for each Nextcloud source. Click **Settings** for details and sync history. ### Sync Modes | Mode | Description | | --------------- | ----------------------------------------------------------------------- | | **Full** | Walks the entire configured directory tree (used for initial sync) | | **Incremental** | Uses ETag change detection to fetch only modified files since last sync | ### Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the Nextcloud source 3. Click **Delete Permanently** ## Troubleshooting The username or app password is incorrect, or the user lacks access to `base_path`. **Solution**: Verify the credentials in Nextcloud and confirm the user can browse `base_path` via the web UI. Regenerate the app password if needed. Common causes: * Files outside `base_path` * Extension filtered out by allowlist/denylist * File larger than `max_file_size` **Solution**: Check the source configuration in **Settings** → **Integrations** → **Nextcloud** and trigger a manual sync. Nextcloud servers sometimes refuse `Depth: infinity` listings. The connector falls back to breadth-first `Depth: 1` traversal, which makes one request per directory. **Solution**: Narrow `base_path` to a specific subdirectory, or restrict extensions with `extension_allowlist`. ## Security Considerations * **Read-only access**: The connector only issues WebDAV `PROPFIND` and `GET` requests * **App passwords**: Use an app password, not the user's main password — it can be revoked independently * **Encrypted storage**: Credentials are encrypted at rest in Omni ## What's Next Learn how to search across your files Ask questions about your documents Connect additional data sources # Notion Source: https://docs.getomni.co/connectors/notion Connect Notion workspaces to Omni for unified search across pages, databases, and content blocks The Notion connector enables Omni to index and search content from your Notion workspace, including data sources, pages, database entries, properties, and nested content blocks. ## Overview ### What Gets Indexed | Source | Content | | ------------------- | --------------------------------------------------------------------------------------------------- | | Data sources | Data source title, description, and property schema definitions | | Data source entries | Page content, properties (title, select, date, people, relation, rollup, etc.), and nested blocks | | Standalone Pages | Full page content including all nested blocks | | Block Types | Paragraphs, headings, lists, code blocks, tables, toggles, bookmarks, equations, callouts, and more | ### How It Works 1. You create a Notion internal integration and share content with it 2. The connector syncs all shared data sources and pages via the Notion API 3. Page content is rendered from blocks — nested children are fully traversed and included 4. Data source entries include rendered property values in the indexed content 5. Incremental sync fetches pages and data sources modified since the last sync The connector uses read-only access. Omni cannot modify any content in your Notion workspace. ## Prerequisites Before setting up the Notion connector, ensure you have: * **Notion account** with permission to create integrations * **Omni deployment** with the Notion connector service running ## Setup ### Step 1: Create a Notion Internal Integration 1. Go to [My Integrations](https://www.notion.so/my-integrations) 2. Click **New integration** 3. Select the workspace you want to connect 4. Give the integration a name (e.g., "Omni Search") 5. Under **Capabilities**, ensure these are selected: * **Read content** * **Read user information with email addresses** 6. Click **Save** 7. Copy the **Internal Integration Secret** (starts with `ntn_`) Keep your integration secret secure. Anyone with access to this token can read content shared with the integration. ### Step 2: Share Content with the Integration Notion integrations can only access pages, databases, and data sources that have been explicitly shared with them. This is a critical step. **To share a page, database, or data source:** 1. Open the page or database in Notion 2. Click the **...** menu in the top-right corner 3. Scroll to **Connections** and click **Connect to** 4. Find and select your integration (e.g., "Omni Search") Share top-level pages to grant access to all their child pages automatically. For example, sharing a team wiki's root page will make all nested pages accessible to the connector. Share databases/data sources directly when you want entries and property schema to be indexed. ### Step 3: Connect in Omni 1. Navigate to **Settings** → **Integrations** in Omni 2. Find **Notion** and click **Connect** 3. Enter your Internal Integration Secret 4. Click **Connect** 5. Click **Sync** to start the initial sync Your Notion connector is now configured. Initial indexing may take a while depending on the amount of content in your workspace. ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view the sync status for each source directly on the list, including last sync time, number of indexed items, and any errors. Click **Settings** on a source for more details and sync history. ### Sync Modes The Notion connector supports two sync modes: | Mode | Description | | --------------- | ------------------------------------------------------------------------------------------------ | | **Full** | Syncs all shared data sources, data source entries, and standalone pages (used for initial sync) | | **Incremental** | Syncs pages and data sources modified since the last sync (used for updates) | After the initial full sync, the connector automatically performs incremental syncs to capture new and updated content. ### Adding More Content To index additional pages or databases: 1. Share the new content with your integration in Notion (see Step 2) 2. The next sync will automatically include the newly shared content ### Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the Notion source 3. Click **Delete Permanently** ## Troubleshooting The most common cause is that the page or its parent has not been shared with the integration. **Solution**: 1. Open the page in Notion 2. Click **...** → **Connections** 3. Verify your integration is listed 4. If not, add the connection and trigger a new sync The integration secret may be expired or revoked. **Solution**: 1. Go to [My Integrations](https://www.notion.so/my-integrations) 2. Select your integration and regenerate the secret 3. Delete the existing Notion source in Omni and reconnect it with the new secret Notion has API rate limits. The connector handles these automatically with exponential backoff. **Solution**: Wait for the connector to retry. For large workspaces, initial sync may take longer due to rate limits. Data source entries are only indexed if the parent database/data source is shared with the integration. **Solution**: Ensure the database or data source itself (not just individual entries) is shared with the integration via **Connections**. ## Security Considerations * **Read-only access**: The connector only reads page and database content * **Token security**: Integration secrets should be treated like passwords * **Encrypted storage**: Credentials are encrypted at rest in Omni * **Explicit sharing**: Only content explicitly shared with the integration is accessible — the connector cannot access your entire workspace by default ## Permissions The Notion connector uses workspace-level permissions. All members of the Notion workspace can access synced content in Omni. Content visibility in Omni is determined by what has been shared with the integration — only pages, databases, and data sources explicitly shared with your Notion integration (see [Step 2](#step-2-share-content-with-the-integration)) are indexed and searchable. ## What's Next Learn how to search across Notion pages Ask questions about your Notion content Connect additional data sources # Connectors Overview Source: https://docs.getomni.co/connectors/overview Connect your data sources to Omni for unified search and AI-powered answers Connectors are the bridge between your organization's data sources and Omni. Each connector syncs content from a specific platform, making it searchable and available to the AI assistant. ## Available Connectors Drive, Docs, Sheets, Slides, Gmail, and Google Chat Account structure, campaigns, ad groups, keywords, and live reports OneDrive, SharePoint, Outlook, Calendar, and Teams Confluence wikis and JIRA issues Messages, threads, and file attachments Repositories, issues, pull requests, and discussions Contacts, companies, deals, and activities Accounts, contacts, opportunities, leads, cases, and tasks with native actions and MCP tools Employee directory, org data, holidays, ATS jobs, and HR workflow actions Pages, databases, and workspace content Meeting transcripts, summaries, and action items Issues, projects, and documents Tasks and documents Email messages and attachments Files and documents from a Nextcloud instance OCR'd documents, tags, and custom fields Local files and directories Crawl and index any public website Work items, descriptions, comments, and MCP-powered actions Need to connect a custom data source? Use the [Python SDK](/developers/python-sdk) or [TypeScript SDK](/developers/typescript-sdk) to build your own connector. ## How Connectors Work All Omni connectors follow the same pattern: 1. **Authentication**: You provide credentials (API keys, OAuth tokens, or service accounts) that grant Omni read-only access to the data source. 2. **Initial Sync**: The connector performs a full sync of all accessible content. This may take minutes to hours depending on the amount of data. 3. **Incremental Updates**: After the initial sync, the connector periodically checks for new or updated content and syncs only the changes. 4. **Permission Inheritance**: Users only see search results for content they have access to in the original data source. ## Security * **Read-only indexing**: Connector syncs read data from your source systems. Some connectors can additionally expose approved, user-authorized write actions to the AI assistant. * **Credential storage**: All credentials are encrypted at rest * **Permission sync**: Access controls from source systems are respected in Omni ## What's Next Connect Google Drive, Docs, and Gmail Connect Confluence and JIRA Create your own connector with Python or TypeScript Monitor and manage your connected sources # Paperless-ngx Source: https://docs.getomni.co/connectors/paperless Connect a Paperless-ngx instance to Omni for unified search across scanned and OCR'd documents The Paperless-ngx connector enables Omni to index and search documents stored in a [Paperless-ngx](https://docs.paperless-ngx.com/) instance via its REST API. OCR text, tags, correspondents, custom fields, and notes are all indexed. ## Overview ### What Gets Indexed | Source | Content | | -------------- | ----------------------------------------------------------------------- | | Documents | Full OCR text extracted by Paperless-ngx | | Classification | Correspondent, document type, tags, storage path, archive serial number | | Custom fields | Resolved to human-readable field names | | Notes | User annotations with author and timestamp | | Dates | Created, added, and modified timestamps | ### How It Works 1. You provide a Paperless-ngx base URL and API token 2. The connector fetches documents via the Paperless-ngx REST API 3. Full syncs retrieve all documents; incremental syncs fetch only documents modified since the last run 4. Incremental state is checkpointed periodically so large syncs can resume The connector is strictly read-only — it never modifies documents, tags, or metadata in your Paperless-ngx instance. ## Prerequisites Before setting up the Paperless-ngx connector, ensure you have: * **Paperless-ngx instance** reachable from the Omni network * **An API token** with read access to documents, tags, correspondents, document types, storage paths, and custom fields * **Omni deployment** with the Paperless connector service running (profile: `paperless`) ## Setup ### Step 1: Create an API Token 1. Sign in to Paperless-ngx as the user Omni will connect as 2. Go to **Settings** → **API** → **API Tokens** 3. Create a new token and copy it — it will only be shown once Use a dedicated service user with read-only permissions. See the [Paperless-ngx API authorization docs](https://docs.paperless-ngx.com/api/#authorization) for more details. ### Step 2: Connect in Omni 1. Navigate to **Settings** → **Integrations** in Omni 2. Find **Paperless-ngx** and click **Connect** 3. Enter the connection details: * **Base URL**: Your Paperless-ngx instance (e.g. `https://paperless.example.com`) * **API Key**: The token from Step 1 4. Click **Connect** 5. Click **Sync** to start the initial sync Your Paperless-ngx connector is now configured. Initial indexing time depends on the number of documents in your instance. ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view last sync time, number of indexed documents, and any errors. Click **Settings** for details and sync history. ### Sync Modes | Mode | Description | | --------------- | -------------------------------------------------------------------------------- | | **Full** | Fetches all documents from the instance (used for initial sync) | | **Incremental** | Fetches only documents modified since the last sync (tracked via `last_sync_at`) | Incremental state is checkpointed periodically so syncs can resume if interrupted. ### Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the Paperless-ngx source 3. Click **Delete Permanently** ## Troubleshooting The API token is incorrect, expired, or lacks the required permissions. **Solution**: Verify the token in Paperless-ngx at **Settings** → **API** → **API Tokens** and regenerate if needed. Ensure the token's user has read access to documents and their related resources. Common causes: * Sync has not yet completed * The API token's user doesn't have permission to view the documents **Solution**: Check the sync status in **Settings** → **Integrations** → **Paperless-ngx** and verify the user's permissions in Paperless-ngx. Custom fields are resolved through a separate API call. If the token lacks access to the custom fields endpoint, they will not appear. **Solution**: Grant the token's user read access to custom fields in Paperless-ngx. ## Security Considerations * **Read-only access**: The connector only issues `GET` requests to the Paperless-ngx API * **API tokens**: Use a dedicated service user and token so access can be revoked independently * **Encrypted storage**: Credentials are encrypted at rest in Omni ## What's Next Learn how to search across your documents Ask questions about your scanned documents Connect additional data sources # Salesforce Source: https://docs.getomni.co/connectors/salesforce Connect Salesforce CRM to Omni for search, permissions, actions, and MCP tools Salesforce has two modes: | Mode | Provides | | ----------------------- | ----------------------------------------------------------------------------------------------------------- | | **Sync + optional MCP** | Indexes CRM records, mirrors Salesforce sharing, and can provide native actions plus live MCP actions | | **MCP only** | Provides live MCP actions with each user's Salesforce identity; no Salesforce records are synced or indexed | Choose **MCP only** for live Salesforce queries and actions without an Omni record index. Choose **Sync + optional MCP** for indexed search, inherited Salesforce permissions, search operators, native actions, and optional MCP. ## Overview ### Synced records Sync-enabled sources index: | Object | Content | | ------------- | ------------------------------------------------------------------------- | | Accounts | Company profile, industry, billing location, revenue, owner | | Contacts | Name, email, phone, title, department, parent account | | Opportunities | Amount, stage, close date, probability, type, lead source, parent account | | Leads | Name, company, status, industry, lead source, owner | | Cases | Case number, subject, status, priority, type, origin, account, contact | | Tasks | Subject, status, priority, activity date, related records | Sync also reads `User`, `Group`, `GroupMember`, `UserRole`, and the relevant `*Share` tables to resolve permissions. Deleted records are reconciled from Salesforce's deleted-records API. Salesforce users appear in Omni's people directory; CRM records are documents. ### Permissions For sync-enabled sources, Omni mirrors: * **Ownership:** owners, including queue members, receive access. * **Role hierarchy:** with *Grant Access Using Hierarchies*, ancestor roles of the owner receive access. * **Shares:** user, group, and role grants from the per-object share tables are expanded. * **Org-wide defaults:** only configured public-read objects are public; other records are private by default. Only active Salesforce users with email addresses receive grants. The default permission settings are `sync_users`, `sync_groups`, `sync_shares`, and `grant_access_using_hierarchies`. Disabling `sync_users` also disables sharing and hierarchy resolution. MCP-only does not copy records or sharing data into Omni. Each user needs **API Enabled** and the object, field, and record permissions required by that user's live queries or actions. ## Prerequisites All modes require: * Omni with the Salesforce connector service running (`salesforce` connector). * Salesforce administrator access to create an External Client App and configure its policies. Any setup that enables MCP also requires this callback, using the public Omni base URL: ```text theme={null} https:///api/oauth/callback ``` For local Omni, use its local base URL with the same `/api/oauth/callback` path. MCP authorization also requires a setup administrator or permitted Salesforce user who can complete OAuth. Sync-enabled sources additionally require a dedicated integration user with a full Salesforce user license, a permission set with the sync permissions below, and a secure location for an RSA private key. ## Setup Each tab is a complete workflow. Do not combine steps between tabs. ### Step 1: Create the External Client App 1. In Salesforce, open **Apps → External Client Apps → External Client App Manager → New External Client App**. 2. Name it `Omni`, provide a contact email, and keep distribution **Local**. 3. Enable OAuth and set the callback to `https:///api/oauth/callback`. 4. Save the generated **Client ID/Consumer Key** and **Client Secret**. Omni is a server-side confidential client; retain secret-required settings for authorization-code/web-server exchange if Salesforce presents them. 5. Enable **Authorization Code and Credentials Flow** and **Require Proof Key for Code Exchange (PKCE)**. #### OAuth permissions In the External Client App, select these Salesforce permissions: * **Access unique user identifiers** * **Access the identity URL service** * **Manage user data via APIs** * **Perform requests at any time** These permissions let Omni identify users, call Salesforce APIs, and maintain MCP authorization. Do not select **Manage user data via Web browsers** or **Access Salesforce hosted MCP servers**; Omni does not require them. Configure policies so the setup administrator and intended Salesforce users can authorize the app; preauthorize them if the org uses admin-approved users. MCP only does **not** require JWT Bearer Flow, an integration user, certificate, private key, instance URL, or sync interval. ### Step 2: Create the Omni source 1. Open **Admin Settings → Integrations → Connect Salesforce**. 2. Select **MCP only**. 3. Enter the External Client App's **Client ID**, **Client Secret**, and **Login URL**: `https://login.salesforce.com` for production or `https://test.salesforce.com` for a sandbox. 4. Select **Connect**. The form has no instance URL, JWT, certificate, private-key, or sync-interval field. No Salesforce records are synced or indexed. ### Step 3: Authorize the setup administrator After you select **Connect**, Omni immediately redirects your browser to Salesforce. Complete these actions in Salesforce: 1. Sign in as the setup administrator. 2. Confirm that you are authorizing the Salesforce organization this Omni source should represent. 3. Approve the requested OAuth scopes and return to Omni. Omni saves this administrator credential for the Salesforce source, uses it to establish the organization's binding, and discovers the MCP catalog. Setup is complete only when the redirect returns to Omni and catalog discovery succeeds. Salesforce supplies instance information in the OAuth response; you do not enter an instance URL. ### Step 4: Authorize users Every Omni user invoking MCP authorizes their own Salesforce identity. The setup administrator is the first user; later users authorize on first MCP use: 1. Start an MCP action in chat. 2. Follow the Salesforce redirect, sign in, complete MFA if requested, approve scopes, and return to Omni. 3. Ensure that user has **API Enabled** and the object, field, and record permissions required by the live action. Credentials and Salesforce permissions remain per user. Omni does not use the setup administrator's credential for another user. Revoked, expired, missing, or wrong-organization credentials must be authorized again. ### Sync step 1: Create the integration user 1. In Salesforce, open **Setup → Users → Users → New User**. 2. Create a dedicated user such as `Omni Salesforce Integration`. 3. Use a full Salesforce user license. The **Salesforce Integration** license cannot provide the role-hierarchy and user-visibility permissions native sync needs. 4. Keep the user active and record its username. ### Sync step 2: Grant sync permissions Create a permission set, for example `Omni Connector Read`, with no license, then assign it to the integration user. Enable: * **Object Settings:** **Read** and **View All Records** for `Account`, `Contact`, `Opportunity`, `Lead`, `Case`, and `Task`. * **System Permissions:** `API Enabled`, `View All Data`, `View Roles and Role Hierarchy`, and `View All Users`. The connector reads `AccountShare`, `ContactShare`, `OpportunityShare`, `LeadShare`, and `CaseShare` through the parent-object and **View All Data** permissions; these share objects are not granted individually. If `User.UserRoleId` is not queryable, role and share resolution fails. Use a full Salesforce license, or disable hierarchy/share resolution with `grant_access_using_hierarchies: false` and, where necessary, `sync_shares: false`. ### Sync step 3: Generate the JWT key pair Run outside the repository: ```bash theme={null} umask 077 openssl genrsa -out privatekey.pem 2048 openssl req -new -x509 \ -key privatekey.pem \ -out server.crt \ -days 3650 \ -subj "/CN=Omni Salesforce Integration" chmod 600 privatekey.pem ``` Keep `privatekey.pem` secret. Upload only `server.crt` to Salesforce. ### Sync step 4: Create the External Client App For every sync source: 1. Open **Apps → External Client Apps → External Client App Manager → New External Client App**. Name it `Omni`, provide a contact email, and keep distribution **Local**. 2. Obtain the **Consumer Key/Client ID**. 3. Enable **JWT Bearer Flow** and upload `server.crt`. 4. Configure policies/preauthorization so the dedicated integration user (or its permitted profile/user) can use the JWT flow. This integration-user policy is required for sync. A sync-only JWT setup does **not** require a callback URL, Client Secret, MCP scopes, **Authorization Code and Credentials Flow**, or PKCE. To add optional MCP to this same app: 1. Enable OAuth and set `https:///api/oauth/callback` as the callback. 2. Retain the generated **Client Secret** for the server-side authorization-code exchange; retain any secret-required web-server settings Salesforce presents. 3. Select these Salesforce permissions: * **Access unique user identifiers** * **Access the identity URL service** * **Manage user data via APIs** * **Perform requests at any time** Do not select **Manage user data via Web browsers** or **Access Salesforce hosted MCP servers**; Omni does not require them. 4. Enable **Authorization Code and Credentials Flow** and **PKCE**. 5. Permit intended Salesforce user profiles/users to authorize MCP. The integration user's JWT policy remains required separately. ### Sync step 5: Create the JWT source 1. Open **Admin Settings → Integrations → Connect Salesforce** and select **External Client App (JWT)**. 2. Enter **Consumer Key**, **Private Key (PEM)**, the integration **Username**, and **Login URL** (`https://login.salesforce.com` for production or `https://test.salesforce.com` for a sandbox). 3. Enter the exact Salesforce **Instance URL**. 4. Select **Connect**, keep the source enabled, and configure its sync interval. The instance URL and JWT credentials bind the sync source. It now provides indexed records and native actions. The UI also offers **Access Token** for short-lived native sync/action trials. It expires within hours and cannot itself authenticate per-user MCP; adding MCP requires a separately configured External Client App/OAuth client. ### Sync step 6: Optionally configure MCP If MCP was enabled in Step 4: 1. Open the Salesforce source under **Admin Settings → Integrations**. 2. Select **Configure MCP OAuth client**. 3. Enter the same app's **Client ID** and **Client Secret**. 4. Leave **DCR initial access token** blank for a pre-created app, then save. Dynamic Client Registration requires an administrator-issued initial access token. This client is stored for this Salesforce source and is separate from JWT credentials. The source is already bound by its instance URL/JWT setup; no admin-first OAuth is required. The first permitted MCP user can authorize from **Authorize Salesforce** or the first MCP action, which also discovers the MCP catalog. ### Sync step 7: Authorize each MCP user Every user invoking MCP authorizes their own Salesforce identity. The first MCP user may be any permitted user; later users authorize when they first use MCP: 1. Start an MCP action in chat. 2. Follow the Salesforce redirect, sign in, complete MFA if requested, approve scopes, and return to Omni. 3. Ensure that user has **API Enabled** and the object, field, and record permissions required by live actions. If optional MCP was not configured, the JWT source still provides sync, indexed search, and native actions without per-user MCP authorization. ## Managing the Integration ### Sync-enabled capabilities These apply only to sync-enabled sources: * **Records and status:** indexed Salesforce records, sync status, manual sync, and a configurable interval. Changes to synced objects, fields, or permission settings trigger full reconciliation. * **Search operators:** `owner:`, `status:`, `priority:`, `stage:`, `account:`, `industry:`, and `lead_source:` filter indexed documents. * **Native actions:** `find_records` (read), `get_case` (read), `create_case` (write), `update_case_status` (write), `create_task` (write), and `update_task_status` (write). Write actions require confirmation and honor read-only/action authorization. MCP-only has no sync schedule, manual sync, sync status, or indexed Salesforce records. ### MCP actions (both modes) MCP actions use the local official `@salesforce/mcp` integration. Omni exposes its discovered tool catalog; each execution uses the invoking user's OAuth credential, and Salesforce enforces that user's permissions. The data toolset includes read tools such as `get_username`, `list_all_orgs`, and `run_soql_query` when available. “MCP only” means **MCP-origin actions only**, not read-only. It excludes native actions and sync, but write-classified MCP actions may still be available subject to confirmation, authorization, and the source's read-only setting. ## Troubleshooting Check the integration user's full license, object **Read** and **View All Records**, and system permissions from Sync step 2. `UserRoleId` is required for default hierarchy resolution; use the documented settings only if your license cannot provide it. Confirm the source-scoped Client ID/Secret. For MCP-only, the setup administrator must finish the immediate redirect and catalog discovery. For sync-enabled sources, the JWT source must be enabled and the first permitted user must authorize MCP. MCP-only binding comes from the first administrator's Salesforce OAuth authorization, not an entered instance URL or JWT. Reauthorize in the intended organization and check the app's login and callback URLs. Salesforce evaluates the invoking user's credential. Grant that user **API Enabled** and the required object, field, and record permissions; the setup administrator's permissions do not transfer. This is sync-only. Run a manual sync after changing profiles, permission sets, roles, or sharing rules and confirm it completes. ## Security Considerations * Sync uses the dedicated integration user's JWT; MCP uses each user's OAuth credential. The JWT is never an MCP fallback. * Sync binding uses the configured instance URL and validated JWT credentials. MCP-only binding comes from the first administrator OAuth authorization. * Store private keys, Client Secrets, access/refresh tokens, and MFA codes securely. Omni encrypts source and user credentials at rest. * Limit Salesforce permissions to required sync or live-query work. Read-only settings govern write-capable native and MCP actions; MCP-only is not inherently read-only. ## What's Next Search synced CRM records alongside your other sources Understand Salesforce sharing for synced records Manage sync-enabled sources and integrations # Slack Source: https://docs.getomni.co/connectors/slack Connect Slack workspaces to Omni for unified search across messages, threads, and files The Slack connector enables Omni to index and search content from your Slack workspace, including channel messages, threads, and text files. ## Overview ### What Gets Indexed | Source | Content | | ---------------- | --------------------------------------------------------------------- | | Channel Messages | Messages from public and private channels (where bot is a member) | | Threads | Full thread conversations with all replies | | Direct Messages | 1:1 DMs and group DMs the bot is a participant in | | Files | File attachments — text, PDFs, DOCX/PPTX/XLSX, images (via OCR), etc. | ### How It Works 1. A Slack bot is installed in your workspace with read-only permissions 2. The connector syncs messages from channels and DMs where the bot is a member 3. Messages are grouped by date and thread for efficient indexing 4. Permission inheritance ensures users only see content from channels and DMs they can access 5. On first sync, the connector indexes the last 2 years of history per conversation by default — see [Advanced configuration](#advanced-configuration) to tune For direct messages, the bot can only see DMs and group DMs it has been explicitly invited to or messaged. Bot tokens cannot read DMs between two users — that's a Slack platform restriction, not an Omni one. The connector uses read-only access. Omni cannot post messages or modify any content in your Slack workspace. ## Prerequisites Before setting up the Slack connector, ensure you have: * **Slack workspace admin access** to install apps * **Omni deployment** with the Slack connector service running ## Setup ### Step 1: Create a Slack App 1. Go to [Slack API Apps](https://api.slack.com/apps) 2. Click **Create New App** → **From an app manifest** 3. Select your workspace and click **Next** 4. Choose **YAML** and paste the following manifest: ```yaml theme={null} display_information: name: Omni description: Read-only bot for Omni features: bot_user: display_name: Omni always_online: false oauth_config: scopes: bot: - "channels:history" - "channels:join" - "channels:read" - "files:read" - "groups:history" - "groups:read" - "im:history" - "im:read" - "mpim:history" - "mpim:read" - "usergroups:read" - "users:read" - "users:read.email" settings: event_subscriptions: bot_events: - "message.channels" - "message.groups" - "message.im" - "message.mpim" - "file_created" - "file_deleted" - "file_change" org_deploy_enabled: false socket_mode_enabled: true token_rotation_enabled: false ``` 5. Click **Next**, review the summary, and click **Create** | Scope | Purpose | | ------------------ | -------------------------------------------------------------------- | | `channels:history` | Read message history from public channels | | `channels:join` | Allow bot to join public channels for indexing | | `channels:read` | View basic channel information | | `files:read` | Download files attached to messages | | `groups:history` | Read message history from private channels the bot has been added to | | `groups:read` | View basic info for private channels the bot has been added to | | `im:read` | List 1:1 DMs the bot is a participant in | | `im:history` | Read messages from those 1:1 DMs | | `mpim:read` | List group DMs the bot is a participant in | | `mpim:history` | Read messages from those group DMs | | `usergroups:read` | View user groups in the workspace | | `users:read` | View user profiles for author information | | `users:read.email` | View user email addresses for identity matching | ### Step 2: Install App to Workspace 1. Go to **OAuth & Permissions** 2. Click **Install to \[Workspace Name]** 3. Review and authorize the permissions 4. Copy the **Bot User OAuth Token** (starts with `xoxb-`) Keep your bot token secure. Anyone with access to this token can read messages from your Slack workspace. ### Step 3: Enable Real-Time Updates (Optional) To receive near real-time indexing of new and changed messages, enable Socket Mode and create an app-level token: 1. Go to **Socket Mode** in your Slack app settings and ensure **Socket Mode** is enabled 2. Go to **Basic Information** → **App-Level Tokens** 3. Click **Generate Token and Scopes** 4. Add the `connections:write` scope 5. Generate the token and copy the **App-Level Token** (starts with `xapp-`) This step is optional. Without an app-level token, the connector still works using scheduled full and incremental syncs. Enabling Socket Mode adds near real-time message indexing with up to \~10 minute delay. Files attached to indexed messages are processed with those message updates. ### Step 4: Add Bot to Channels During sync, the bot automatically joins all public channels. For private channels, you must manually add the bot: 1. Open the private channel you want to index in Slack 2. Click the channel name to open details 3. Go to **Integrations** tab 4. Click **Add apps** 5. Find and add **Omni** Public channels are automatically indexed — the bot joins them during each sync. You only need to manually invite the bot to **private channels** you want indexed. ### Step 5: Connect in Omni 1. Navigate to **Settings** → **Integrations** in Omni 2. Find **Slack** and click **Connect** 3. Enter your Slack **Bot User OAuth Token** and, optionally, the **App-Level Token** for real-time updates 4. Click **Connect** 5. Click **Settings** against the Slack source to adjust source settings, then click **Sync** to start the initial sync. Your Slack connector is now configured. Initial indexing may take a while depending on the amount of content in your channels. ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view the sync status for each source directly on the list, including last sync time, number of indexed items, and any errors. Click **Settings** on a source for more details and sync history. ### Sync Modes The Slack connector supports three sync modes: | Mode | Description | | --------------- | ----------------------------------------------------------------------------------------------------------------------------- | | **Full** | Syncs all messages from all channels (used for initial sync) | | **Incremental** | Syncs only new messages since last sync (used for updates) | | **Real-Time** | Receives message events via Socket Mode for near real-time message indexing (up to \~10 min delay). Requires app-level token. | After the initial full sync, the connector automatically performs incremental syncs to capture new messages. To enable real-time updates, provide the app-level token during setup (see Step 3). ### Adding More Channels To index additional channels: 1. Add the Omni bot to the new channel (see Step 4) 2. The next sync will automatically include the new channel ### Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the Slack source 3. Click **Delete Permanently** 4. Optionally, uninstall the Omni app from your Slack workspace ## Advanced configuration The Slack connector reads a few optional environment variables at startup. These apply to the connector container globally — set them in your deployment's env file or task definition. | Variable | Default | Description | | -------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `SLACK_MAX_AGE_DAYS` | `730` (2 years) | First-sync history horizon, in days. Channels and DMs are backfilled at most this far back. Subsequent incremental syncs only fetch messages newer than the last seen one and ignore this bound. Increase if you have a paid Slack plan and want full historical coverage; decrease to keep the initial sync fast on busy workspaces. | ## Troubleshooting Common causes: * Bot is not a member of the channel * Sync has not completed yet * Channel permissions don't allow the user to see results **Solution**: Add the bot to the channel and trigger a manual sync. The token may be expired or revoked. **Solution**: 1. Go to your Slack app settings 2. Regenerate the Bot User OAuth Token 3. Delete the existing Slack source in Omni and reconnect it with the new token Slack has API rate limits. The connector handles these automatically with backoff. **Solution**: Wait for the connector to retry. For large workspaces, initial sync may take longer due to rate limits. The bot must be explicitly invited to private channels. **Solution**: Open the private channel, go to **Integrations** → **Add apps**, and add the Omni bot. Most file types — text, PDFs, Office docs (DOCX/PPTX/XLSX), and images (via OCR) — are extracted automatically. Audio/video and other unsupported binary formats are skipped silently. If a specific file is missing, common causes: * The bot lost access to the channel where the file was shared * The file is on a Slack plan with retention limits and is older than the workspace's retention window * Extraction failed for that specific file (check connector logs) ## Security Considerations * **Read-only access**: The bot only has read permissions * **Channel-based permissions**: Users only see messages from channels they can access in Slack * **Token security**: Bot tokens should be treated like passwords * **Audit logging**: Monitor bot activity in Slack's audit logs ## What's Next Learn how to search across Slack messages Ask questions about your Slack conversations Connect additional data sources # Web Connector Source: https://docs.getomni.co/connectors/web Crawl and index content from any public website The Web Connector allows you to crawl and index content from any publicly accessible website, making it searchable in Omni. ## Overview ### What Gets Indexed | Source | Content | | --------- | --------------------------------- | | Web pages | Page text, headings, and metadata | | Documents | PDFs and other linked documents | ### How It Works 1. You provide a root URL as the starting point 2. The crawler follows links to discover pages within the configured depth and limits 3. Page content is extracted and indexed for search 4. Subsequent syncs check for updated content The connector only accesses publicly available content. It respects robots.txt by default. ## Prerequisites Before setting up the Web Connector, ensure you have: * **Target website URL** that is publicly accessible (HTTP or HTTPS) * **Permission to crawl** the target website (check their terms of service) ## Setup ### Step 1: Navigate to Integrations 1. Go to **Settings** → **Integrations** in Omni 2. Find **Web** in Available Integrations and click **Connect** ### Step 2: Configure the Crawler Enter the following configuration: | Field | Required | Default | Description | | ------------------ | -------- | ------- | ------------------------------------------------------------------------------------------------------ | | Root URL | Yes | - | The starting URL for the crawler (must be HTTP or HTTPS) | | Max Depth | No | 10 | How many levels deep to follow links from the root URL | | Max Pages | No | 10,000 | Maximum number of pages to crawl; the crawler stops once this many pages are indexed (`0` = unlimited) | | Respect Robots.txt | No | Yes | Whether to honor the site's robots.txt rules | | Include Subdomains | No | No | Whether to crawl subdomains of the root URL | | Blacklist Patterns | No | Empty | URL patterns to exclude from crawling | | User Agent | No | Default | Custom user agent string for HTTP requests | ### Step 3: Connect 1. Click **Connect** to start crawling 2. A new web source is created with the name `Web: ` 3. An initial sync is triggered automatically Your Web Connector is now configured. You can monitor crawl progress on the Integrations page. *** ## Configuration Tips **Start small**: Begin with a lower `Max Pages` value (e.g., 100) to test the crawler before doing a full crawl. ### Blacklist Patterns Use blacklist patterns to exclude specific sections from crawling: ``` /api/* /admin/* /login/* *.pdf ``` Common patterns to exclude: * Login and authentication pages (`/login/*`, `/auth/*`) * API documentation (`/api/*`, `/swagger/*`) * Admin sections (`/admin/*`, `/dashboard/*`) * Large binary files (`*.zip`, `*.tar.gz`) ### Subdomain Handling Enable **Include Subdomains** only if the site uses subdomains for content you want to index: * `blog.example.com` * `docs.example.com` * `help.example.com` Enabling subdomains can significantly increase the crawl scope. Monitor page counts carefully. *** ## Managing the Integration ### Viewing Sync Status Navigate to **Settings** → **Integrations** to view the sync status for each source directly on the list, including last sync time, number of indexed items, and any errors. Click **Settings** on a source for more details and sync history. ### Updating Configuration 1. Navigate to **Settings** → **Integrations** 2. Find your web source and click **Settings** 3. Update settings as needed 4. Click **Save** to apply changes Changes take effect on the next sync cycle. ### Removing the Integration 1. Navigate to **Settings** → **Integrations** 2. Click **Settings** against the web source 3. Click **Delete Permanently** *** ## Troubleshooting Common causes: * **Max Depth too low**: Increase depth if pages are nested deep in the site structure * **Blacklist too aggressive**: Check if your patterns are excluding desired content * **JavaScript-rendered content**: The crawler may not execute JavaScript; static HTML is indexed Try starting with a higher Max Depth and fewer blacklist patterns. To speed up crawling: * Lower **Max Pages** to limit scope * Add **Blacklist Patterns** for large or irrelevant sections * Disable **Include Subdomains** if not needed Large sites (100,000+ pages) may take several hours to fully index. The target site may be blocking the crawler: * Verify the site allows web crawlers (check robots.txt) * Some sites block automated access entirely * Try setting a custom **User Agent** string Content updates are picked up during sync cycles: * Check when the last sync occurred * Trigger a manual sync if needed * New pages may not be discovered if they're not linked from existing indexed pages ## Security Considerations * **Respect robots.txt**: Keep this enabled to follow site owner preferences * **Rate limiting**: The crawler automatically rate-limits requests to avoid overloading servers * **Public content only**: Only publicly accessible pages are indexed * **No authentication**: The crawler cannot access login-protected content ## What's Next Learn how to search across indexed web content Ask questions about your indexed websites Connect additional data sources # Windshift Source: https://docs.getomni.co/connectors/windshift Connect Windshift work items to Omni for personal search and MCP-powered actions The Windshift connector indexes the work items, descriptions, and comments a user can see in Windshift, and exposes Windshift's MCP tools as Omni actions. Windshift is a **personal source**: every user connects their own account, runs an independent sync, and sees only their own indexed items. ## Overview ### What Gets Indexed | Windshift | Omni document | | -------------------------- | ----------------------------------------------------- | | Item ID | `external_id = windshift:item:` | | Title | Document title | | Description and comments | Markdown content | | Workspace | `attributes.workspace` | | Status | `attributes.status` | | Priority | `attributes.priority` | | Assignee | `attributes.assignee` and `attributes.assignee_email` | | Milestone and iteration | `attributes.milestone` and `attributes.iteration` | | Created/updated timestamps | Document metadata | Full sync walks the workspaces visible to the connecting user while capturing each workspace's change watermark. Incremental sync then reads Windshift's ordered item change log, including comment activity and deletions. ### How It Works 1. An administrator sets the externally reachable Windshift base URL once for the instance. 2. Omni dynamically registers itself as a public OAuth client (no client ID or secret to manage), uses S256 PKCE, and binds every token to the Windshift MCP resource. 3. Each user connects Windshift from **My Integrations**. The initial authorization grants read access for that user's sync and read-only MCP tools; write and destructive tools request expanded authorization the first time they are used. Windshift 0.8.4 or newer is required, with Windshift's MCP server enabled (`MCP_ENABLED=true`). ## Prerequisites * **Windshift 0.8.4+** with the MCP server enabled * **Admin access** to Omni to set the Windshift server URL * **A user account in Windshift** for each person who wants to search Windshift content * Omni deployment with the Windshift connector service running (profile: `windshift`) ## Setup ### Step 1: Configure the Windshift server URL (admin) Windshift is configured in the admin UI, not through environment variables: 1. Open **Settings** → **Integrations**. 2. Find the **Windshift server** card on the **Sources** tab and click **Configure**. 3. Enter the externally reachable Windshift base URL, e.g. `https://windshift.example.com`. If Windshift uses a context path, include it. 4. Save. The URL is validated against Omni's SSRF policy — only `http(s)`, no credentials or fragments, and the resolved address must be publicly routable. The setting is stored in the database and read by the connector from connector-manager, so changes propagate without a container restart. Deployments that predate the UI setting can still set `WINDSHIFT_BASE_URL` on the connector container as a fallback. The UI setting wins when both are present. #### Optional: internal route for server-to-server traffic To keep client registration, token exchange, user-info, sync, and MCP traffic off the public network, set `WINDSHIFT_INTERNAL_BASE_URL` on the connector container to a private route to the same Windshift instance, e.g. `http://windshift:8080` on the Compose network. This is intentionally environment-only and cannot be set from the UI. When set, the connector advertises it in its manifest and Omni's OAuth flow allows that exact origin (scheme + host + port) to resolve to RFC1918 addresses; loopback, link-local/metadata, and reserved ranges are still rejected, and every other endpoint must remain publicly routable. Browser authorization, resource binding, and document links always use the public URL. ### Step 2: Connect Windshift (each user) 1. Open **My Integrations** and select **Windshift**. 2. Click **Continue to Windshift** to start the OAuth flow. 3. Sign in to Windshift and approve the requested scopes. 4. Return to Omni. The connector starts syncing the workspaces you can access. Access tokens are refreshed automatically, and rotated refresh tokens are persisted under the same per-credential database lock. ## Managing the Integration ### Restricting Sync To restrict sync to specific Windshift workspaces, set the source's `workspace_keys` option to a list of workspace keys (omit it to sync all visible workspaces). ### Search Operators | Operator | Meaning | | ------------ | ----------------------- | | `status:` | Filter by item status | | `priority:` | Filter by item priority | | `assignee:` | Filter by assignee | | `workspace:` | Filter by workspace | | `milestone:` | Filter by milestone | | `iteration:` | Filter by iteration | ### MCP Actions Windshift has no static action list. Omni discovers the tools exposed by your Windshift MCP server after the first sync and surfaces them as source-scoped actions — for example `list_items`, `transition_item`, and `add_comment`. Read-only tools use the read scope from the initial authorization; write and destructive tools request expanded authorization when first used. ## Permissions Windshift is a personal source. Each user's sync is backed by their own OAuth credential, and indexed items are visible only to that Omni user. Actions also run with the connecting user's Windshift identity, so tools can only see and change what that user can see and change in Windshift. ## Removing the Integration 1. Open **My Integrations** and remove the Windshift connection. 2. Revoke the Omni app in Windshift if you no longer want it authorized. Removing the connection stops syncing and removes that user's indexed Windshift documents. ## Troubleshooting An administrator must set the Windshift URL in **Settings** → **Integrations** → **Windshift server** (or set `WINDSHIFT_BASE_URL` as a fallback on the connector container). If Omni reaches Windshift over a private route, set `WINDSHIFT_INTERNAL_BASE_URL` on the connector container. Browser-facing authorization still uses the public URL. Confirm the Windshift version is 0.8.4 or newer and that Windshift's MCP server is enabled. Omni discovers tools after the connector's first successful sync. Windshift is personal — confirm that user completed the OAuth connection from **My Integrations** and that they have access to the workspaces you expect. ## What's Next Search work items alongside your other sources Understand document and action authorization Monitor syncs and manage sources # AWS Terraform Deployment Source: https://docs.getomni.co/deployment/aws-terraform Deploy Omni to AWS using Terraform for production environments Deploy Omni to AWS using Terraform for production-ready infrastructure with auto-scaling and multi-AZ redundancy. For development or single-server deployments, see [Docker Compose Deployment](/deployment/docker-compose). ## Prerequisites * AWS account with administrative access * [Terraform](https://www.terraform.io/downloads) 1.0+ * [AWS CLI](https://aws.amazon.com/cli/) configured (`aws configure`) * GitHub container registry access (provided during onboarding) * Domain name for the application ## Quick Start ```bash theme={null} # See https://github.com/getomnico/omni/releases for available versions OMNI_VERSION="v0.1.9" curl -fsSL "https://github.com/getomnico/omni/releases/download/${OMNI_VERSION}/omni-terraform-aws.tar.gz" \ | tar xz cd terraform-aws cp terraform.tfvars.example terraform.tfvars ``` Edit `terraform.tfvars`: ```hcl theme={null} # Required customer_name = "acme-corp" # Lowercase, hyphens only github_org = "omni-platform" embedding_api_key = "your-embedding-api-key-here" custom_domain = "omni..com" # Optional: AWS Configuration region = "us-east-1" environment = "production" # Optional: Database Configuration paradedb_instance_type = "t3.small" # Default paradedb_volume_size = 50 # GB, default # Optional: Cache Configuration redis_node_type = "cache.t3.micro" redis_engine_version = "7.1" # Optional: ECS Configuration ecs_task_cpu = "512" ecs_task_memory = "1024" ecs_desired_count = 1 # Optional: SSL (leave empty for HTTP-only) # ssl_certificate_arn = "arn:aws:acm:us-east-1:xxx:certificate/xxx" # Optional: Monitoring log_retention_days = 30 # Optional: AI Service Configuration # embedding_provider = "jina" # embedding_model = "jina-embeddings-v3" # embedding_dimensions = "1024" # embedding_max_model_len = "8192" ``` See [Configuration Reference](/deployment/configuration) for all options. ## Deploy ```bash theme={null} terraform init terraform plan -out=tfplan terraform apply tfplan ``` Deployment takes 15-20 minutes. Get outputs when complete: ```bash theme={null} terraform output omni_url # Application URL terraform output alb_dns_name # For DNS CNAME record ``` Create a CNAME record pointing your domain to the ALB DNS name, then access at `https://omni..com`. ## Infrastructure Overview | Component | AWS Service | Purpose | | ------------- | ----------------- | --------------------------------------------------------------- | | Compute | ECS Fargate | Runs all Omni services (web, searcher, indexer, ai, connectors) | | Database | ParadeDB on ECS | Primary data store (PostgreSQL + pg\_search) | | Cache | ElastiCache Redis | Sessions and caching | | Load Balancer | ALB | HTTPS termination, routing | | Network | VPC | Private subnets, NAT gateway | | Secrets | Secrets Manager | Auto-generated credentials | | Logs | CloudWatch | Centralized logging | | Storage | S3 | Content and batch inference | ## Scaling ```hcl theme={null} # Vertical: increase task resources (defaults: cpu="512", memory="1024") ecs_task_cpu = "2048" # 2 vCPU ecs_task_memory = "4096" # 4 GB # Horizontal: more tasks per service (default: 1) ecs_desired_count = 3 ``` Apply with `terraform apply`. ## Updating Re-download the Terraform artifact for the new release and re-apply: ```bash theme={null} # Back up your terraform.tfvars first cp terraform.tfvars terraform.tfvars.bak OMNI_VERSION="v0.1.9" curl -fsSL "https://github.com/getomnico/omni/releases/download/${OMNI_VERSION}/omni-terraform-aws.tar.gz" \ | tar xz cd terraform-aws cp ../terraform.tfvars.bak terraform.tfvars terraform plan -out=tfplan terraform apply tfplan ``` ## Backups ```bash theme={null} # Get a running task ID TASK_ID=$(aws ecs list-tasks --cluster $(terraform output -raw ecs_cluster_name) \ --service-name paradedb --query 'taskArns[0]' --output text) # Run backup (uploads to S3 directly from container) aws ecs execute-command \ --cluster $(terraform output -raw ecs_cluster_name) \ --task $TASK_ID \ --container paradedb \ --interactive \ --command "/bin/bash -c 'pg_dump -U omni omni | gzip | aws s3 cp - s3://your-backup-bucket/backup-\$(date +%Y%m%d).sql.gz'" ``` ## Monitoring ```bash theme={null} # Tail logs aws logs tail $(terraform output -raw log_group_name) --follow # Filter errors aws logs filter-log-events \ --log-group-name $(terraform output -raw log_group_name) \ --filter-pattern "ERROR" ``` Container Insights is enabled by default for ECS metrics. Use `log_retention_days` (default: `30`) to control CloudWatch log retention. ## Destroying ```bash theme={null} # Backup database first, then destroy terraform destroy ``` ## Troubleshooting | Problem | Solution | | -------------------------- | ------------------------------------------------------------------------------ | | Terraform apply fails | Run `aws sts get-caller-identity` to verify credentials; check IAM permissions | | ECS tasks won't start | Check CloudWatch logs; verify image pull permissions; increase task memory | | Database connection errors | Verify security group allows port 5432 from ECS; check ParadeDB task status | See [Troubleshooting Guide](/operations/troubleshooting) for detailed diagnostics. ## Next Steps * [Initial Setup](/deployment/initial-setup) - Configure LLMs, embeddings, and connectors * [Connect Data Sources](/connectors/overview) - Google, Slack, Confluence * [User Management](/admin/user-management) - Add users and permissions * [Monitoring Setup](/admin/monitoring) - Configure alerts and dashboards # Omni CLI Source: https://docs.getomni.co/deployment/cli Manage Docker Compose upgrades, diagnostics, and backups with the Omni CLI The `omni` CLI manages self-hosted Omni Docker Compose deployments. v1 supports Docker Compose only. ## Install Install the latest release with the install script: ```bash theme={null} curl -fsSL https://github.com/getomnico/omni/releases/latest/download/install-cli.sh | sh ``` By default, the script installs to `$XDG_BIN_HOME` when set, otherwise `$HOME/.local/bin`. Make sure that directory is on your `PATH`. Install a specific release or choose a different install directory: ```bash theme={null} curl -fsSL https://github.com/getomnico/omni/releases/latest/download/install-cli.sh | sh -s -- --version v0.1.11 curl -fsSL https://github.com/getomnico/omni/releases/latest/download/install-cli.sh | sh -s -- --install-dir /usr/local/bin ``` The script detects Linux/macOS and x86\_64/arm64, downloads the matching GitHub release artifact, and verifies its `.sha256` file when present. You can also download the binary manually from the Omni GitHub release assets: * `omni-linux-x86_64` * `omni-linux-arm64` * `omni-macos-x86_64` * `omni-macos-arm64` * `omni-windows-x86_64.exe` Then place it on your `PATH` and make it executable on Unix: ```bash theme={null} mkdir -p "$HOME/.local/bin" chmod +x omni-linux-x86_64 mv omni-linux-x86_64 "$HOME/.local/bin/omni" omni --version ``` ## Upgrade Run from your Omni Docker Compose install directory, or pass `--install-dir`: ```bash theme={null} omni upgrade ``` By default, `omni upgrade` uses the latest stable GitHub release. To target a specific release: ```bash theme={null} omni upgrade --to v0.1.11 # or omni upgrade --to 0.1.11 ``` The upgrade command: 1. downloads the target release's `omni-docker-compose.tar.gz` and verifies `omni-docker-compose.tar.gz.sha256` when present; 2. backs up `.env` and managed deployment files under `.omni/backups//`; 3. updates managed files such as `docker/docker-compose.yml`, `.env.example`, and `Caddyfile`; 4. updates `OMNI_VERSION` in `.env` to the release image tag; 5. warns about variables missing from `.env` and prompts you to append new values; 6. warns about variables in your `.env` that no longer appear in `.env.example`; 7. runs `docker compose pull` and `docker compose up -d --remove-orphans`; 8. runs a doctor summary. Useful flags: ```bash theme={null} omni upgrade --dry-run # preview changes only omni upgrade --yes # accept prompts with defaults omni upgrade --force # continue despite detected local edits to managed files omni upgrade --skip-pull # update files without pulling images omni upgrade --skip-up # pull/update files without recreating services ``` ## Managed Compose files and customization Omni's Compose files are vendor-managed release assets. The CLI replaces these files during upgrades instead of attempting to merge YAML. This keeps upgrades predictable because service definitions, anchors, profiles, health checks, and image tags often change together. Do not customize `docker/docker-compose.yml` directly. Put local changes in user-owned override files instead, for example: * `docker/docker-compose.override.yml` * `docker-compose.override.yml` The CLI includes these override files in its Compose commands and never overwrites them. After a successful upgrade, the CLI records hashes for managed files in `.omni/managed-files.json`. On the first CLI-managed upgrade, if that manifest does not exist yet, the CLI compares against the currently configured release when possible and otherwise treats changed existing managed files conservatively as local edits. ## Environment variables `.env.example` is the canonical template for a release. During upgrade, the CLI preserves your existing `.env` values, updates `OMNI_VERSION`, and prompts for variables that exist in the target `.env.example` but not in your `.env`. Missing variables are warnings, not hard errors, because not every variable is required in every deployment. Preview env changes without upgrading: ```bash theme={null} omni env diff omni env diff --to v0.1.11 ``` ## Doctor Run diagnostics: ```bash theme={null} omni doctor omni doctor --verbose omni doctor --json ``` `doctor` checks local deployment files, Docker and Docker Compose availability, Compose service state, container health, image tags, service health endpoints, connector-manager source/connector status where reachable, recent sync run status, and recent logs for suspicious errors. ## Other commands ```bash theme={null} omni version # CLI, configured Omni version, and running Omni image tags omni version --json # machine-readable version info omni version --check # compare CLI and deployment versions against the latest release omni status # concise docker compose ps view omni logs web --tail 100 # service logs omni pull # pull images using Omni's Compose files and .env omni up # run docker compose up -d --remove-orphans omni backup # back up .env and managed files omni compose -- ps # passthrough to docker compose using Omni files ``` # Configuration Reference Source: https://docs.getomni.co/deployment/configuration Complete environment variables and configuration options for Omni This page provides a comprehensive reference for all environment variables and configuration options available in Omni. All environment variables can be set in your `.env` file for Docker Compose deployments or in `terraform.tfvars` for AWS deployments. ## Database Configuration | Variable | Required | Default | Description | | ---------------------------- | -------- | ---------- | --------------------------------------- | | `DATABASE_HOST` | Yes | `postgres` | Database hostname or IP address | | `DATABASE_PORT` | Yes | `5432` | Database port | | `DATABASE_USERNAME` | Yes | `omni` | Database username | | `DATABASE_PASSWORD` | Yes | - | Database password (use strong password) | | `DATABASE_NAME` | Yes | `omni` | Database name | | `DATABASE_SSL` | No | `false` | Enable SSL for database connection | | `DB_MAX_CONNECTIONS` | No | `10` | Connection pool size per service | | `DB_ACQUIRE_TIMEOUT_SECONDS` | No | `3` | Connection acquisition timeout | ## Redis Configuration | Variable | Required | Default | Description | | ----------- | -------- | -------------------- | -------------------------------------------------- | | `REDIS_URL` | Yes | `redis://redis:6379` | Redis connection URL (format: `redis://host:port`) | For Redis with password: ```bash theme={null} REDIS_URL=redis://:password@redis:6379 ``` ## Application Configuration | Variable | Required | Default | Description | | ----------------------- | -------- | ----------------------- | --------------------------------------------------------------- | | `APP_URL` | Yes | `http://localhost:3000` | Public-facing application URL (include protocol) | | `OMNI_DOMAIN` | No | `localhost` | Domain name for the application | | `OMNI_VERSION` | No | `latest` | Docker image version tag for all Omni services (e.g., `v0.1.9`) | | `SESSION_SECRET` | Yes | - | Secret key for session encryption (32+ characters) | | `SESSION_COOKIE_NAME` | No | `auth-session` | Name of the session cookie | | `SESSION_DURATION_DAYS` | No | `7` | Session expiry in days | | `ACME_EMAIL` | No | - | Email for Let's Encrypt notifications (for automatic HTTPS) | Never use the same `SESSION_SECRET` across different environments. Generate unique secrets for dev, staging, and production. ## Security & Encryption | Variable | Required | Default | Description | | ----------------- | -------- | ------- | --------------------------------------------------------- | | `ENCRYPTION_KEY` | Yes | - | Encryption key for sensitive credentials (32+ characters) | | `ENCRYPTION_SALT` | Yes | - | Salt for key derivation (16+ characters) | ## Service Ports ### Core Services | Variable | Required | Default | Description | | ------------------------ | -------- | ------- | ------------------------------ | | `WEB_PORT` | No | `3000` | SvelteKit web application port | | `SEARCHER_PORT` | No | `3001` | Search service port | | `INDEXER_PORT` | No | `3002` | Indexer service port | | `AI_SERVICE_PORT` | No | `3003` | AI service port | | `CONNECTOR_MANAGER_PORT` | No | `3004` | Connector manager port | ### Connector Services | Variable | Required | Default | Description | | --------------------------- | -------- | ------- | ---------------------------- | | `GOOGLE_CONNECTOR_PORT` | No | `4001` | Google connector port | | `SLACK_CONNECTOR_PORT` | No | `4002` | Slack connector port | | `ATLASSIAN_CONNECTOR_PORT` | No | `4003` | Atlassian connector port | | `WEB_CONNECTOR_PORT` | No | `4004` | Web connector port | | `GITHUB_CONNECTOR_PORT` | No | `4005` | GitHub connector port | | `HUBSPOT_CONNECTOR_PORT` | No | `4006` | HubSpot connector port | | `MICROSOFT_CONNECTOR_PORT` | No | `4007` | Microsoft 365 connector port | | `NOTION_CONNECTOR_PORT` | No | `4008` | Notion connector port | | `FIREFLIES_CONNECTOR_PORT` | No | `4009` | Fireflies connector port | | `IMAP_CONNECTOR_PORT` | No | `4010` | IMAP email connector port | | `CLICKUP_CONNECTOR_PORT` | No | `4011` | ClickUp connector port | | `LINEAR_CONNECTOR_PORT` | No | `4012` | Linear connector port | | `FILESYSTEM_CONNECTOR_PORT` | No | `4013` | Filesystem connector port | | `NEXTCLOUD_CONNECTOR_PORT` | No | `4014` | Nextcloud connector port | | `PAPERLESS_CONNECTOR_PORT` | No | `4015` | Paperless-ngx connector port | | `GOOGLE_ADS_CONNECTOR_PORT` | No | `4016` | Google Ads connector port | | `DARWINBOX_CONNECTOR_PORT` | No | `4017` | Darwinbox connector port | | `WINDSHIFT_CONNECTOR_PORT` | No | `4018` | Windshift connector port | | `SALESFORCE_CONNECTOR_PORT` | No | `4019` | Salesforce connector port | ### Optional Services | Variable | Required | Default | Description | | ---------------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------- | | `LOCAL_INFERENCE_MODEL_PORT` | No | `8000` | Local LLM inference port (used by the `llama.cpp` container in `docker-compose.local-inference.yml`) | | `LOCAL_EMBEDDINGS_PORT` | No | `8001` | Local embedding model port (used by the HuggingFace TEI container) | | `DOCLING_PORT` | No | `8003` | Docling document conversion service port | | `SANDBOX_PORT` | No | `8090` | Code execution sandbox port (used by the AI service for agent tools) | In Docker Compose, services communicate via service names (e.g., `http://searcher:3001`). Ports only need to be exposed to the host for debugging. ## Docker Compose Resource Limits Docker Compose deployments expose CPU and memory controls in `.env.example`. Defaults are conservative for a small single-node deployment and can be raised on larger hosts. | Variable group | Description | | --------------------------------- | --------------------------------------------------------- | | `OMNI_*_CPUS`, `DOCLING_CPUS` | Per-service CPU limits, expressed in cores | | `OMNI_*_MEMORY`, `DOCLING_MEMORY` | Per-service container memory limits | | `OMNI_POSTGRES_SHM_SIZE` | Shared memory allocated to Postgres | | `OMNI_CPU_SHARES_*` | Relative CPU weights used when containers contend for CPU | ## Inter-Service URLs These URLs are used for internal communication between services. In Docker Compose, they use the service name and port variable interpolation. ### Core Service URLs | Variable | Required | Default | Description | | ----------------------- | -------- | ---------------------------------------------------- | --------------------- | | `SEARCHER_URL` | Yes | `http://searcher:${SEARCHER_PORT}` | Search service URL | | `INDEXER_URL` | Yes | `http://indexer:${INDEXER_PORT}` | Indexer service URL | | `AI_SERVICE_URL` | Yes | `http://ai:${AI_SERVICE_PORT}` | AI service URL | | `CONNECTOR_MANAGER_URL` | Yes | `http://connector-manager:${CONNECTOR_MANAGER_PORT}` | Connector manager URL | ### Optional Service URLs | Variable | Required | Default | Description | | ---------------------- | ----------- | ----------------------------------------------- | ----------------------------------------------------------------------------- | | `LOCAL_EMBEDDINGS_URL` | Conditional | `http://embeddings:${LOCAL_EMBEDDINGS_PORT}/v1` | Local embeddings service URL (required if using the local embedding provider) | | `DOCLING_URL` | No | `http://docling:${DOCLING_PORT}` | Docling document conversion service URL | | `SANDBOX_URL` | No | `http://sandbox:${SANDBOX_PORT}` | Sandbox service URL (used by the AI service for agent code execution) | ## LLM Provider Configuration LLM providers and models are configured through the **Admin Panel** (Settings > LLM Providers). API keys and other secrets are **encrypted at rest** in the database using `ENCRYPTION_KEY` and `ENCRYPTION_SALT` — they are never read from environment variables. Multiple providers can be active simultaneously, and users can select which model to use on a per-chat basis. Omni validates provider settings with a lightweight test call before saving them. ### Supported Providers Omni supports seven LLM provider types: | Provider | Required Config | Description | | --------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------- | | **Anthropic** | API Key | Direct access to Claude models via Anthropic's API | | **OpenAI** | API Key | Access to GPT models via OpenAI's API | | **Google Gemini** | API Key | Direct access to Gemini models via the Google AI Studio API | | **AWS Bedrock** | AWS Region (+ optional credentials) | Claude and other models via AWS Bedrock | | **Vertex AI** | GCP Region + Project ID | Claude and Gemini models via Google Cloud Vertex AI | | **Azure AI Foundry** | Endpoint URL | Claude and GPT models via Azure AI Foundry | | **OpenAI-compatible** | API URL (+ optional API key) | Any OpenAI-compatible endpoint — use this for self-hosted models via llama.cpp, vLLM, Ollama, LM Studio, etc. | ### Predefined Models When you add a provider, the following models are automatically available: **Anthropic:** Claude Opus 5, Claude Sonnet 5, Claude Haiku 4.5 **OpenAI:** GPT-5.6, GPT-5.2, GPT-5 Mini **Google Gemini:** Gemini 3.6 Flash, Gemini 3.5 Flash, Gemini 3.5 Flash-Lite, Gemini 3.1 Pro **AWS Bedrock:** Claude Opus 5, Claude Sonnet 5, Claude Haiku 4.5, Amazon Nova 2 Lite **Vertex AI:** Claude Opus 5, Claude Sonnet 5, Gemini 3.6 Flash, Gemini 3.5 Flash **Azure AI Foundry:** No predefined models; specify the model ID exposed by your endpoint. **OpenAI-compatible:** No predefined models — specify the model ID exposed by your endpoint. Unlike the other provider types, **OpenAI-compatible** can be registered multiple times in a single instance — each configured endpoint (e.g. a local llama.cpp + a separate vLLM instance + an OpenRouter account) shows up as its own provider card in the admin panel, and its models become independently selectable per chat. ### AWS Bedrock Environment Variables Bedrock requests are made by the `omni-ai` service, so set these variables on `omni-ai`. | Variable | Required | Default | Description | | ----------------------- | ----------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `AWS_REGION` | Conditional | - | AWS region for Bedrock (e.g., `us-east-1`). Required for Bedrock embeddings and used as the fallback LLM region when the provider config does not specify one | | `AWS_DEFAULT_REGION` | Recommended | - | Standard AWS SDK region fallback. Set it to the same value as `AWS_REGION` for compatibility with AWS tooling and libraries | | `AWS_ACCESS_KEY_ID` | Conditional | - | AWS access key. Only use when not using an IAM role | | `AWS_SECRET_ACCESS_KEY` | Conditional | - | AWS secret key. Only use when not using an IAM role | | `AWS_SESSION_TOKEN` | Conditional | - | Session token when using temporary credentials | When running on EC2 or ECS with an appropriate IAM role, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN` are not needed — the AWS SDK uses the EC2 instance profile or ECS task role automatically. You should still set `AWS_REGION` on `omni-ai`; EC2 metadata does not export it as an environment variable inside your host or containers. For EC2 deployments using Docker, make sure the `omni-ai` container can reach the EC2 instance metadata service so the AWS SDK can retrieve role credentials. If IMDSv2 is enabled and credentials are not resolving inside containers, set the instance metadata response hop limit to `2`. The role attached to `omni-ai` must allow the Bedrock models you configure. At minimum, include: ```json theme={null} { "Effect": "Allow", "Action": [ "bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream", "bedrock:ListFoundationModels" ], "Resource": "*" } ``` Also enable access to the chosen Bedrock models in the AWS console for the same region before testing the provider in Omni. ### Vertex AI Environment Variables Vertex AI uses Google Cloud Application Default Credentials (ADC). Configure `region` and `project_id` in the admin panel. | Variable | Required | Default | Description | | -------------------------------- | ----------- | ------- | ------------------------------------------------------------------------------------------------------ | | `GOOGLE_APPLICATION_CREDENTIALS` | Conditional | - | Path to service account key JSON file (not needed when running on GCP with a service account attached) | When running on GKE, Cloud Run, or Compute Engine with an attached service account, ADC is automatic — no environment variables are needed. ### Azure AI Foundry Environment Variables Azure AI Foundry uses `DefaultAzureCredential` (Managed Identity). Configure the `endpoint_url` in the admin panel. When running on Azure (AKS, Container Apps, VMs) with Managed Identity configured, authentication is automatic. No additional environment variables are needed. ### Self-hosted Local Inference Omni ships a `docker-compose.local-inference.yml` overlay that starts a local `llama.cpp` container for LLM inference and (optionally) a HuggingFace TEI container for embeddings. Once running, register them as providers in the admin UI: `OpenAI-compatible` for the LLM pointed at `http://llama-cpp:${LOCAL_INFERENCE_MODEL_PORT}` and `Local` for the embedding provider. | Variable | Required | Default | Description | | ---------------------------- | -------- | ------- | ---------------------------------------------- | | `LOCAL_INFERENCE_MODEL_PORT` | No | `8000` | Port exposed by the `llama.cpp` container | | `LOCAL_EMBEDDINGS_PORT` | No | `8001` | Port exposed by the HuggingFace TEI container | | `LOCAL_EMBEDDINGS_MODEL` | No | - | HuggingFace model ID loaded by TEI | | `EMBEDDING_MAX_MODEL_LEN` | No | `8192` | Maximum context length for the embedding model | The local LLM and embedding containers can be enabled independently — for example, cloud LLM with a local embedding model, or vice versa. ## Web Search and Fetch Providers Public web providers are configured through **Settings** → **Web Providers** and stored encrypted in the database. They are optional and only affect agent tools; the [Web Connector](/connectors/web) is configured separately as a connector source. | Provider kind | Supported providers | Agent tool | | ------------- | ------------------------- | ---------------- | | **Search** | Exa, Serper, Brave Search | `web_search` | | **Fetch** | Exa, Firecrawl | `fetch_web_page` | Admins can connect multiple providers, set the current provider for each kind, override a provider base URL when needed, and maintain a URL blocklist. The fetch tool rejects localhost, private/internal IP addresses, and blocklisted domains or URL prefixes. ## Feature Flags | Variable | Required | Default | Description | | ------------------- | -------- | ------------------------- | ------------------------------------------------------------------- | | `AI_ANSWER_ENABLED` | No | `false` in `.env.example` | Enable or disable AI-generated answers in search results | | `AGENTS_ENABLED` | No | `false` | Enable background AI agents (scheduled tasks) | | `MEMORY_ENABLED` | No | `false` | Enable the Memory settings pages and AI-service memory provider | | `MEMORY_PROVIDER` | No | `mem0` | Memory backend provider. `mem0` is the currently supported provider | ## Memory Configuration Memory lets Omni recall selected context across future chats and agent runs. Set `MEMORY_ENABLED=true`, configure an embedding provider in the admin UI, then use **Settings** → **Memory** to choose the organization-wide default mode and memory LLM. | Mode | Behavior | | --------------- | -------------------------------------------------------------------------------- | | **Off** | Memory is disabled for users and agents | | **Chat memory** | Completed chat turns can be summarized into memories and recalled in later chats | | **Full memory** | Chat memory plus agent run context for background agents | The organization default is a ceiling: users can lower their personal memory level from **Settings** → **Memory**, but cannot choose a mode above the admin default. ## Background Agents Configuration Controls the background agent scheduler and execution limits. Requires `AGENTS_ENABLED=true`. | Variable | Required | Default | Description | | ------------------------------- | -------- | ------- | ------------------------------------- | | `AGENT_SCHEDULER_POLL_INTERVAL` | No | `30` | Seconds between scheduler poll checks | | `AGENT_MAX_CONCURRENT_RUNS` | No | `3` | Maximum concurrent agent executions | | `AGENT_MAX_ITERATIONS` | No | `15` | Maximum tool calls per agent run | ## AI Service Configuration | Variable | Required | Default | Description | | -------------------------- | -------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `AI_WORKERS` | No | `2` | Number of uvicorn worker processes | | `MODEL_PATH` | No | `/models` | Directory for model storage | | `APPROVAL_TIMEOUT_SECONDS` | No | `600` | How long interactive-chat tool approval prompts wait for a user decision before expiring (background agents don't use approval prompts) | Token usage for every LLM call (chat, agent run, compaction, title generation) is recorded in the `model_usage` table in Postgres, broken down by user, provider, and model. No additional configuration is required. ## Conversation Compaction Controls automatic compaction of long conversations to stay within model context limits. | Variable | Required | Default | Description | | ---------------------------------- | -------- | -------- | ------------------------------------------------------- | | `ENABLE_CONVERSATION_COMPACTION` | No | `true` | Enable or disable conversation compaction | | `MAX_CONVERSATION_INPUT_TOKENS` | No | `150000` | Maximum input tokens before compaction triggers | | `COMPACTION_RECENT_MESSAGES_COUNT` | No | `20` | Number of recent messages to preserve during compaction | | `COMPACTION_SUMMARY_MAX_TOKENS` | No | `2000` | Maximum tokens for the compaction summary | | `COMPACTION_CACHE_TTL_SECONDS` | No | `86400` | Cache TTL for compaction results (default: 24 hours) | ## Searcher Configuration | Variable | Required | Default | Description | | ---------------------------- | -------- | ------- | ---------------------------------------------------- | | `RAG_CONTEXT_WINDOW` | No | `2` | Number of surrounding chunks to fetch in RAG search | | `SEMANTIC_SEARCH_TIMEOUT_MS` | No | `1000` | Timeout for semantic (vector) search in milliseconds | | `RECENCY_BOOST_WEIGHT` | No | `0.2` | Weight for recency in search ranking (0.0–1.0) | | `RECENCY_HALF_LIFE_DAYS` | No | `30.0` | Days for document relevance to decay to 50% | ## Connector Manager The connector-manager service orchestrates all connector operations including scheduling syncs, health checks, and connector lifecycle management. | Variable | Required | Default | Description | | ------------------------------------------------ | -------- | ---------- | ---------------------------------------------------------------------------- | | `MAX_CONCURRENT_SYNCS` | No | `10` | Maximum concurrent syncs across all sources | | `MAX_CONCURRENT_SYNCS_PER_TYPE` | No | `3` | Maximum concurrent syncs per connector type | | `SCHEDULER_POLL_INTERVAL_SECONDS` | No | `60` | How often the scheduler checks for due syncs | | `STALE_SYNC_TIMEOUT_MINUTES` | No | `60` | Timeout to mark a sync as stale/failed | | `EXTRACTION_CONCURRENCY` | No | `2` | Maximum concurrent document extraction requests handled by connector-manager | | `EXTRACTION_RETRY_AFTER_SECONDS` | No | `30` | Retry delay advertised when extraction capacity is saturated | | `CONNECTOR_MANAGER_MAX_EXTRACT_INPUT_BYTES` | No | `52428800` | Maximum binary payload size accepted for extraction requests | | `CONNECTOR_MANAGER_MAX_EXTRACTED_TEXT_BYTES` | No | `5242880` | Maximum extracted text returned from connector-manager extraction requests | | `CONNECTOR_MANAGER_SPREADSHEET_MAX_INDEXED_ROWS` | No | `1000` | Maximum spreadsheet rows indexed through connector-manager extraction | ## Document Conversion (Docling) Docling is an optional service for extracting structured text from PDFs, Word documents, Excel files, PowerPoint, and common image formats. It can be toggled per-instance from **Settings** → **Document Conversion** in the admin UI. When disabled, Omni falls back to lightweight built-in extractors. The same admin page also exposes a **quality preset** that controls how aggressively Docling parses each file: | Preset | Behavior | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------- | | **Fast** | OCR off, fast table-former mode, no code/formula enrichment. Use for text-heavy docs where basic tables are fine. | | **Balanced** *(default)* | OCR off, accurate table-former mode. Best tradeoff for most deployments. | | **Quality** | OCR on, accurate table-former mode, 1.5× image scale, code and formula enrichment enabled. Slowest, but highest fidelity. | The preset is stored in Redis and picked up by indexers and connector-manager on every extraction — no restart needed. | Variable | Required | Default | Description | | ------------------------------------ | -------- | -------------------------------- | ------------------------------------------------------------------------------------------------------- | | `DOCLING_ENABLED` | No | `false` | Expose the Document Conversion admin page. Also set the Compose `docling` profile to deploy the service | | `DOCLING_URL` | No | `http://docling:${DOCLING_PORT}` | Docling service URL used by indexer and connector-manager extraction paths | | `DOCLING_DEVICE` | No | - | Leave empty for the CPU-only image; set to `cuda` to pull the CUDA-enabled image | | `DOCLING_MEMORY` | No | `2g` | Memory limit for the Docling container | | `DOCLING_MAX_CONCURRENT_CONVERSIONS` | No | `1` | Maximum concurrent conversions inside the Docling service | ## Storage Configuration | Variable | Required | Default | Description | | ----------------- | ----------- | ---------- | ------------------------------------------------- | | `STORAGE_BACKEND` | Yes | `postgres` | Storage backend: `postgres` or `s3` | | `S3_BUCKET` | Conditional | - | S3 bucket name (required if `STORAGE_BACKEND=s3`) | | `S3_REGION` | Conditional | - | S3 region (required if `STORAGE_BACKEND=s3`) | **PostgreSQL storage** (default): ```bash theme={null} STORAGE_BACKEND=postgres # Content stored directly in the database — simplest setup ``` **S3 storage**: ```bash theme={null} STORAGE_BACKEND=s3 S3_BUCKET=omni-content-prod S3_REGION=us-east-1 # Uses IAM role in AWS, or set AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY ``` ## Connector-Specific Configuration ### Google Workspace Connector | Variable | Required | Default | Description | | ---------------------------------------- | -------- | ---------- | --------------------------------------------------------- | | `WEBHOOK_RENEWAL_CHECK_INTERVAL_SECONDS` | No | `3600` | How often to check and renew Google Drive webhooks | | `GOOGLE_MAX_AGE_DAYS` | No | `730` | Maximum age of documents to index | | `GOOGLE_DRIVE_MAX_DOWNLOAD_BYTES` | No | `52428800` | Maximum bytes downloaded for a single Drive file | | `GOOGLE_DRIVE_PARALLEL_USERS` | No | `3` | Number of Drive users processed concurrently during sync | | `GOOGLE_WEBHOOK_DEBOUNCE_SECONDS` | No | `14400` | Debounce window for repeated Google webhook notifications | Google Drive webhook URLs are derived from `OMNI_DOMAIN` as `https:///google-webhook`. Webhooks are disabled automatically when `OMNI_DOMAIN=localhost`. ## Logging & Monitoring | Variable | Required | Default | Description | | ---------------- | -------- | ------- | ------------------------------------------------------------------ | | `RUST_LOG` | No | `info` | Rust services log level: `trace`, `debug`, `info`, `warn`, `error` | | `RUST_BACKTRACE` | No | - | Enable Rust backtraces: set to `1` or `full` for debugging | **Log level recommendations**: * **Development**: `RUST_LOG=debug` * **Production**: `RUST_LOG=info` * **Troubleshooting**: `RUST_LOG=trace` ## Telemetry (OpenTelemetry) | Variable | Required | Default | Description | | ----------------------------- | -------- | ------------ | ---------------------------------------------------- | | `OTEL_EXPORTER_OTLP_ENDPOINT` | No | - | OTLP collector endpoint (empty = telemetry disabled) | | `OTEL_DEPLOYMENT_ID` | No | - | Deployment identifier for tracing | | `OTEL_DEPLOYMENT_ENVIRONMENT` | No | `production` | Environment: `development`, `staging`, `production` | | `SERVICE_VERSION` | No | `0.1.0` | Service version for tracing | **Example with Honeycomb**: ```bash theme={null} OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io OTEL_EXPORTER_OTLP_HEADERS=x-honeycomb-team=your-api-key OTEL_DEPLOYMENT_ID=omni-prod-us-east-1 OTEL_DEPLOYMENT_ENVIRONMENT=production ``` # Docker Compose Deployment Source: https://docs.getomni.co/deployment/docker-compose Deploy Omni with Docker Compose for development or single-server production Docker Compose is the simplest deployment option, all services are deployed on a single node. For high availability, auto-scaling, or multi-region deployments, see [AWS Deployment with Terraform](/deployment/aws-terraform). ## Prerequisites * Linux/macOS/Windows with WSL * Docker 24.0+ with Compose v2 installed | Resource | Recommended | | -------- | ----------- | | CPU | 4 cores | | RAM | 8 GB | | Storage | 50 GB SSD | Memory and storage might need to be adjusted depending on the total data indexed. ## Step 1: Download Docker Compose Configuration ```bash theme={null} # Create project directory mkdir omni && cd omni # Download the latest release # See https://github.com/getomnico/omni/releases for available versions curl -fsSL "https://github.com/getomnico/omni/releases/latest/download/omni-docker-compose.tar.gz" \ -o omni-docker-compose.tar.gz tar xzf omni-docker-compose.tar.gz rm omni-docker-compose.tar.gz ``` To download a specific version, replace `latest/download` with `download/v0.1.9` in the URL above. ## Step 2: Setup Environment ```bash theme={null} cp .env.example .env ``` Edit `.env` and update the following variables: ```bash theme={null} # Database (generate a secure password, e.g., openssl rand -base64 32) DATABASE_PASSWORD= # Generate encryption key # E.g., # For the key: openssl rand -hex 16 # For the salt: openssl rand -hex 8 ENCRYPTION_KEY= # Must be at least 32 characters ENCRYPTION_SALT= # Must be 16 characters # Application OMNI_DOMAIN= APP_URL=https:// ACME_EMAIL= # Enabled Connectors # Specify connector names as a comma-separated string # Available: google, google_ads, slack, atlassian, web, github, notion, hubspot, # fireflies, microsoft, filesystem, imap, linear, clickup, nextcloud, # paperless, darwinbox, windshift, salesforce ENABLED_CONNECTORS=google,slack,web ``` See [Configuration Reference](/deployment/configuration) for all options. ## Step 3: Start Services For convenience, define an alias: ```bash theme={null} alias omni-compose="docker compose -f docker/docker-compose.yml --env-file .env" ``` Start Omni: ```bash theme={null} omni-compose up -d ``` Monitor startup: ```bash theme={null} omni-compose logs -f omni-compose ps # all services should show "healthy" ``` Access at `https://`. First user becomes admin. Once all services are healthy, follow the [Initial Setup](/deployment/initial-setup) guide to configure LLM providers, embeddings, and connectors. ### Self-hosted Local Inference To run LLMs and/or embedding models locally alongside Omni, use the `docker-compose.local-inference.yml` overlay. It starts: * A `llama.cpp` container serving an OpenAI-compatible LLM endpoint * A HuggingFace TEI container serving an embedding endpoint Start with local inference enabled: ```bash theme={null} omni-compose -f docker/docker-compose.local-inference.yml up -d ``` Then in the admin panel, add an **OpenAI-compatible** LLM provider pointing at `http://llama-cpp:${LOCAL_INFERENCE_MODEL_PORT}`, and (optionally) the **Local** embedding provider at `http://embeddings:${LOCAL_EMBEDDINGS_PORT}/v1`. The two containers are independent, so you can mix a cloud LLM with a local embedding model, or vice versa. GPU acceleration can be enabled by editing the overlay to pass through the appropriate device. ### Document Conversion (Docling) Docling is optional. It provides higher-fidelity extraction for PDFs, Office documents, HTML/Markdown, CSV, LaTeX/AsciiDoc, and common image formats. To deploy the service with Docker Compose: ```bash theme={null} # In .env DOCLING_ENABLED=true # Start the optional service profile omni-compose --profile docling up -d ``` You can also add `docling` to `COMPOSE_PROFILES` in `.env` if you want the service to start with normal `omni-compose up -d` runs. Once the service is running, use **Settings** → **Document Conversion** to turn Docling extraction on or off and choose a quality preset. `DOCLING_ENABLED=true` exposes the Document Conversion settings page in the admin UI. The Compose `docling` profile deploys the actual `omni-docling` container. ## Stopping Services To stop all services: ```bash theme={null} omni-compose down ``` To stop and remove all data (including the database): ```bash theme={null} omni-compose down -v ``` The `-v` flag will permanently delete all indexed data and settings. ## Troubleshooting Check Docker logs for errors: ```bash theme={null} omni-compose logs ``` Common causes: * Port 3000 already in use * Missing environment variables Verify the web service is running: ```bash theme={null} omni-compose ps omni-web ``` Check if the port is accessible: ```bash theme={null} curl http://localhost:3000 ``` Ensure PostgreSQL is fully started: ```bash theme={null} omni-compose logs postgres ``` Wait for the message: "database system is ready to accept connections" Check your LLM provider configuration in the admin panel: * Confirm whether you set the API keys * Review AI service logs: ```bash theme={null} omni-compose logs omni-ai ``` ## Next Steps Configure LLMs, embeddings, and connectors Google, Slack, Confluence, etc. All environment variables Add users and permissions # Initial Setup Source: https://docs.getomni.co/deployment/initial-setup Configure LLM providers, embeddings, and connectors after deploying Omni After deploying Omni via [Docker Compose](/deployment/docker-compose) or [AWS Terraform](/deployment/aws-terraform), follow these steps to get your instance fully operational. ## Step 1: Access the Web UI and Create Your Admin Account Navigate to your Omni URL (e.g., `https://`) and sign up. The first user to register automatically becomes the admin. Admin users have access to **Settings** in the sidebar, where you can configure LLM providers, manage connectors, and invite users. ## Step 2: Configure an LLM Provider LLM providers are configured through the admin panel at **Settings > LLM Providers**. You can add multiple providers and users can select which model to use per chat. ### Add a Provider 1. Go to **Settings > LLM Providers** 2. Click **Connect** against the provider you wish to, well, connect 3. Enter the API key/credentials 4. Omni validates the provider by making a lightweight test call before saving 5. Click **Connect** to save; the provider's predefined or discovered models become available automatically You can add multiple providers simultaneously. For example, use Anthropic for complex reasoning and a local `llama.cpp` model (via the OpenAI-compatible provider) for sensitive queries. ### AWS Bedrock Notes Bedrock calls are made by the `omni-ai` service. When running Omni on EC2 or ECS, attach an IAM role to the EC2 instance or ECS task that runs `omni-ai`; do not use long-lived access keys unless you cannot use an IAM role. Set the Bedrock region explicitly on `omni-ai`: ```bash theme={null} AWS_REGION=us-east-1 AWS_DEFAULT_REGION=us-east-1 ``` EC2 instance metadata provides IAM role credentials, but it does not automatically export `AWS_REGION` inside your host or containers. `AWS_REGION` is required for Bedrock embeddings and is used as the fallback LLM region when the provider's AWS Region field is left blank in **Settings > LLM Providers**. The `omni-ai` role needs permission to use the models you configure: ```json theme={null} { "Effect": "Allow", "Action": [ "bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream", "bedrock:ListFoundationModels" ], "Resource": "*" } ``` Before testing the provider in Omni, enable access to the selected Bedrock models in the AWS console for the same region. If you run Docker containers on EC2 with an instance profile, ensure containers can reach the EC2 metadata service; for IMDSv2 this may require setting the instance metadata response hop limit to `2`. If you are not using an IAM role, set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` on `omni-ai` instead. Add `AWS_SESSION_TOKEN` too when using temporary credentials. ### Vertex AI Notes Vertex AI uses Google Cloud Application Default Credentials (ADC). In the admin panel, configure the GCP `region` and `project_id`. When running on GCP (GKE, Cloud Run, Compute Engine) with an attached service account, authentication is automatic. For other environments, set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable pointing to a service account key JSON file. ### Azure AI Foundry Notes Azure AI Foundry uses `DefaultAzureCredential` (Managed Identity). In the admin panel, configure the `endpoint_url` of your Azure AI Foundry deployment. When running on Azure (AKS, Container Apps) with Managed Identity, authentication is automatic. ### Self-hosted Models (OpenAI-compatible) Omni ships an `docker-compose.local-inference.yml` overlay that starts a local `llama.cpp` server for LLMs and, optionally, a HuggingFace TEI server for embeddings. Start them with: ```bash theme={null} omni-compose -f docker/docker-compose.local-inference.yml up -d ``` Then in the admin panel: * Add an **OpenAI-compatible** LLM provider pointing to `http://llama-cpp:${LOCAL_INFERENCE_MODEL_PORT}` * (Optional) Add the **Local** embedding provider pointing to `http://embeddings:${LOCAL_EMBEDDINGS_PORT}/v1` The local LLM and embedding containers can be enabled independently — you can mix cloud LLMs with a local embedding model, or vice versa. See [LLM Provider Configuration](/deployment/configuration#llm-provider-configuration) for all environment variable details. ## Step 3: Configure an Embedding Provider Embeddings power Omni's semantic search. Like LLM providers, embedding providers are configured through the admin panel. ### Add a Provider 1. Go to **Settings > Embedding Providers** 2. Click **Connect** against the provider you wish to use 3. Enter the API key/credentials 4. Click **Connect** to save Changing the embedding provider after documents have been indexed will require a full re-index, since different providers produce incompatible vector representations. ## Step 4: Configure Web Providers (Optional) If you want the AI assistant and background agents to search or fetch public web pages, configure web providers from **Settings > Web Providers**. | Provider kind | Supported providers | Used by | | ------------- | ------------------------- | --------------------------- | | **Search** | Exa, Serper, Brave Search | `web_search` agent tool | | **Fetch** | Exa, Firecrawl | `fetch_web_page` agent tool | You can connect multiple providers and mark one search provider and one fetch provider as current. The same page includes a URL blocklist for domains or URL prefixes that agents should never fetch. These web providers are separate from the [Web Connector](/connectors/web). The connector indexes a configured website into Omni's internal search index; web providers let agents query or fetch public web content live during a chat or agent run. ## Step 5: Enable and Configure Connectors Connectors sync data from your external tools into Omni. ### Enable Connectors Set the `ENABLED_CONNECTORS` environment variable to a comma-separated list of the connectors you want to run: ```bash theme={null} # Example: enable Google Workspace, Slack, and Atlassian ENABLED_CONNECTORS=google,slack,atlassian ``` Available connector names: `google`, `google_ads`, `slack`, `atlassian`, `github`, `hubspot`, `notion`, `filesystem`, `fireflies`, `web`, `microsoft`, `imap`, `clickup`, `linear`, `nextcloud`, `paperless`, `darwinbox`, `windshift`, `salesforce` Restart services after changing this variable (`omni-compose up -d`). ### Configure Each Connector Once enabled, connectors are configured in the admin panel: 1. Go to **Settings > Integrations** 2. Select the connector you want to configure 3. Follow the setup instructions (typically involves providing service credentials or API keys) 4. Start the initial sync Drive, Docs, Sheets, Gmail OneDrive, SharePoint, Outlook, Calendar Messages, threads, files Jira issues, Confluence pages CRM records with permissions and MCP tools Full list and setup guides ## Step 6: Verify Your Setup Once you've configured at least one LLM provider, an embedding provider, and a connector, verify everything is working. ### Check Connector Sync Status Go to **Settings > Integrations** and confirm your connectors show a successful sync status. The initial sync may take a few minutes depending on the volume of data. ### Test Search 1. Navigate to the **Search** page 2. Enter a query related to content from your connected sources 3. Verify that results appear and are relevant ### Test the AI Assistant 1. Navigate to the **Chat** page 2. Ask a question about information in your connected sources 3. Verify the assistant responds with an answer that cites your documents If search returns results but the AI assistant doesn't work, double-check your LLM provider configuration in **Settings > LLM Providers**. ## Next Steps * [Configuration Reference](/deployment/configuration) — Full list of environment variables and options * [User Management](/admin/user-management) — Invite team members and manage roles * [Connector Management](/admin/connector-management) — Monitor sync status and schedules # Deployment Overview Source: https://docs.getomni.co/deployment/overview Choose the right deployment strategy for your needs Omni supports two deployment strategies: Docker Compose and AWS Terraform. For Docker Compose deployments, the [Omni CLI](/deployment/cli) provides upgrades, diagnostics, backups, and Compose command helpers. ## Deployment Options Development, small teams, single-server production. Get running in 10-15 minutes. Upgrade, diagnose, and back up Docker Compose deployments. High availability, auto-scaling, multi-region. Production-ready in 30-45 minutes. After deploying, follow the [Initial Setup](/deployment/initial-setup) guide to configure LLM providers, embeddings, and connectors. ## Omni Components All deployments include the same core components: | Component | Purpose | | -------------------------- | --------------------------------------------------------------------- | | **omni-web** | SvelteKit frontend and API | | **omni-searcher** | Search query processing | | **omni-indexer** | Document processing | | **omni-ai** | LLM and embedding orchestration | | **omni-connector-manager** | Orchestrate connector services | | **omni-sandbox** | Isolated execution environment for agent tools (bash, Python, files) | | **omni-docling** | Optional structured text extraction for PDFs, Office docs, and images | | **PostgreSQL** | Database with pg\_search and pgvector | | **Redis** | Caching and sessions | Each connector runs in its own Docker container. Connectors are typically very lightweight services whose only job is to act as an interface between Omni and the third-party APIs. For reference, the built-in Google connector uses \~5MB memory at idle, and \~50MB during indexing. # Python SDK Source: https://docs.getomni.co/developers/python-sdk Build custom connectors with Python and FastAPI The Python SDK (`omni-connector`) provides everything you need to build custom connectors that integrate with Omni's search and AI platform. ## Installation The SDK is **not published to PyPI**. Connectors live in the Omni monorepo and depend on the SDK as a local path dependency. To start a new Python connector, fork or clone [omni](https://github.com/getomnico/omni) and create a directory under `connectors/`: ```bash theme={null} git clone https://github.com/getomnico/omni.git cd omni/connectors mkdir my-connector && cd my-connector ``` Add a `pyproject.toml` that pulls the SDK from the local checkout — copy from any existing Python connector (e.g. `connectors/notion/pyproject.toml`): ```toml theme={null} [project] name = "my-connector" version = "1.0.0" requires-python = ">=3.11" dependencies = [ "omni-connector", ] [tool.uv.sources] omni-connector = { path = "../../sdk/python" } ``` Then install with `uv`: ```bash theme={null} uv sync ``` **Requirements:** * Python >= 3.11 * [uv](https://docs.astral.sh/uv/) (used throughout the monorepo for Python dependency management) **Transitive dependencies pulled in by the SDK:** * `fastapi` - Web framework * `uvicorn` - ASGI server * `httpx` - Async HTTP client * `pydantic` - Data validation Run `/build-connector ` from Claude Code inside the omni repo to scaffold the entire connector structure (sync logic, manifest, Dockerfile, frontend wiring, Terraform, integration tests) following the conventions used by built-in connectors. See the [SDK overview](/developers/sdk-overview#scaffolding-with-the-build-connector-skill) for details. ## Quick Start ```python theme={null} from omni_connector import ( Connector, Document, DocumentMetadata, DocumentPermissions, SyncContext, ) class MyConnector(Connector): @property def name(self) -> str: return "my-connector" @property def version(self) -> str: return "1.0.0" @property def source_types(self) -> list[str]: return ["my_source_type"] @property def sync_modes(self) -> list[str]: return ["full", "incremental"] async def sync( self, source_config: dict, credentials: dict, checkpoint: dict | None, ctx: SyncContext ) -> None: # Your sync logic here await ctx.complete() if __name__ == "__main__": MyConnector().serve(port=8000) ``` When running under Docker, set `CONNECTOR_MANAGER_URL`, `CONNECTOR_HOST_NAME`, and `PORT`. The SDK uses `CONNECTOR_HOST_NAME` plus `PORT` to register the connector URL that connector-manager calls for syncs, actions, MCP resources, and prompts. ## Core Concepts ### Connector Class The `Connector` abstract base class defines the interface for all connectors. **Required Properties:** | Property | Type | Description | | -------------- | ----------- | ---------------------------------------------------- | | `name` | `str` | Unique connector identifier (e.g., `"my-connector"`) | | `version` | `str` | Semantic version (e.g., `"1.0.0"`) | | `source_types` | `list[str]` | Source types the connector handles | **Optional Properties:** | Property | Type | Default | Description | | ------------ | ----------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `sync_modes` | `list[str]` | `["full"]` | Supported modes: `"full"`, `"incremental"` | | `actions` | `list[ActionDefinition]` | `[]` | Custom actions the connector supports. Set `hidden=True` on an action definition to keep internal/setup actions out of chat tools. | | `skills` | `list[ConnectorSkillDefinition]` | `[]` | Connector-owned skills that guide the Omni agent through connector-specific workflows | | `mcp_server` | `StdioMcpServer \| HttpMcpServer \| None` | `None` | MCP server that exposes tools to Omni | **Required Methods:** ```python theme={null} async def sync( self, source_config: dict, # Configuration from Omni UI credentials: dict, # Stored credentials (API keys, tokens) checkpoint: dict | None, # Latest successful or per-run checkpoint ctx: SyncContext # Sync context utilities ) -> None: """Execute a sync operation.""" pass ``` **Optional Methods:** ```python theme={null} def cancel(self, sync_run_id: str) -> None: """Handle sync cancellation request.""" pass async def execute_action( self, action: str, params: dict, credentials: dict ) -> dict: """Execute a custom action.""" pass async def oauth_credential_ready( self, request: OAuthCredentialReadyRequest ) -> None: """Called after OAuth credentials are stored for a connector. Use this to run post-OAuth setup such as authenticated MCP catalog discovery.""" pass ``` ### SyncContext The `SyncContext` object is passed to your `sync` method and provides utilities for the sync operation. **Properties:** | Property | Type | Description | | ------------------- | ---------------- | ---------------------------------------------------------------------------- | | `sync_run_id` | `str` | Unique identifier for this sync run | | `source_id` | `str` | Source identifier | | `source_type` | `str \| None` | Source type dispatched by connector-manager | | `checkpoint` | `dict` | Latest successful checkpoint, or the latest per-run checkpoint when resuming | | `connector_state` | `dict` | Durable source-level metadata that is not used as the sync cursor | | `sync_mode` | `SyncMode` | Dispatched mode: `full`, `incremental`, or `realtime` | | `is_resume` | `bool` | Whether this run was resumed after an interruption | | `content_storage` | `ContentStorage` | Storage for document content | | `documents_emitted` | `int` | Count of emitted documents, seeded from the running sync on resume | | `documents_scanned` | `int` | Count of scanned items, seeded from the running sync on resume | **Methods:** ```python theme={null} # Emit a new document await ctx.emit(document: Document) # Emit an updated document await ctx.emit_updated(document: Document) # Mark a document as deleted await ctx.emit_deleted(external_id: str) # Emit source group membership for permission filtering await ctx.emit_group_membership( group_email: str, member_emails: list[str], group_name: str | None = None, ) # Report a non-fatal error for a document await ctx.emit_error(external_id: str, error: str) # Increment scanned counter (also sends heartbeat) await ctx.increment_scanned() # Check source-level user allow/deny settings before emitting user-owned docs ctx.should_index_user(user_email: str) -> bool # Save a per-run checkpoint for resumability await ctx.save_checkpoint(checkpoint: dict) # Persist source-level metadata that is not the resume cursor await ctx.save_connector_state(connector_state: dict) # Deprecated alias for save_checkpoint await ctx.save_state(checkpoint: dict) # Mark sync as completed, optionally saving the final checkpoint first await ctx.complete(checkpoint: dict | None = None) # Mark sync as failed await ctx.fail(error: str) # Check if sync was cancelled ctx.is_cancelled() -> bool ``` ### ContentStorage The `ContentStorage` class handles storing document content. ```python theme={null} # Store text content content_id = await ctx.content_storage.save( content: str, content_type: str = "text/plain" ) -> str # Store binary content (base64 encoded) content_id = await ctx.content_storage.save_binary( content: bytes, content_type: str ) -> str # Extract text from a binary file (PDF, DOCX, PPTX, XLSX, images, ...) # via connector-manager's /sdk/extract-text endpoint, without persisting. # Returns the extracted text. text = await ctx.content_storage.extract_text( data: bytes, mime_type: str, filename: str, ) -> str # Same as extract_text, but also stores the extracted text and returns # the resulting content_id. content_id = await ctx.content_storage.extract_and_store_content( data: bytes, mime_type: str, filename: str, ) -> str ``` The returned `content_id` is a ULID that references the stored content. Always prefer `extract_text` / `extract_and_store_content` over shelling out to your own PDF/Office parser. The connector-manager routes these calls through the centralized Docling service when the admin has enabled it (with the configured quality preset), and falls back to a lightweight built-in extractor otherwise — so your connector automatically honors the instance-wide document-conversion setting. ### Document Model The `Document` class represents a searchable document. ```python theme={null} from omni_connector import Document, DocumentMetadata, DocumentPermissions document = Document( external_id="unique-id-from-source", title="Document Title", content_id=content_id, # From content_storage.save() metadata=DocumentMetadata( title="Document Title", author="Author Name", created_at=datetime.now(), updated_at=datetime.now(), mime_type="text/plain", size=1234, url="https://source.com/doc/123", path="/folder/document.txt", extra={"custom_field": "value"}, ), permissions=DocumentPermissions( public=True, users=["user@example.com"], groups=["engineering"], ), attributes={"category": "documentation"}, ) ``` **Document Fields:** | Field | Type | Required | Description | | ------------- | --------------------- | -------- | -------------------------------- | | `external_id` | `str` | Yes | Unique ID from the source system | | `title` | `str` | Yes | Document title for display | | `content_id` | `str` | Yes | Reference to stored content | | `metadata` | `DocumentMetadata` | No | Document metadata | | `permissions` | `DocumentPermissions` | No | Access control | | `attributes` | `dict` | No | Custom searchable attributes | ### Actions Connectors can define custom actions that users can trigger from the Omni UI. ```python theme={null} from omni_connector import ActionDefinition, ActionParameter class MyConnector(Connector): @property def actions(self) -> list[ActionDefinition]: return [ ActionDefinition( name="validate_connection", description="Test the API connection", parameters=[ ActionParameter( name="timeout", type="integer", required=False, description="Connection timeout in seconds", ), ], ), ] async def execute_action( self, action: str, params: dict, credentials: dict ) -> dict: if action == "validate_connection": # Perform validation return {"status": "success", "message": "Connection valid"} raise ValueError(f"Unknown action: {action}") ``` ## Connector Skills Connectors can ship skills — Markdown documents that guide the Omni agent through connector-specific workflows. Skills are loaded into Omni when the connector is enabled. ```python theme={null} from omni_connector import ConnectorSkillDefinition from pathlib import Path class MyConnector(Connector): @property def skills(self) -> list[ConnectorSkillDefinition]: return [ ConnectorSkillDefinition( id="my-tool-guide", title="My Tool Guide", description="Best practices for using My Tool", source_types=["my_source_type"], content=Path(__file__).parent.joinpath( "skills/my-tool.md" ).read_text(), ), ] ``` | Field | Type | Required | Description | | -------------- | ----------- | -------- | ---------------------------------------------------------------------------- | | `id` | `str` | Yes | Unique skill identifier | | `title` | `str` | Yes | Human-readable skill name | | `description` | `str` | No | Short summary shown in the skill list | | `source_types` | `list[str]` | No | Source types the skill applies to | | `content` | `str` | No | Markdown content loaded into the assistant | | `mcp_prompt` | `str` | No | Name of an MCP prompt to use as the skill content instead of inline Markdown | ## MCP OAuth and Catalog Cache ### Dynamic Client Registration (DCR) For connectors whose MCP servers require per-user OAuth, `OAuthManifestConfig` supports automatic client registration so admins don't need to create OAuth apps manually: ```python theme={null} def oauth_config(self) -> OAuthManifestConfig | None: return OAuthManifestConfig( auth_endpoint="https://api.example.com/oauth/authorize", token_endpoint="https://api.example.com/oauth/token", registration_endpoint="https://api.example.com/oauth/register", token_endpoint_auth_method="none", # Public DCR client resource="https://mcp.example.com", # RFC 8707 resource indicator scope_sets={ "read": OAuthScopeSet( read=["tasks:read"], write=["tasks:write"], ), }, ) ``` | Field | Description | | ---------------------------- | -------------------------------------------------------------------------------------------------------- | | `registration_endpoint` | OAuth Dynamic Client Registration endpoint — Omni auto-creates a client instead of requiring admin setup | | `token_endpoint_auth_method` | `"client_secret_post"`, `"client_secret_basic"`, or `"none"` (public DCR clients) | | `resource` | RFC 8707 resource indicator, sent on auth/token requests (e.g., a remote MCP server URL) | ### Credential-Ready Lifecycle After a user completes OAuth, connector-manager calls `oauth_credential_ready` so the connector can run post-OAuth setup such as authenticated MCP catalog discovery: ```python theme={null} from omni_connector import OAuthCredentialReadyRequest class MyConnector(Connector): async def oauth_credential_ready( self, request: OAuthCredentialReadyRequest ) -> None: # Discover MCP tools now that credentials are available self._load_mcp_catalog_cache(self.mcp_adapter) ``` ### Catalog Cache The SDK caches MCP tool catalogs to disk (default: `/var/lib/omni/mcp-catalogs`) to avoid costly discovery on every restart. Set `CATALOG_CACHE_TTL_SECONDS` to control the cache lifetime; it defaults to `86400` seconds (24 hours). ```bash theme={null} CATALOG_CACHE_TTL_SECONDS=3600 ``` Set a shorter TTL for MCP servers whose tool sets change frequently, or a larger value for stable tool catalogs. Here's a complete example of an RSS feed connector: ```python theme={null} import asyncio from datetime import datetime from typing import Any import feedparser from omni_connector import ( ActionDefinition, ActionParameter, Connector, Document, DocumentMetadata, DocumentPermissions, SyncContext, ) class RSSConnector(Connector): """Connector that indexes RSS/Atom feeds.""" @property def name(self) -> str: return "rss" @property def version(self) -> str: return "1.0.0" @property def sync_modes(self) -> list[str]: return ["full", "incremental"] @property def actions(self) -> list[ActionDefinition]: return [ ActionDefinition( name="validate_feed", description="Validate that a feed URL is accessible", parameters=[ ActionParameter( name="url", type="string", required=True, description="The RSS/Atom feed URL to validate", ), ], ), ] async def sync( self, source_config: dict[str, Any], credentials: dict[str, Any], checkpoint: dict[str, Any] | None, ctx: SyncContext, ) -> None: """Sync articles from an RSS feed.""" feed_url = source_config.get("feed_url") if not feed_url: await ctx.fail("Missing feed_url in source configuration") return # Parse the feed feed = await asyncio.to_thread(feedparser.parse, feed_url) if feed.bozo and not feed.entries: await ctx.fail(f"Failed to parse feed: {feed.bozo_exception}") return # Get last sync time for incremental sync last_sync_time = None if checkpoint and "last_sync_time" in checkpoint: last_sync_time = datetime.fromisoformat(checkpoint["last_sync_time"]) for entry in feed.entries: if ctx.is_cancelled(): return await ctx.increment_scanned() # Skip old entries in incremental mode if last_sync_time and hasattr(entry, "published_parsed"): entry_date = datetime(*entry.published_parsed[:6]) if entry_date <= last_sync_time: continue # Build content from entry content = self._build_content(entry) content_id = await ctx.content_storage.save(content) # Create document doc = Document( external_id=entry.get("id", entry.link), title=entry.get("title", "Untitled"), content_id=content_id, metadata=DocumentMetadata( author=entry.get("author"), url=entry.get("link"), created_at=self._parse_date(entry), extra={ "feed_title": feed.feed.get("title"), "feed_url": feed_url, }, ), permissions=DocumentPermissions(public=True), ) await ctx.emit(doc) # Complete with updated checkpoint await ctx.complete({"last_sync_time": datetime.utcnow().isoformat()}) def _build_content(self, entry) -> str: """Extract content from feed entry.""" if hasattr(entry, "content") and entry.content: return entry.content[0].get("value", "") if hasattr(entry, "summary"): return entry.summary return entry.get("title", "") def _parse_date(self, entry) -> datetime | None: """Parse date from feed entry.""" if hasattr(entry, "published_parsed") and entry.published_parsed: return datetime(*entry.published_parsed[:6]) return None async def execute_action( self, action: str, params: dict[str, Any], credentials: dict[str, Any], ) -> dict[str, Any]: if action == "validate_feed": url = params.get("url") if not url: return {"status": "error", "error": "Missing URL parameter"} feed = await asyncio.to_thread(feedparser.parse, url) if feed.bozo and not feed.entries: return {"status": "error", "error": str(feed.bozo_exception)} return { "status": "success", "result": { "title": feed.feed.get("title"), "entry_count": len(feed.entries), }, } return {"status": "error", "error": f"Unknown action: {action}"} if __name__ == "__main__": RSSConnector().serve(port=8000) ``` ## Error Handling The SDK provides custom exception classes: ```python theme={null} from omni_connector import ( ConnectorError, # Base exception SdkClientError, # Communication errors SyncCancelledError, # Sync was cancelled ConfigurationError, # Configuration issues ) ``` **Handling errors in sync:** ```python theme={null} async def sync(self, source_config, credentials, checkpoint, ctx): try: # Your sync logic await ctx.complete() except SyncCancelledError: # Handle cancellation gracefully return except Exception as e: await ctx.fail(str(e)) ``` ## Development ### Project Setup ```bash theme={null} # From your connector directory inside the omni monorepo uv sync ``` ### Running Tests ```bash theme={null} uv run pytest tests/ -v ``` ### Type Checking ```bash theme={null} uv run mypy my_connector/ ``` ### Linting ```bash theme={null} uv run ruff check my_connector/ ``` ## Testing The SDK includes a testing module (`omni_connector.testing`) with utilities for writing integration tests for your connectors. ### TestHarness The `TestHarness` class provides a self-contained environment for testing your connector without needing a running Omni deployment. ```python theme={null} import pytest from omni_connector.testing import TestHarness from my_connector import MyConnector @pytest.fixture def harness(): return TestHarness(MyConnector()) @pytest.mark.asyncio async def test_full_sync(harness): # Seed test data harness.set_source_config({"api_url": "https://api.example.com"}) harness.set_credentials({"api_key": "test-key"}) # Run sync result = await harness.run_sync() # Assert results assert result.status == "completed" assert result.documents_emitted > 0 @pytest.mark.asyncio async def test_incremental_sync(harness): # Run initial sync harness.set_source_config({"api_url": "https://api.example.com"}) harness.set_credentials({"api_key": "test-key"}) first_result = await harness.run_sync() # Run incremental sync with checkpoint from first run second_result = await harness.run_sync(checkpoint=first_result.checkpoint) assert second_result.status == "completed" ``` ### Assertions The harness captures all emitted documents, errors, and checkpoint changes for easy assertion: ```python theme={null} async def test_document_content(harness): harness.set_source_config({"feed_url": "https://example.com/feed"}) result = await harness.run_sync() # Check emitted documents assert len(result.documents) == 5 assert result.documents[0].title == "Expected Title" # Check for errors assert len(result.errors) == 0 # Check final checkpoint assert "last_sync_time" in result.checkpoint ``` ## API Reference ### Exports The SDK exports the following: **Core Classes:** * `Connector` - Abstract base class * `SyncContext` - Sync operation context * `ContentStorage` - Content storage interface * `SdkClient` - SDK client for connector-manager **Data Models:** * `Document`, `DocumentMetadata`, `DocumentPermissions` * `ConnectorEvent`, `EventType` * `ActionDefinition`, `ActionParameter` * `ActionRequest`, `ActionResponse` * `ConnectorManifest`, `SyncMode` * `SyncRequest`, `SyncResponse` * `CancelRequest`, `CancelResponse` * `ConnectorSkillDefinition` * `OAuthManifestConfig`, `OAuthScopeSet` * `OAuthCredentialReadyRequest` * `McpResourceDefinition`, `McpPromptDefinition` * `StdioMcpServer`, `HttpMcpServer` **Exceptions:** * `ConnectorError`, `SdkClientError`, `SyncCancelledError`, `ConfigurationError` ## What's Next Learn about SDK architecture Build connectors with TypeScript # Rust SDK Source: https://docs.getomni.co/developers/rust-sdk Build custom connectors in Rust with Axum and the Omni connector SDK The Rust SDK (`omni-connector-sdk`) is used by many built-in Omni connectors, including Google Workspace, Slack, Atlassian, IMAP, Nextcloud, Filesystem, Web, and Darwinbox. Use it when you want a native Rust connector with typed configuration, high-throughput sync logic, or direct reuse of Omni's shared Rust models. ## Installation The Rust SDK is **not published to crates.io**. Connectors live in the Omni monorepo and depend on the SDK as a local path dependency. Create a connector under `connectors/` and add a `Cargo.toml` like: ```toml theme={null} [package] name = "omni-my-connector" version = "0.1.0" edition = "2024" [dependencies] omni-connector-sdk = { path = "../../sdk/rust" } anyhow = "1" async-trait = "0.1" serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1", features = ["full"] } ``` The connector should be part of the Omni workspace so it is built, tested, and released with the matching SDK and shared model versions. ## Server Startup The SDK provides an Axum server with the standard connector endpoints and a registration loop that advertises the connector manifest to connector-manager every 30 seconds. ```rust theme={null} use anyhow::Result; use omni_connector_sdk::serve; use my_connector::MyConnector; #[tokio::main] async fn main() -> Result<()> { serve(MyConnector).await } ``` When running under Docker, set: | Variable | Purpose | | ----------------------- | ------------------------------------------------------------------------------------ | | `CONNECTOR_MANAGER_URL` | URL of omni-connector-manager | | `CONNECTOR_HOST_NAME` | Hostname connector-manager should call back, usually the Docker Compose service name | | `PORT` | Port the connector server binds and registers | ## Connector Trait Implement `omni_connector_sdk::Connector` for your connector type. ```rust theme={null} use anyhow::{Context, Result}; use async_trait::async_trait; use omni_connector_sdk::{ Connector, ServiceCredential, Source, SourceType, SyncContext, SyncType, }; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Deserialize, Serialize)] struct MyConfig { api_url: String, } #[derive(Debug, Clone, Deserialize, Serialize)] struct MyCredentials { api_key: String, } #[derive(Debug, Clone, Default, Deserialize, Serialize)] struct MyCheckpoint { cursor: Option, } struct MyConnector; #[async_trait] impl Connector for MyConnector { type Config = MyConfig; type Credentials = MyCredentials; type State = MyCheckpoint; fn name(&self) -> &'static str { "my_connector" } fn version(&self) -> &'static str { env!("CARGO_PKG_VERSION") } fn display_name(&self) -> String { "My Connector".to_string() } fn source_types(&self) -> Vec { vec![SourceType::Web] } fn sync_modes(&self) -> Vec { vec![SyncType::Full, SyncType::Incremental] } async fn sync( &self, source: Source, credentials: Option, checkpoint: Option, ctx: SyncContext, ) -> Result<()> { let config: MyConfig = serde_json::from_value(source.config) .context("failed to decode source config")?; let creds = credentials.context("credentials are required")?; let creds: MyCredentials = serde_json::from_value(creds.credentials) .context("failed to decode credentials")?; run_sync(config, creds, checkpoint.unwrap_or_default(), ctx).await } } ``` ### Associated Types | Associated type | Description | | --------------- | ------------------------------------------------------------------------- | | `Config` | Shape of `source.config`; the SDK validates it before dispatching `/sync` | | `Credentials` | Shape of `service_credentials.credentials`; validated before dispatch | | `State` | Checkpoint type passed to `sync` and serialized by `save_checkpoint` | Use `serde_json::Value` for any associated type when you want to opt out of typed validation. ## SyncContext `SyncContext` is the connector's handle back to connector-manager. | Method | Purpose | | ------------------------------------------------------- | ------------------------------------------------------------------------------- | | `sync_run_id()` / `source_id()` / `source_type()` | Identify the current sync and source | | `sync_mode()` | Current mode: full, incremental, or realtime | | `is_resume()` | Whether this run resumed after interruption | | `is_cancelled()` | Whether cancellation was requested | | `store_content(content)` | Store extracted text and return a content ID | | `extract_and_store_content(data, mime_type, filename)` | Extract text from binary content through connector-manager/Docling and store it | | `emit_event(event)` | Emit document or permission events | | `increment_scanned(count)` / `increment_updated(count)` | Update sync progress counters | | `save_checkpoint(value)` | Persist a run checkpoint for safe resume | | `complete()` / `fail(error)` | Mark the sync complete or failed | | `heartbeat()` | Keep long-running syncs alive | `emit_event()` buffers events. `save_checkpoint()` flushes buffered events before persisting the checkpoint so the checkpoint does not race ahead of emitted events. ## Emitting Documents Rust connectors emit `ConnectorEvent` values directly. ```rust theme={null} use anyhow::Result; use omni_connector_sdk::{ ConnectorEvent, DocumentMetadata, DocumentPermissions, SourceType, SyncContext, }; use serde_json::json; async fn emit_one(ctx: &SyncContext) -> Result<()> { let content_id = ctx.store_content("Hello from my data source").await?; let external_id = "item-123".to_string(); ctx.emit_event(ConnectorEvent::DocumentCreated { sync_run_id: ctx.sync_run_id().to_string(), source_id: ctx.source_id().to_string(), document_id: external_id.clone(), content_id, metadata: DocumentMetadata { title: Some("Example item".to_string()), author: None, created_at: None, updated_at: None, content_type: Some("text/plain".to_string()), mime_type: Some("text/plain".to_string()), size: None, url: Some("https://example.com/items/123".to_string()), path: None, extra: None, }, permissions: DocumentPermissions { public: true, users: vec![], groups: vec![], }, attributes: Some([ ("source_type".to_string(), json!(SourceType::Web)), ].into_iter().collect()), }).await?; ctx.increment_scanned(1).await?; ctx.increment_updated(1).await?; Ok(()) } ``` For updates, emit `DocumentUpdated`; for removals, emit `DocumentDeleted`. For group-based permissions, emit `GroupMembershipSync` events. ## Checkpoints and Resume Use `save_checkpoint` after batches or remote pages that are safe to resume past: ```rust theme={null} let checkpoint = serde_json::json!({ "cursor": next_cursor }); ctx.save_checkpoint(checkpoint).await?; ``` On the next run, connector-manager passes the latest checkpoint as the `checkpoint` argument to `sync`. `ctx.is_resume()` tells you whether the current run is a resume attempt after interruption. Checkpoint only after the documents for that cursor/page have been emitted. The SDK flushes before checkpointing, but your connector still controls where the logical cursor advances. ## Actions Expose connector-specific actions by returning `ActionDefinition` values and implementing `execute_action`. ```rust theme={null} use anyhow::{anyhow, Result}; use axum::http::StatusCode; use omni_connector_sdk::{ActionDefinition, ActionMode, ActionResponse, Connector, SourceType}; use serde_json::{json, Value as JsonValue}; fn actions(&self) -> Vec { vec![ActionDefinition { name: "validate_connection".to_string(), description: "Validate connector credentials".to_string(), input_schema: json!({ "type": "object", "properties": {} }), mode: ActionMode::Read, source_types: vec![SourceType::Web], admin_only: false, hidden: true, }] } async fn execute_action( &self, action: &str, _params: JsonValue, _credentials: Option, ) -> Result { match action { "validate_connection" => Ok(ActionResponse::success(json!({ "ok": true })).into_response()), other => Ok(ActionResponse::not_supported(other) .into_response_with_status(StatusCode::NOT_FOUND)), } } ``` | Field | Meaning | | -------------- | ------------------------------------------------------------------------------------------------------------- | | `mode` | `Read` or `Write`; write actions require approval/allowlisting where applicable | | `source_types` | Restricts an action to specific source types; empty means all source types for the connector | | `admin_only` | Restricts chat/tool exposure to admins and affects credential resolution | | `hidden` | Keeps setup/internal actions out of every chat and agent tool surface while leaving them dispatchable by name | ## Search Operators Connectors can register inline search operators that map to document attributes. ```rust theme={null} use omni_connector_sdk::SearchOperator; fn search_operators(&self) -> Vec { vec![SearchOperator { operator: "project".to_string(), attribute_key: "project_id".to_string(), value_type: "text".to_string(), }] } ``` After registration, users can filter with queries like `roadmap project:alpha` in Search and Chat. ## OAuth and MCP Rust connectors can also expose: * `oauth_config()` for Omni's generic OAuth flow, including optional Dynamic Client Registration via `registration_endpoint` and RFC 8707 resource indicators * `mcp_server()` for stdio or Streamable HTTP MCP servers * `prepare_mcp_env()` / `prepare_mcp_headers()` to inject credentials into MCP calls * `skills()` for connector-owned skills that guide the Omni agent through connector-specific workflows * `serve_with_extra_routes()` for provider-specific callback/enrichment routes when the standard SDK routes are not enough Use the GitHub, Google, Atlassian, and Darwinbox connectors as references for advanced actions, OAuth manifests, and MCP integration patterns. ## Testing Rust connectors should use integration tests wherever possible. Existing Rust connector tests under `connectors/*/tests/` use testcontainers-backed ParadeDB, Redis, and connector-manager helpers from the repo's shared test infrastructure. Recommended coverage: * Config and credential deserialization failures * Full and incremental sync behavior * Checkpoint/resume behavior * Permission and group membership events * Search operators and attributes * Action success/failure cases * Cancellation for long-running syncs ## Reference Connectors | Connector | Good reference for | | ----------------------- | ---------------------------------------------------------------------------------- | | `connectors/filesystem` | Minimal Rust connector, local config, realtime sync, hidden setup action | | `connectors/darwinbox` | Typed config/credentials/checkpoint, many actions, search operators | | `connectors/google` | Service-account auth, multiple source types, OAuth manifest, Drive/Gmail/Chat sync | | `connectors/slack` | Realtime/socket behavior and message/file indexing | | `connectors/atlassian` | Multiple products, attachments, permission mapping | ## What's Next Compare SDKs and connector lifecycle concepts Learn how connector-manager schedules and monitors sources # Connector SDK Overview Source: https://docs.getomni.co/developers/sdk-overview Build custom connectors to integrate any data source with Omni The Omni Connector SDKs enable you to build custom connectors that integrate any data source with Omni's search and AI capabilities. Whether you're connecting an internal database, a SaaS application, or a proprietary system, the SDKs provide everything you need. ## Why Build a Custom Connector? While Omni includes connectors for popular platforms (Google Workspace, Slack, Atlassian, HubSpot), you may need to integrate: * **Internal systems** — Databases, wikis, or custom applications * **Niche SaaS tools** — Industry-specific platforms not yet supported * **Proprietary data sources** — File formats or APIs unique to your organization * **Legacy systems** — Older systems with custom APIs ## SDK Features The SDKs share these core capabilities, although language-specific APIs and advanced features differ: | Feature | Description | | ------------------------ | ------------------------------------------------------ | | **Connector Base Class** | Abstract base with sync lifecycle management | | **Sync Context** | Utilities for emitting documents and tracking progress | | **Content Storage** | Store document content for indexing | | **SDK Client** | Communication with the connector-manager service | | **HTTP Server** | Built-in server exposing standard connector endpoints | | **Data Models** | Type-safe models for documents, events, and metadata | ## Available SDKs Build connectors in Python with FastAPI Build connectors in TypeScript with Express Build connectors in Rust with Axum Choose the language that best fits your connector and consult its SDK reference for the supported APIs. The built-in connectors provide language-specific reference implementations across the Python, TypeScript, and Rust SDKs. **The SDKs are not published to PyPI or npm.** All connector development happens **inside the Omni monorepo**, with the SDK consumed as a local path dependency (`{ path = "../../sdk/python" }` for Python, `"file:../../sdk/typescript"` for TypeScript). To build a connector, clone [omni](https://github.com/getomnico/omni) and add a new directory under `connectors/`. This keeps connectors and the SDK versioned together, so you never have to chase a mismatched release. ## Scaffolding with the `/build-connector` skill The omni repository ships with a [Claude Code](https://claude.com/claude-code) skill called `build-connector` that scaffolds an entire new connector — sync logic, manifest, Dockerfile, package config, frontend wiring (icon, setup dialog, source type enum), Docker Compose service, AWS and GCP Terraform, and integration tests — following the conventions of the built-in connectors. To use it, install Claude Code, open the `omni` repo, and run: ``` /build-connector Asana ``` (or any other service name). The skill walks through the full checklist: 1. Choose a language (Python, TypeScript, or Rust) and read the matching reference connector 2. Implement sync, manifest, and any custom actions 3. Add a database migration for the new source type 4. Add the variant to the Rust `SourceType` enum in `shared/src/models.rs` 5. Wire up the frontend (icon, setup dialog, integrations page entry) 6. Add the service to `docker/docker-compose.yml` and `.env.example` 7. Add AWS ECS and GCP Cloud Run definitions 8. Write integration tests against a real ParadeDB + Redis + connector-manager via testcontainers The skill is not required — you can build a connector by hand by reading the existing ones — but it eliminates a lot of boilerplate and ensures nothing is missed. ## Architecture Custom connectors integrate with Omni exclusively through HTTP communication with the connector-manager service. Connectors have **no direct database access** — all document emission, state management, and content storage is handled via the SDK client which communicates with connector-manager over HTTP. ``` ┌─────────────────┐ ┌───────────────────┐ ┌─────────────┐ │ Your Connector │ ←HTTP→ │ connector-manager │ ←Queue→ │ omni-indexer│ │ (SDK-based) │ │ (orchestrator) │ │ (indexing) │ └─────────────────┘ └───────────────────┘ └─────────────┘ │ │ │ │ ↓ ↓ ┌─────────────────┐ ┌───────────────────┐ │ Data Source │ │ PostgreSQL │ │ (your API) │ │ (storage/queue) │ └─────────────────┘ └───────────────────┘ ``` ### Connector Lifecycle 1. **omni-connector-manager** triggers a sync via HTTP `POST /sync` 2. Your connector receives the request with source configuration and credentials 3. Your connector fetches data from the external source 4. Documents are emitted through the SDK, which sends them to connector-manager 5. Long-running syncs can save per-run checkpoints so interrupted runs resume safely 6. connector-manager queues events for the indexer 7. Your connector reports completion or failure; on success, the run checkpoint is promoted to the source's persisted checkpoint ### HTTP Endpoints The SDK automatically exposes these endpoints: | Endpoint | Method | Purpose | | --------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `/health` | GET | Health check for container orchestration | | `/manifest` | GET | Returns connector metadata and capabilities | | `/sync` | POST | Triggers a sync operation | | `/sync/{sync_run_id}` | GET | Reports whether a given sync run is still active in this process. connector-manager polls this to detect connector restarts and auto-resume interrupted syncs from the last per-run checkpoint. | | `/cancel` | POST | Cancels a running sync | | `/action` | POST | Executes connector-specific actions | | `/resource` | POST | Reads resources from an MCP server exposed by the connector | | `/prompt` | POST | Fetches prompts from an MCP server exposed by the connector | ### Document Conversion (Docling) Binary documents (PDFs, Word, Excel, PowerPoint, images) are converted to text via the centralized Docling service rather than per-connector logic. The SDK exposes this through `ctx.content_storage.extract_text(data, mime_type, filename)` (and `extract_and_store_content(...)` when you want to extract and persist in one call). Under the hood the SDK calls `POST /sdk/extract-text` on connector-manager, which routes to Docling when the admin has enabled it and falls back to lightweight built-in extractors otherwise. All built-in connectors (IMAP, Gmail, Filesystem, Nextcloud, etc.) use this pathway — custom connectors should too. ## Quick Start Example Here's a minimal connector implementation in Python: ```python theme={null} from omni_connector import Connector, Document, DocumentMetadata, SyncContext class MyConnector(Connector): @property def name(self) -> str: return "my-connector" @property def version(self) -> str: return "1.0.0" @property def source_types(self) -> list[str]: return ["my_source_type"] @property def sync_modes(self) -> list[str]: return ["full"] async def sync( self, source_config: dict, credentials: dict, checkpoint: dict | None, ctx: SyncContext ) -> None: # Fetch items from your data source items = await self.fetch_items(credentials["api_key"]) for item in items: # Store content content_id = await ctx.content_storage.save(item["content"]) # Emit document await ctx.emit(Document( external_id=item["id"], title=item["title"], content_id=content_id, metadata=DocumentMetadata( url=item["url"], created_at=item["created_at"], ), )) await ctx.increment_scanned() await ctx.complete() if __name__ == "__main__": MyConnector().serve(port=8000) ``` ## Wrapping an MCP Server The Python SDK can expose tools from a [Model Context Protocol](https://modelcontextprotocol.io) server to Omni. Override the `mcp_server` property on your connector to return either a `StdioMcpServer` (subprocess transport) or an `HttpMcpServer` (Streamable HTTP transport for remote MCP servers). Consult the language-specific SDK references for their supported MCP APIs. **Stdio (local subprocess):** ```python theme={null} from omni_connector import Connector, StdioMcpServer class MyConnector(Connector): @property def mcp_server(self) -> StdioMcpServer: return StdioMcpServer( command="my-mcp-server", args=["stdio"], ) def prepare_mcp_env(self, credentials: dict) -> dict[str, str]: return {"API_TOKEN": credentials["api_key"]} ``` **Remote (Streamable HTTP):** ```python theme={null} from omni_connector import Connector, HttpMcpServer class MyConnector(Connector): @property def mcp_server(self) -> HttpMcpServer: return HttpMcpServer(url="https://api.example.com/mcp") def prepare_mcp_headers(self, credentials: dict) -> dict[str, str]: return {"Authorization": f"Bearer {credentials['api_key']}"} ``` The `github` connector is a reference implementation that wraps the official GitHub MCP server over stdio. Return `None` (the default) to disable MCP support entirely for your connector. #### Connector-owned Skills Connectors can also ship skills — Markdown documents that guide the Omni agent through connector-specific workflows. Return a list of `ConnectorSkillDefinition` values from the `skills` property: ```python theme={null} from omni_connector import ConnectorSkillDefinition class MyConnector(Connector): @property def skills(self) -> list[ConnectorSkillDefinition]: return [ ConnectorSkillDefinition( id="my-tool-guide", title="My Tool Guide", description="Best practices for using My Tool", source_types=["my_source_type"], content=Path(__file__).parent.joinpath("skills/my-tool.md").read_text(), ), ] ``` Skills are loaded into Omni when the connector is enabled. The Google connector is a reference: it ships Drive and Gmail skills that teach the agent how to navigate the Google Workspace API surface. #### Python OAuth Credential-Ready Lifecycle For Python MCP connectors that require per-user OAuth, override `oauth_credential_ready` to run provider-specific post-OAuth setup — such as authenticated MCP catalog discovery — once credentials are stored: ```python theme={null} class MyConnector(Connector): async def oauth_credential_ready( self, request: OAuthCredentialReadyRequest ) -> None: # Discover the MCP tool catalog now that credentials are available pass ``` This hook is called by connector-manager after a user completes the OAuth flow, with the resolved credentials passed in the request. ## Example Connectors Both SDKs include an RSS connector example that demonstrates: * Fetching data from an external API * Parsing and transforming content * Emitting documents with metadata * Implementing incremental sync * Handling errors and cancellation * Implementing custom actions See the SDK-specific documentation for the complete example walkthrough. ## Deploying Your Connector ### Docker Deployment Each connector ships its own `Dockerfile`. Copy from a reference connector in your chosen language (e.g. `connectors/notion/Dockerfile` for Python, `connectors/linear/Dockerfile` for TypeScript, `connectors/google/Dockerfile` for Rust). Add the connector as a service in `docker/docker-compose.yml`, gated by a Compose profile so it only starts when the user opts in via `ENABLED_CONNECTORS`: ```yaml theme={null} services: my-connector: build: context: ../connectors/my-connector profiles: ["my-connector"] environment: CONNECTOR_MANAGER_URL: ${CONNECTOR_MANAGER_URL} CONNECTOR_HOST_NAME: my-connector PORT: ${MY_CONNECTOR_PORT} networks: - omni-network ``` Then add the port to `.env.example` and document the new profile name in the `ENABLED_CONNECTORS` comment. ### Registration with Connector Manager You don't need to set per-connector URLs in connector-manager's environment. **Connectors auto-register their manifest with connector-manager on a 30-second heartbeat** (90s TTL) — as long as the container is reachable on the Docker network and `CONNECTOR_MANAGER_URL`, `CONNECTOR_HOST_NAME`, and `PORT` are set, it will appear automatically. `CONNECTOR_HOST_NAME` is the hostname connector-manager should call back to, usually the Docker Compose service name (for example, `my-connector`). The SDK combines it with `PORT` to register the connector URL. ## What's Next Detailed Python SDK documentation Detailed TypeScript SDK documentation Detailed Rust SDK documentation # TypeScript SDK Source: https://docs.getomni.co/developers/typescript-sdk Build custom connectors with TypeScript and Express The TypeScript SDK (`@getomnico/connector`) provides everything you need to build custom connectors that integrate with Omni's search and AI platform. ## Installation The SDK is **not published to npm**. Connectors live in the Omni monorepo and depend on the SDK as a local file path. To start a new TypeScript connector, fork or clone [omni](https://github.com/getomnico/omni) and create a directory under `connectors/`: ```bash theme={null} git clone https://github.com/getomnico/omni.git cd omni/connectors mkdir my-connector && cd my-connector ``` In your `package.json`, reference the SDK with a `file:` link — copy from `connectors/linear/package.json`: ```json theme={null} { "name": "@getomnico/my-connector", "version": "1.0.0", "type": "module", "dependencies": { "@getomnico/connector": "file:../../sdk/typescript" } } ``` Then install: ```bash theme={null} npm install ``` **Requirements:** * Node.js >= 20.0.0 **Transitive dependencies pulled in by the SDK:** * `express` - Web framework * `pino` - Structured logging * `zod` - Schema validation Run `/build-connector ` from Claude Code inside the omni repo to scaffold the entire connector structure (sync logic, manifest, Dockerfile, frontend wiring, Terraform, integration tests) following the conventions used by built-in connectors. See the [SDK overview](/developers/sdk-overview#scaffolding-with-the-build-connector-skill) for details. ## Quick Start ```typescript theme={null} import { Connector, Document, DocumentMetadata, DocumentPermissions, SyncContext, } from '@getomnico/connector'; class MyConnector extends Connector { get name(): string { return 'my-connector'; } get version(): string { return '1.0.0'; } get sourceTypes(): string[] { return ['my_source_type']; } get syncModes(): string[] { return ['full', 'incremental']; } async sync( sourceConfig: Record, credentials: Record, state: Record | null, ctx: SyncContext ): Promise { // Your sync logic here await ctx.complete(); } } const connector = new MyConnector(); connector.serve({ port: 8000 }); ``` When running under Docker, set `CONNECTOR_MANAGER_URL`, `CONNECTOR_HOST_NAME`, and `PORT`. The SDK uses `CONNECTOR_HOST_NAME` plus `PORT` to register the connector URL that connector-manager calls for syncs, actions, MCP resources, and prompts. ## Core Concepts ### Connector Class The `Connector` abstract class defines the interface for all connectors. **Required Properties:** | Property | Type | Description | | ------------- | ---------- | ---------------------------------------------------- | | `name` | `string` | Unique connector identifier (e.g., `"my-connector"`) | | `version` | `string` | Semantic version (e.g., `"1.0.0"`) | | `sourceTypes` | `string[]` | Source types the connector handles | **Optional Properties:** | Property | Type | Default | Description | | ----------- | -------------------- | ---------- | ------------------------------------------ | | `syncModes` | `string[]` | `["full"]` | Supported modes: `"full"`, `"incremental"` | | `actions` | `ActionDefinition[]` | `[]` | Custom actions the connector supports | **Required Methods:** ```typescript theme={null} async sync( sourceConfig: Record, // Configuration from Omni UI credentials: Record, // Stored credentials state: Record | null, // Latest per-run checkpoint ctx: SyncContext // Sync context utilities ): Promise ``` **Optional Methods:** ```typescript theme={null} cancel(syncRunId: string): void // Handle sync cancellation request async executeAction( action: string, params: Record, credentials: Record ): Promise> // Execute a custom action ``` ### SyncContext The `SyncContext` object is passed to your `sync` method and provides utilities for the sync operation. **Properties:** | Property | Type | Description | | ------------------ | ------------------------- | ------------------------------------------------------------------ | | `syncRunId` | `string` | Unique identifier for this sync run | | `sourceId` | `string` | Source identifier | | `sourceType` | `string \| null` | Source type dispatched by connector-manager | | `state` | `Record` | Latest per-run checkpoint when resuming | | `syncMode` | `SyncMode` | Dispatched mode: `full`, `incremental`, or `realtime` | | `isResume` | `boolean` | Whether this run was resumed after an interruption | | `contentStorage` | `ContentStorage` | Storage for document content | | `documentsEmitted` | `number` | Count of emitted documents, seeded from the running sync on resume | | `documentsScanned` | `number` | Count of scanned items, seeded from the running sync on resume | **Methods:** ```typescript theme={null} // Emit a new document await ctx.emit(document: Document): Promise // Emit an updated document await ctx.emitUpdated(document: Document): Promise // Mark a document as deleted await ctx.emitDeleted(externalId: string): Promise // Emit source group membership for permission filtering await ctx.emitGroupMembership( groupEmail: string, memberEmails: string[], groupName?: string, ): Promise // Report a non-fatal error for a document ctx.emitError(externalId: string, error: string): void // Increment scanned counter (also sends heartbeat) await ctx.incrementScanned(): Promise // Increment the server-side updated counter await ctx.incrementUpdated(count: number): Promise // Check source-level user allow/deny settings before emitting user-owned docs ctx.shouldIndexUser(userEmail: string): boolean // Save a per-run checkpoint for resumability await ctx.saveCheckpoint(checkpoint: Record): Promise // Alias for saveCheckpoint await ctx.saveState(state: Record): Promise // Mark sync as completed, optionally saving the final checkpoint first await ctx.complete(checkpoint?: Record): Promise // Mark sync as failed await ctx.fail(error: string): Promise // Check if sync was cancelled ctx.isCancelled(): boolean ``` ### ContentStorage The `ContentStorage` class handles storing document content. ```typescript theme={null} // Store text content const contentId = await ctx.contentStorage.save( content: string, contentType?: string // default: "text/plain" ): Promise // Store binary content (base64 encoded) const contentId = await ctx.contentStorage.saveBinary( content: Buffer, contentType: string ): Promise // Extract text from a binary file (PDF, DOCX, PPTX, XLSX, images, ...) // via connector-manager's /sdk/extract-text endpoint, without persisting. // Returns the extracted text. const text = await ctx.contentStorage.extractText( data: Buffer, mimeType: string, filename: string, ): Promise // Same as extractText, but also stores the extracted text and returns // the resulting contentId. const contentId = await ctx.contentStorage.extractAndStoreContent( data: Buffer, mimeType: string, filename: string, ): Promise ``` The returned `contentId` is a ULID that references the stored content. Always prefer `extractText` / `extractAndStoreContent` over shelling out to your own PDF/Office parser. The connector-manager routes these calls through the centralized Docling service when the admin has enabled it (with the configured quality preset), and falls back to a lightweight built-in extractor otherwise — so your connector automatically honors the instance-wide document-conversion setting. ### Document Model The `Document` interface represents a searchable document. ```typescript theme={null} import { Document, DocumentMetadata, DocumentPermissions } from '@getomnico/connector'; const document: Document = { external_id: 'unique-id-from-source', title: 'Document Title', content_id: contentId, // From contentStorage.save() metadata: { title: 'Document Title', author: 'Author Name', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), mime_type: 'text/plain', size: 1234, url: 'https://source.com/doc/123', path: '/folder/document.txt', extra: { custom_field: 'value' }, }, permissions: { public: true, users: ['user@example.com'], groups: ['engineering'], }, attributes: { category: 'documentation' }, }; ``` **Document Fields:** | Field | Type | Required | Description | | ------------- | ------------------------- | -------- | -------------------------------- | | `external_id` | `string` | Yes | Unique ID from the source system | | `title` | `string` | Yes | Document title for display | | `content_id` | `string` | Yes | Reference to stored content | | `metadata` | `DocumentMetadata` | No | Document metadata | | `permissions` | `DocumentPermissions` | No | Access control | | `attributes` | `Record` | No | Custom searchable attributes | ### Actions Connectors can define custom actions that users can trigger from the Omni UI. ```typescript theme={null} import { ActionDefinition } from '@getomnico/connector'; class MyConnector extends Connector { get actions(): ActionDefinition[] { return [ { name: 'validate_connection', description: 'Test the API connection', input_schema: { type: 'object', properties: { timeout: { type: 'integer', description: 'Connection timeout in seconds', }, }, }, }, ]; } async executeAction( action: string, params: Record, credentials: Record ): Promise> { if (action === 'validate_connection') { // Perform validation return { status: 'success', message: 'Connection valid' }; } throw new Error(`Unknown action: ${action}`); } } ``` ## RSS Connector Example Here's a complete example of an RSS feed connector: ```typescript theme={null} import { ActionDefinition, Connector, Document, DocumentMetadata, DocumentPermissions, SyncContext, } from '@getomnico/connector'; interface FeedEntry { id?: string; link?: string; title?: string; content?: string; summary?: string; author?: string; published?: string; } interface Feed { title?: string; entries: FeedEntry[]; } class RSSConnector extends Connector { get name(): string { return 'rss'; } get version(): string { return '1.0.0'; } get sourceTypes(): string[] { return ['rss']; } get syncModes(): string[] { return ['full', 'incremental']; } get actions(): ActionDefinition[] { return [ { name: 'validate_feed', description: 'Validate that a feed URL is accessible', input_schema: { type: 'object', properties: { url: { type: 'string', description: 'The RSS/Atom feed URL to validate', }, }, required: ['url'], }, }, ]; } async sync( sourceConfig: Record, credentials: Record, state: Record | null, ctx: SyncContext ): Promise { const feedUrl = sourceConfig.feed_url as string; if (!feedUrl) { await ctx.fail('Missing feed_url in source configuration'); return; } // Fetch and parse the feed const feed = await this.parseFeed(feedUrl); // Get last sync time for incremental sync const lastSyncTime = state?.last_sync_time ? new Date(state.last_sync_time as string) : null; for (const entry of feed.entries) { if (ctx.isCancelled()) { return; } await ctx.incrementScanned(); // Skip old entries in incremental mode if (lastSyncTime && entry.published) { const entryDate = new Date(entry.published); if (entryDate <= lastSyncTime) { continue; } } // Build content from entry const content = entry.content || entry.summary || entry.title || ''; const contentId = await ctx.contentStorage.save(content); // Create document const doc: Document = { external_id: entry.id || entry.link || '', title: entry.title || 'Untitled', content_id: contentId, metadata: { author: entry.author, url: entry.link, created_at: entry.published, extra: { feed_title: feed.title, feed_url: feedUrl, }, }, permissions: { public: true, }, }; await ctx.emit(doc); } // Complete with updated state await ctx.complete({ last_sync_time: new Date().toISOString() }); } async executeAction( action: string, params: Record, credentials: Record ): Promise> { if (action === 'validate_feed') { const url = params.url as string; if (!url) { return { status: 'error', error: 'Missing URL parameter' }; } try { const feed = await this.parseFeed(url); return { status: 'success', result: { title: feed.title, entry_count: feed.entries.length, }, }; } catch (error) { return { status: 'error', error: String(error) }; } } return { status: 'error', error: `Unknown action: ${action}` }; } private async parseFeed(url: string): Promise { // Simple XML parsing (use rss-parser library in production) const response = await fetch(url); const text = await response.text(); const entries: FeedEntry[] = []; const itemRegex = /([\s\S]*?)<\/item>/g; let match; while ((match = itemRegex.exec(text)) !== null) { const item = match[1]; entries.push({ title: this.extractTag(item, 'title'), link: this.extractTag(item, 'link'), content: this.extractTag(item, 'content:encoded') || this.extractTag(item, 'description'), author: this.extractTag(item, 'author') || this.extractTag(item, 'dc:creator'), published: this.extractTag(item, 'pubDate'), id: this.extractTag(item, 'guid') || this.extractTag(item, 'link'), }); } return { title: this.extractTag(text, 'title'), entries, }; } private extractTag(xml: string, tag: string): string | undefined { const regex = new RegExp(`<${tag}[^>]*>([\\s\\S]*?)`, 'i'); const match = regex.exec(xml); return match ? match[1].trim() : undefined; } } const connector = new RSSConnector(); connector.serve({ port: 8000 }); ``` ## Error Handling The SDK provides custom error classes: ```typescript theme={null} import { ConnectorError, // Base error SdkClientError, // Communication errors (includes statusCode) SyncCancelledError, // Sync was cancelled ConfigurationError, // Configuration issues } from '@getomnico/connector'; ``` **Handling errors in sync:** ```typescript theme={null} async sync( sourceConfig: Record, credentials: Record, state: Record | null, ctx: SyncContext ): Promise { try { // Your sync logic await ctx.complete(); } catch (error) { if (error instanceof SyncCancelledError) { // Handle cancellation gracefully return; } await ctx.fail(String(error)); } } ``` ## Development ### Project Setup ```bash theme={null} # Install dependencies npm install # Build npm run build # Run tests npm test # Watch mode for tests npm run test:watch # Lint npm run lint ``` ### TypeScript Configuration The SDK uses strict TypeScript settings. Your `tsconfig.json` should include: ```json theme={null} { "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", "strict": true, "esModuleInterop": true, "skipLibCheck": true } } ``` ## API Reference ### Exports The SDK exports the following: **Core Classes:** * `Connector` - Abstract base class * `SyncContext` - Sync operation context * `ContentStorage` - Content storage interface * `SdkClient` - SDK client for connector-manager **Data Models (with Zod schemas):** * `Document`, `DocumentMetadata`, `DocumentPermissions` * `ConnectorEvent`, `EventType` * `ActionDefinition` * `ActionRequest`, `ActionResponse` * `ConnectorManifest`, `SyncMode` * `SyncRequest`, `SyncResponse` * `CancelRequest`, `CancelResponse` **Error Classes:** * `ConnectorError`, `SdkClientError`, `SyncCancelledError`, `ConfigurationError` ### Zod Schemas All data models include Zod schemas for runtime validation: ```typescript theme={null} import { DocumentSchema, SyncRequestSchema } from '@getomnico/connector'; // Validate incoming data const result = SyncRequestSchema.safeParse(data); if (!result.success) { console.error('Validation failed:', result.error); } ``` ## What's Next Learn about SDK architecture Build connectors with Python # Welcome to Omni Source: https://docs.getomni.co/index Open-source, self-hosted AI agent for company-wide work ## What is Omni? Omni is an **open-source, self-hosted AI agent for company-wide work**. It connects your company's apps, creates a shared context and tool layer over workplace knowledge, and lets agents search, reason, and carry out supported actions across your organization while respecting existing permissions. Search across Google Workspace, Google Chat, Google Ads, Microsoft 365, Slack, JIRA, Confluence, Salesforce, Darwinbox, Nextcloud, Paperless-ngx, and more from a single interface Ask questions with citations, analyze information, and carry out supported actions Deploy on your infrastructure with full data control and privacy Users only see what they have access to in the underlying sources ## Why Omni? * **Cross-tool execution**: Gather context and carry out supported actions across connected company systems. * **Grounded answers**: Answer questions using company context and show the sources behind each response. * **Simple architecture**: Use Postgres for application data, full-text search, semantic search, and queues. See [Architecture](/architecture) for more details. * **Self-hosted by design**: Run Omni in your cloud, on-premises, or an isolated environment. * **Flexible models**: Use Anthropic, OpenAI, Gemini, AWS Bedrock, Vertex AI, Azure AI Foundry, or an OpenAI-compatible endpoint. * **Extensible**: Connect MCP tools or build company-specific integrations with the Python or TypeScript SDKs. ## Getting Started Ready to deploy Omni? Get up and running in under 10 minutes. Deploy Omni locally with Docker Compose Choose the right deployment strategy for your needs Upgrade and diagnose Docker Compose deployments ## Learn More Understand how Omni works under the hood Set up connectors for Google, Slack, Confluence, and more Learn how to use Omni's search and AI features ## Community & Support Check out our repo here: [github.com/getomnico/omni](https://github.com/getomnico/omni). ## License Omni is open source and available under the Apache 2.0 License. # Observability Source: https://docs.getomni.co/observability OpenTelemetry tracing from Omni core services: signal architecture and collector configuration Omni exports OpenTelemetry **traces** from the core services: **omni-web** (Node/SvelteKit), **omni-ai** (Python/FastAPI), **omni-searcher** (Rust), **omni-indexer** (Rust), and **omni-connector-manager** (Rust). Connectors capture traces individually through the connector SDK. Metrics and OTLP log export are not currently implemented; traces are exported as OTLP/HTTP to the configured endpoint. ## Architecture & Signal Flow Omni does **not** bundle or require an OpenTelemetry Collector or backend. Traces are sent to whatever endpoint is configured in `OTEL_EXPORTER_OTLP_ENDPOINT`. The default value is empty, which means instrumentation is active but signals are **dropped / not exported or stored**. When a non-empty endpoint is set, traces are sent to `${OTEL_EXPORTER_OTLP_ENDPOINT}/v1/traces`. Point `OTEL_EXPORTER_OTLP_ENDPOINT` at any compatible OTLP/HTTP collector or backend. See [Production & Privacy Guidance](#production--privacy-guidance) for production recommendations. ### Base OTLP Endpoint Semantics | Environment Variable | Default | Description | | ----------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `OTEL_EXPORTER_OTLP_ENDPOINT` | *(empty)* | Base URL for OTLP HTTP trace export (e.g. `http://my-collector:4318`). Empty = traces are instrumented but not exported. | | `OTEL_DEPLOYMENT_ID` | *(empty)* | **Recommended:** set one shared non-empty value across all services. With Compose's empty value, services export an empty `deployment.id`. If the variable is absent entirely, Rust, Python, and Node generate a ULID. Set it explicitly for consistent cross-service identity. | | `OTEL_DEPLOYMENT_ENVIRONMENT` | `development` | Environment label (`production`, `staging`, etc.) | | `SERVICE_VERSION` | `0.1.0` | Version label attached to all signals (Rust services default to their own crate version) | ### Resource Attributes Every trace carries: * `service.name`: the logical service name (`omni-searcher`, `omni-indexer`, `omni-ai`, `omni-web`, `omni-connector-manager`) * `service.version`: from `SERVICE_VERSION` * `deployment.environment`: from `OTEL_DEPLOYMENT_ENVIRONMENT` * `deployment.id`: from `OTEL_DEPLOYMENT_ID` ### Shutdown Behaviour On graceful shutdown (SIGTERM/SIGINT), each service flushes pending spans before exiting. In Rust services the OTLP exporter timeout is hard-coded at 10 seconds in the opentelemetry-rust SDK; `OTEL_EXPORTER_OTLP_TIMEOUT` is **not** honoured. Busy shutdowns that exceed the Rust SDK timeout may lose the final batch. Python (omni-ai) and Node (omni-web) use their SDK defaults. ## Trace Continuity ### HTTP Request Tracing **Rust services** (searcher, indexer, connector-manager) use custom axum middleware. SERVER spans use `METHOD /path` names (e.g. `GET /v1/search`); the `trace_id` is also propagated as a request id. W3C `traceparent` / `tracestate` is propagated in HTTP headers: inbound requests extract the parent context, and outbound HTTP calls inject it via the shared `http_client` helper. **Node (omni-web)** and **Python (omni-ai)** use auto-instrumentation provided by the respective OpenTelemetry SDKs (Node auto-instrumentations with the filesystem instrumentation disabled; Python FastAPI and HTTPX instrumentation). Span names follow the SDK convention rather than a unified scheme. Outbound HTTP requests create a CLIENT span, producing **parent/child relationships within a single trace** (not links) when the web service calls internal APIs. The same `traceId` is shared across the web→searcher and web→AI call chain. ### Queue Processing The indexer and connector-manager consume events from Postgres-based queues (`connector_events_queue`, the generic `tasks` queue used for document embeddings, and `agent_runs`). Queue rows do not currently carry trace context, so consumer processing starts a new root trace independent of the producer. ## Logs Logs are written to the container's stdout (standard `docker logs`), not exported via OTLP. Rust services emit structured JSON logs through `tracing-subscriber`; Python and Node use their standard logging. No global PII scrubber is applied. Runtime logging call sites are audited to avoid sensitive and high-cardinality fields, but log content is produced by the application as-is. In Rust services, when an active trace span exists at the log call site, `trace_id`/`span_id` are attached to the log record via the tracing–OpenTelemetry layer. ## Troubleshooting | Symptom | Likely Cause & Fix | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | No traces exported | `OTEL_EXPORTER_OTLP_ENDPOINT` is empty or unreachable. Set it to the URL of your OTLP/HTTP collector or backend. | | Telemetry lost on shutdown | OTLP exporter timeout too short for pending data. Rust services use a hard-coded 10 s timeout (`OTEL_EXPORTER_OTLP_TIMEOUT` is not honoured). Ensure shutdown completes within the timeout. | | Invalid `traceparent` header | Client libraries generate invalid W3C TraceContext. Check that the `traceparent` header is formatted as `00-{trace_id}-{span_id}-{flags}`. | | High trace cardinality | Dynamic span attributes (e.g. user IDs, search queries) create many time series. Configure your collector or backend with appropriate sampling or cardinality limits. | ## Production & Privacy Guidance When using an external OTLP collector or backend in production: 1. **Use TLS**. Enable TLS on the OTLP endpoint, or use gRPC with TLS. 2. **Authenticate**. Use API keys or mTLS to secure the connection. 3. **Configure sampling**. Use a `probabilistic_sampler` or `tail_sampling` processor in your collector for high-throughput services. 4. **Set resource limits**. Configure memory limits and spike limits in the collector or backend. 5. **Never expose the OTLP endpoint publicly**. It has no built-in authentication. Bind to loopback or use a private network. 6. **Filter sensitive data**. Omni applies no PII scrubbing. Redact sensitive fields in the collector or at the application level. # Quick Start Source: https://docs.getomni.co/quickstart Get Omni running locally in 10 minutes This guide will help you deploy Omni on your local machine using Docker Compose. For production deployments, see our comprehensive [Deployment Guide](/deployment/overview). ## Step 1: Download and Start Omni ```bash theme={null} # Download the latest release mkdir omni && cd omni curl -fsSL "https://github.com/getomnico/omni/releases/latest/download/omni-docker-compose.tar.gz" \ -o omni-docker-compose.tar.gz tar xzf omni-docker-compose.tar.gz rm omni-docker-compose.tar.gz # Setup environment cp .env.example .env ``` ## Step 2: Start Omni Services Launch all services using Docker Compose. The default configuration enables only the [Web Connector](/connectors/web): ```bash theme={null} docker compose -f docker/docker-compose.yml --env-file .env up -d ``` The first startup will take a few minutes as Docker pulls images and initializes the database. After setup, use the [Omni CLI](/deployment/cli) for upgrades, diagnostics, backups, and other Docker Compose operations. ## Step 3: Access the Web UI Open your browser and navigate to: ``` http://localhost:3000 ``` You should see the Omni login page. Create your first admin account: 1. Click "Create Account" 2. Enter an email and password 3. The first user is automatically granted admin privileges ## Step 4: Connect Your First Data Source In our deployment, we've only enabled the [Web Connector](/connectors/web), so we'll set it up to get some data into Omni. 1. Navigate to **Settings** → **Integrations** 2. Under **Available Integrations**, find the **Web** connector and click **Connect** 3. Enter the root URL of the website you want to index (e.g., `https://docs.example.com`) 4. Click **Connect** and wait for initial sync to complete Start with a low **Max Pages** value (e.g., 100) to test the crawler before doing a full crawl. ## Step 5: Configure LLM and Embedding models While Omni is busy indexing, let's configure LLM providers for AI chat. 1. Navigate to **Settings** → **LLM Providers**. 2. Click **Connect** against the provider you want to connect. You can have more than one connected, users will be able to switch between all available models. 3. Add your API key and hit **Connect**. 4. (Optional) Navigate to **Embedding Providers** and setup a provider of your choice, this will enable semantic search in Omni. Without a provider configured, search will still work but it will be limited to keyword-based search. Congratulations! You have a working deployment of Omni up and running on your local machine. ## Next Steps Learn how to use Omni's search interface Ask questions and get AI-powered answers Connect additional data sources Move to a production environment Having trouble? See the [Troubleshooting section](/deployment/docker-compose#troubleshooting) in the Docker Compose deployment guide. # Access Control Source: https://docs.getomni.co/user-guide/access-control Understanding what you can see in Omni ## Permission Inheritance Omni respects the permissions from your source systems. You only see content you already have access to in the original application - if you can't view a document in Google Drive, you won't see it in Omni either. ## How It Works Every document indexed by Omni carries a permissions record alongside its content. The record has three fields: `public`, a list of authorized user emails, and a list of authorized group identifiers. Group memberships are synced from each connector and stored against your user profile. When you search, Omni: 1. Finds matching content across all sources 2. Filters results to documents where **any** of the following holds: * The document is marked public * Your email is in the document's user list * Your email domain matches a group in the document's group list * You belong to one of the document's listed groups 3. Returns only content you're authorized to view The same permission filter applies to the AI assistant's `search` tool — it cannot retrieve or cite any document you wouldn't see in Search. People-directory lookups and chat-history lookups are separate capabilities with the scopes described below. ## People Directory The people directory is separate from indexed documents. The `search_people` capability searches active directory records and can return organization-visible profile fields supplied by connectors, such as a person's title, department, manager, or office. It does not use a document's `public`/`users`/`groups` permission record. ## Chat History `search_chats` and `read_chat` expose only the current user's previous Omni chats. A user cannot use them to read another user's conversations, and chat content is treated as untrusted context rather than instructions. ## Source-Specific Permissions Most connectors mirror a source's own access model. A few behaviors are worth calling out: | Source | Permission behavior | | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Salesforce** | Document permissions mirror record ownership, role hierarchy (when *Grant Access Using Hierarchies* is enabled), sharing rules and manual shares, and org-wide defaults. Only active users with an email address receive grants, and only objects explicitly marked public-read are visible to everyone | | **Windshift** | Personal source — each user's sync uses their own credential and indexed items are visible only to that user | | **Atlassian (Rovo MCP)** and **Salesforce MCP** | MCP-powered actions run with the authorizing user's own provider identity, not a shared service account, so tools can only see and change what that user can | Users without a valid per-user credential for an MCP-backed connector simply cannot use its MCP actions until they authorize the provider. ## Projects Projects are personal workspaces, not a sharing mechanism. Project instructions only shape the model's behavior, and context documents must be documents you can already read. The assistant's search inside a project is still permission-filtered, so a project never widens access beyond what you already have. See [Projects](/user-guide/projects). ## FAQ Check your permissions in the source application (Google Drive, Confluence, etc.). Omni mirrors those permissions. Permission changes sync periodically. Contact your admin if content doesn't appear within a few hours. Search history is private. Admins can see aggregate usage metrics but not individual queries. # AI Assistant Source: https://docs.getomni.co/user-guide/ai-assistant Get answers synthesized from your organization's knowledge Omni AI Assistant ## How It Works In Chat mode, Omni's AI assistant: 1. Searches across all your connected sources 2. Synthesizes information into a coherent answer 3. Provides citations so you can verify the source ## Conversations Each chat is saved in your **Recent chats** sidebar. Click any previous conversation to continue where you left off. ## Projects Projects group related chats together with standing instructions and context documents. Every chat in a project automatically inherits the project's instructions, and attached documents stay available as context across all of its conversations. Open **Projects** in the sidebar to create one, and see [Projects](/user-guide/projects) for details. ## Chat History The assistant can search your previous Omni conversations by title and message content when the answer is not available in the workplace index. It can then read a matching conversation one branch at a time. Chat history tools only expose your own chats; previous chat content is treated as context, not as instructions. ## Citations Every AI response includes links to source documents with inline citation markers. Click citation chips to: * Verify the information * Read the full context * Access the original document in its native application Citation support works across all LLM providers. Anthropic and AWS Bedrock use native citation APIs; other providers use synthetic `[citation:n]` markers that are converted to the same inline chip format with hover excerpts and a dedicated Sources drawer. ## Conversation Branching You can edit a previous message to explore a different direction. This creates a new **branch** in the conversation, preserving the original thread while letting you take a different path. Use the navigation arrows on messages to switch between branches. ## Retry Responses If you're not satisfied with an AI response, click the **retry button** to regenerate it. The assistant will produce a new answer using the same context. ## Agent Tools When you ask a question, the assistant may invoke one or more tools to gather information or take action. The current toolset is: | Tool | Purpose | | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **search** | Search across all connected sources using inline filters such as source, content type, author, and date | | **search\_people** | Search the people directory by name, email, title, department, location, employee type, or manager | | **search\_chats** | Find your previous Omni chats by conversation title and message content | | **read\_chat** | Read one branch of a previous chat, with pagination for long conversations | | **read\_document** | Fetch the full text of a document by ID | | **read\_file** / **write\_file** / **edit\_file** | Read, write, and edit files inside a per-chat workspace | | **run\_bash** | Execute a bash command inside the `omni-sandbox` container | | **run\_python** | Execute a Python snippet inside the `omni-sandbox` container | | **send\_email** | Draft and send email (via the configured email provider) | | **web\_search** | Search the public web using the admin-configured current search provider | | **fetch\_web\_page** | Fetch readable content from a public HTTP(S) URL using the admin-configured current fetch provider | | **Connector actions** | Source-specific actions exposed by connectors, such as exporting a Notion data source, reading from an MCP-backed connector, or invoking provider APIs with user authorization | Any tool call that touches the outside world (running code, sending email, or a write-capable connector action) is gated by an **approval prompt** in the chat UI. You can approve or reject each request before it runs. Web tools are available only after an admin configures providers in **Settings** → **Web Providers**. Web fetches are limited to public HTTP(S) URLs and respect the admin URL blocklist. Some connector actions require your own OAuth authorization for the connected app. When that happens, Omni pauses the assistant loop and shows a **Connection required** card in chat. Click **Connect** to authorize the provider, then the assistant can continue with the action. If the admin has not configured the provider's OAuth app yet, the card explains that setup is required first. ## File Attachments You can attach ad-hoc files directly to a chat message by clicking the attach button in the input area (or dragging a file onto the input). The assistant can read the attached file alongside indexed documents — useful for one-off questions about something that isn't connected to Omni as a source. | Behavior | Details | | -------------------------- | --------------------------------------------------------------------------------------------------------------------- | | **Supported size** | Up to 50 MB per file | | **Small text files** | Inlined directly into the chat context so the assistant sees the full content | | **Larger or binary files** | Copied into the chat's sandbox workspace so the assistant can open them with `read_file`, `run_bash`, or `run_python` | | **Reuse across chats** | Uploads are not pinned to a single chat — you can reference the same upload from any message in any chat | Attachments are stored using the same content-storage backend as indexed documents (Postgres by default, or S3 if configured). They remain available until you delete them. ## Artifacts When the assistant creates a file in the sandbox — a spreadsheet, document, PDF, or chart — it can present it to you as an **artifact**. Artifacts appear as chips in the chat: * **Images** render inline in the conversation. * **Documents and other files** (PDF, Word, Excel, HTML, text) open in a resizable side pane with a type-appropriate viewer. Click the artifact chip to open or close the pane, and drag the splitter to resize it. * Each artifact is pinned to the **version** that was presented, so later edits to the same file do not change what you're viewing. The version you saw when the card was created stays available. You can download any artifact from the pane header. ## Document References You can reference specific indexed documents directly in a chat message by typing `@` followed by a search query. A typeahead menu shows matching documents from your connected sources — select one to inject its full content into the chat context alongside any documents the assistant retrieves on its own. This is useful when you already know which document contains the answer and want the assistant to focus on it: * "What does the onboarding checklist in `@employee-handbook` say about security training?" * "Summarise the budget section from `@Q3-report`" The referenced document content is injected into the model context, making it available alongside the assistant's own search results. Document references work across all connected sources — Google Drive, Confluence, Slack, Notion, and any other indexed content. ## People Search The AI assistant can look up people in your organization with the `search_people` tool. Ask about someone by name or email, or filter by job title, department, office, work country, employee type, or manager to find direct reports. Directory fields depend on the connected sources. Connectors such as Darwinbox can provide structured profiles including job title, department, manager, office, work country, employee ID, contact number, and top department. Only active directory records are returned. ## Memory If your admin enables Memory, Omni can remember useful facts from prior chats and recall them in future conversations. Memory is scoped to your user account, and you can manage it from **Settings** → **Memory**. | Mode | Details | | --------------- | ----------------------------------------------------------------------------- | | **Off** | Omni does not read or write memories for you | | **Chat memory** | Omni can remember facts from completed chat turns and use them in later chats | | **Full memory** | Chat memory plus agent run context when your organization allows it | Your admin sets the organization-wide maximum memory mode. You can choose a lower personal mode or delete stored memories at any time. ## Skills Skills are reusable Markdown instructions that guide the assistant through specific workflows. They ship with certain connectors (e.g. Google Drive and Gmail skills for the Google connector) and are automatically loaded when the connector is enabled. You can also create your own skills from the **Skills** page in the sidebar. Write instructions in Markdown, set visibility to **Public** to share them with your organisation, or keep them **Private** for personal use. Browse and clone public skills created by other users in your organisation. When the assistant encounters a relevant task, it searches available skills and applies matching ones to guide its response. ## Best Practices * **Ask follow-up questions** - The assistant remembers context within a conversation * **Be specific** - "What's our PTO policy for employees in California?" beats "PTO policy" * **Request formats** - Ask for bullet points, tables, or summaries as needed # Chat vs Search Source: https://docs.getomni.co/user-guide/chat-vs-search Two ways to find information in Omni Omni offers two modes for finding information. Toggle between them using the icons below the search bar. Omni mode toggle ## Chat Mode Use Chat mode when you want AI-synthesized answers with citations. The assistant searches your sources, can look up people in the directory, and can recall your previous chats when the answer is not in the workplace index. Omni Chat Mode **Best for:** * Questions that span multiple documents * Summarizing information * Looking up a colleague's role, department, or manager * Recalling a decision or answer from one of your previous chats * Follow-up questions in a conversation ## Search Mode Use Search mode when you want direct links to documents. Results are ranked by relevance and displayed as a traditional search results page. **Best for:** * Finding a specific document * Quick document lookup * Browsing available content ## When to Use Each | Use Chat Mode | Use Search Mode | | ------------------------------------------------------- | ----------------------- | | "What were the key decisions from the product meeting?" | "Q3 sales report" | | "Summarize our refund policy" | "employee handbook" | | "How do I set up SSO?" | "SSO configuration doc" | ## Chat-Only Lookups Chat can use capabilities that are separate from the document index: * **People directory**: Ask about a colleague by name, email, title, department, office, or manager. Directory records contain organization-visible fields and are separate from document permissions. * **Chat history**: Ask the assistant to find and read your own previous Omni conversations when the information is not available in connected workplace sources. Chat history is scoped to the current user. These are separate capabilities rather than ordinary document searches. ## Filters and Operators Both modes accept the same [search operators](/user-guide/search#search-operators) — `in:`, `type:`, `by:`, `before:`, and `after:` — plus connector-provided operators such as `from:` for email sources, Slack `channel:`, or Jira/GitHub `status:`. The filter panel (source, content type, author, date range) applies the same underlying filters. In chat mode, you can also ask the assistant to apply filters in natural language (e.g. "find recent engineering docs in Confluence"), and it will translate them into tool-level filters on your behalf. ## File Attachments (Chat Only) Chat mode lets you attach a file to a message (up to 50 MB). The assistant reads it alongside any indexed documents it retrieves. See [AI Assistant → File Attachments](/user-guide/ai-assistant#file-attachments) for details. Search mode doesn't accept attachments — it only queries the index. ## Document References (Chat Only) Chat mode lets you reference specific indexed documents by typing `@` followed by a search query. Select a document from the typeahead menu to inject its full content into the chat context. See [AI Assistant → Document References](/user-guide/ai-assistant#document-references) for details. Search mode does not support document references. # Projects Source: https://docs.getomni.co/user-guide/projects Organize chats, standing instructions, and context documents into durable workspaces Projects are personal workspaces that group related chats together with standing instructions and context documents. Every chat in a project inherits the project's instructions and can read the documents you attach, so you don't have to repeat the same context in every conversation. Projects are personal to your Omni account. They are not shared with other users, and they never grant access to documents you can't already see. ## Why Use Projects * **Keep related chats together** — a project collects every conversation about the same topic, launch, customer, or initiative. * **Set standing instructions once** — guidance you'd otherwise paste into every chat is applied automatically to each chat in the project. * **Attach reference material** — keep key indexed documents available as context across all of the project's chats. * **Scope your chat history** — browse and search only the conversations that belong to a project. ## Creating a Project 1. Open **Projects** in the sidebar. 2. Click **New Project** (or **Create your first project** on the empty state). 3. Fill in: * **Name** — required, and unique among your live projects (case-insensitive). * **Description** — optional summary shown on the project card. * **Instructions** — optional standing instructions applied to every chat in the project. 4. Click **Create project**. ## Project Instructions Instructions are included automatically in the system prompt of every chat in the project, alongside Omni's normal guidance. Use them for context and preferences that should always apply, for example: ```text theme={null} This project is about the Q3 launch. Prefer German responses and reference the roadmap. ``` You can edit instructions any time from the project detail page and save them with **Save instructions**. ## Context Documents The project detail page has a **Context documents** card. Search for an indexed document and attach it to make it available as standing context in every chat in the project. Attached documents are provided to the model as pointers rather than pasted into every prompt — the assistant reads the full content on demand with `read_document` when it's relevant. Detach a document at any time with the remove control next to it. Only documents you already have permission to read can be attached, and project membership never changes a document's permissions. ## Chats in a Project ### Starting a chat Open a project and click **New chat** to start a conversation inside it. The chat inherits the project's instructions and context documents. ### Moving an existing chat From an open chat, use the project menu in the chat header to move it into a project, switch it to a different project, or **Remove from project**. ### Deleting a project Deleting a project is permanent, but it does **not** delete its chats. The chats are kept and move back to **unorganized** so you don't lose their history. ## Archiving a Project Archiving hides a project from the main list without deleting anything. Toggle **Show archived** on the Projects page to see archived projects, and use **Unarchive** to bring one back. Archived projects are read-only: you can't start new chats or edit instructions and attachments until you unarchive them. ## Permissions Projects are owned by a single user and are never shared. All project access is mediated by ownership, and projects do not widen source permissions: * Instructions only affect the model's behavior; they cannot change what the assistant is allowed to read. * Context documents must be documents you can already access. * The assistant's document search inside a project is still filtered to your permissions. This keeps project instructions and attachments safe to use without worrying that they grant access beyond what you already have. ## What's Next Learn how to work with the assistant Search across your connected sources Understand document permissions # Search Source: https://docs.getomni.co/user-guide/search Find documents across all your connected sources ## Getting Started Type your query in the search bar. You can ask a question or enter keywords - Omni handles both. Omni Home ## Search Results After submitting a query in Search mode, you'll see a list of matching documents ranked by relevance. Click any result to open the original document. Omni Search Results ## Pagination Search results are paginated for large result sets. Use the page navigation at the bottom of the results to browse through all matches. ## Search Operators You can narrow a query inline using the following operators: | Operator | Example | Meaning | | --------- | -------------------------------- | --------------------------------------------------------------------- | | `in:` | `quarterly plan in:slack` | Restrict to a specific source (slack, google, atlassian, notion, ...) | | `type:` | `onboarding type:pdf` | Restrict to a content type (pdf, docx, spreadsheet, email, ...) | | `by:` | `policy by:alice@acme.com` | Restrict to documents authored or sent by a specific user | | `before:` | `roadmap before:2026-01-01` | Only documents last modified before the given date | | `after:` | `release notes after:2025-12-01` | Only documents last modified on or after the given date | Operators can be combined — for example, `onboarding in:google type:docx after:2025-09-01`. Date operators are interpreted in your saved timezone from **Settings** → **Profile**. If you have not saved a timezone, Omni falls back to UTC. Connectors can also add source-specific operators when they register with Omni. Examples include `channel:` for Slack, `folder:` or `subject:` for IMAP, `project:` / `status:` / `assignee:` for Jira and GitHub-style sources, and `tag:` / `correspondent:` for document-management connectors. These operators are available in both Search and Chat once the relevant connector is running. ## Filters Filters in the search UI (source, type, author, date range) apply the same operators as above and are available from both Search and Chat modes. ## Multilingual Search The search index uses ASCII folding and language-agnostic tokenization, so queries against documents in French, German, Spanish, and other non-English languages work without any extra configuration. ## Tips * **Be specific**: "Engineering onboarding checklist" works better than "onboarding" * **Ask naturally**: The AI understands conversational questions * **Use operators**: Combine `in:`, `type:`, and `after:` to narrow results quickly * **Use recent chats**: Continue previous conversations from the sidebar * **Recency matters**: More recent documents are given a ranking boost, so up-to-date content surfaces first