# Wexio.io | Conversational Automation Platform for Multi-Channel Messaging > Wexio.io is a conversational automation platform that helps businesses automate customer engagement across WhatsApp, Telegram, Instagram, and Viber with AI-powered flows, a unified inbox, and no-code tools. Teams use it to manage customer conversations across messaging channels from a single dashboard. ## Documentation - [Help Center llms.txt](https://learn.wexio.io/llms.txt): Machine-readable summary of Wexio.io help center documentation and guides. - [Help Center llms-full.txt](https://learn.wexio.io/llms-full.txt): Full-text help center documentation for AI models and LLM-powered tools. - [Read llms.txt](https://wexio.io/llms.txt): Concise machine-readable site description for AI models. - [Templates llms.txt](https://templates.wexio.io/llms.txt): Machine-readable description of Wexio.io's pre-built automation flow templates. ## Products ### Inbox | Omnichannel Messaging Inbox URL: https://wexio.io/products/inbox Unified inbox for all WhatsApp, Telegram, Instagram, and Viber conversations in one place. Team collaboration, AI-powered actions, spam protection, smart collections, and real-time messaging across every channel. - Multi-channel support: WhatsApp Business API, Telegram Bot API, Instagram DMs, Viber Business - Team collaboration: assign conversations, internal notes, @mentions, shared drafts - AI-powered actions: auto-replies, message classification, sentiment analysis - Smart collections: filter and organize conversations by channel, status, assignee, tags - Spam protection: multi-layer spam filtering with content analysis and rate limiting - Real-time messaging: instant message delivery and read receipts ### Flow | Visual No-Code Automation Builder URL: https://wexio.io/products/flow Drag-and-drop visual automation builder for customer engagement flows. AI cards, WhatsApp template messages, version history, undo/redo, conditional branching, and smart publishing across all channels. - Visual builder: drag-and-drop canvas with zoom, pan, and grid snapping - AI cards: AI Classification, AI Agent, Text to Speech, Speech to Text - WhatsApp templates: pre-approved message templates with dynamic variables - Version history: full version control with rollback support - Conditional branching: if/else logic, switch cases, variable conditions - Multi-channel publishing: deploy flows to WhatsApp, Telegram, Instagram, Viber simultaneously ### AI | Smart AI Assistants URL: https://wexio.io/products/ai Built-in AI assistant powered by OpenAI and Anthropic. AI Classification, AI Agent, Text to Speech, Speech to Text, auto-transcription, image analysis, and document analysis for intelligent conversation handling. - AI Classification: categorize incoming messages by intent, sentiment, or topic - AI Agent: autonomous conversation handling with configurable personality and knowledge base - Text to Speech: convert text responses to voice messages - Speech to Text: transcribe voice messages to text automatically - Image analysis: understand and respond to images sent by customers - Document analysis: extract information from PDFs, documents, and files ### People | Contact Management & CRM Sync URL: https://wexio.io/products/people Contact management with CRM sync to HubSpot, Salesforce, and Google BigQuery. Customer profiles, conversation history, and segmentation. - Contact profiles: unified customer view across all channels - Conversation history: full message history per contact - CRM sync: bidirectional sync with HubSpot, Salesforce, Google BigQuery - Segmentation: group contacts by tags, properties, and behavior ### Broadcasts | Scheduled Bulk Messaging URL: https://wexio.io/products/broadcasts Send scheduled broadcasts to a filtered audience across every channel. - Audience filters: target by tags, properties, and behavior - Delivery status: real-time tracking with retries and opt-outs - Payloads: send a flow or a template message ## Channels Dedicated product pages for each messaging channel Wexio supports. ### WhatsApp URL: https://wexio.io/products/whatsapp Answer customers on WhatsApp with a shared inbox, AI replies, and scheduled broadcasts. ### Web widget URL: https://wexio.io/products/web A smart chat widget for your website. It greets visitors, answers questions from your help articles, and captures leads day and night, with a home tab, messages, FAQs, articles, and profile. ### Instagram URL: https://wexio.io/products/instagram Handle Instagram DMs, welcome new conversations, and follow up, all from one shared inbox. ### Telegram URL: https://wexio.io/products/telegram Automate Telegram chats for business and personal accounts with AI replies and broadcasts. ### Viber URL: https://wexio.io/products/viber Reach customers on Viber with instant answers, follow-ups, and broadcasts. ## Resources - [Wexio.io Homepage](https://wexio.io): Platform overview, features, customer testimonials, and getting started guide for the conversational automation platform. - [About Us](https://wexio.io/about): Learn about Wexio, our mission to simplify business messaging, and the team behind the platform. - [Pricing | Plans Built to Scale Your Business](https://wexio.io/pricing): Wexio.io offers 4 plans: Free ($0/mo), Standard ($16.50/mo), Pro ($29.89/mo), and Enterprise (custom). Save 15% with yearly billing. Pay only for the operations you use. - [Blog | Articles and Guides](https://wexio.io/blog): Articles, tutorials, and guides about conversational automation, multi-channel messaging, AI, and business growth strategies. - [What's New | Release Notes](https://wexio.io/whats-new): Latest product updates, new features, improvements, and fixes for the Wexio.io platform. - [FAQ | Frequently Asked Questions](https://wexio.io/faq): 66 questions and answers across 11 categories covering getting started, pricing, products, security, and more. - [Integrations | Supported Channels and Tools](https://wexio.io/integrations): Connect WhatsApp Business API, Telegram Bot API, Instagram DMs, Viber Business, ChatGPT, Claude, HubSpot, Salesforce, and BigQuery. - [Help Center](https://learn.wexio.io): Step-by-step guides for every feature of the Wexio.io platform. - [Contact Us](https://wexio.io/contact): Get in touch with the Wexio team for questions, partnerships, or support. - [Talk to Sales](https://wexio.io/talk-to-sales): Schedule a call with our sales team to discuss Enterprise plans and custom solutions. - [Careers](https://wexio.io/careers): Join the Wexio team. View open positions and learn about our culture. - [Press](https://wexio.io/press): Wexio.io in the news. Press releases, media coverage, and brand assets. ## Integrations ### Messaging Channels - [WhatsApp Business API](https://wexio.io/integrations): Connect WhatsApp Business API to send and receive messages, templates, and media at scale. Supports text, images, videos, documents, locations, contacts, and interactive messages (buttons, lists). - [Telegram Bot API](https://wexio.io/integrations): Connect a Telegram Bot to automate conversations with customers over Telegram. Supports text, media, inline keyboards, and bot commands. - [Instagram Direct Messages](https://wexio.io/integrations): Connect an Instagram Business account to manage DMs from a unified inbox. Supports text, images, stories, and quick replies. - [Viber Business](https://wexio.io/integrations): Connect Viber Business to chat with customers over Viber. Supports text, images, files, and rich messages. ### AI Providers - [ChatGPT (OpenAI GPT-4)](https://wexio.io/integrations): Use OpenAI's GPT-4 for AI classification, AI agents, text generation, and document analysis. - [Claude (Anthropic)](https://wexio.io/integrations): Use Anthropic's Claude for AI-powered conversation handling and analysis. ### CRM & Data - [HubSpot](https://wexio.io/integrations): Sync contacts and deal data between Wexio.io and HubSpot CRM. Bidirectional sync with custom field mapping. - [Salesforce](https://wexio.io/integrations): Sync contacts and customer data between Wexio.io and Salesforce CRM. Full object mapping and sync configuration. - [Google BigQuery](https://wexio.io/integrations): Export conversation and contact data to Google BigQuery for analytics and reporting. ## Industry Solutions - [Automotive | Dealership & Service Messaging](https://wexio.io/solutions/automotive): 24/7 dealership inquiry handling, lead capture, test drive scheduling, and service appointment reminders across WhatsApp, Telegram, Instagram, and Viber. - [Beauty & Spa | Client Communication](https://wexio.io/solutions/beauty): Focus on clients, not messages. Automate service inquiries, booking confirmations, and follow-ups for beauty salons and spas. - [Education | Student & Parent Engagement](https://wexio.io/solutions/education): Automate student and parent communication, course FAQs, enrollment processes, and campus updates. - [Entertainment & Events | Fan Engagement](https://wexio.io/solutions/entertainment): Fan engagement, event FAQs, ticket information, and real-time updates for entertainment businesses. - [Finance & Banking | Client Communication](https://wexio.io/solutions/finance): Secure client communication, lead qualification, service inquiries, and compliance-friendly messaging for financial services. - [Food & Grocery | Customer Support](https://wexio.io/solutions/food): Product FAQs, store information, order updates, and customer support for food and grocery businesses. - [Hotel & Lodging | Guest Communication](https://wexio.io/solutions/hotel): Guest communication, room inquiries, amenity FAQs, check-in/out reminders, and concierge services. - [Medical & Healthcare | Patient Communication](https://wexio.io/solutions/medical): Patient communication, clinic FAQs, appointment information, and follow-up reminders for healthcare providers. - [Shopping & Retail | Product Inquiries](https://wexio.io/solutions/retail): Product inquiries, customer support, lead capture, and order updates for retail businesses. - [Restaurant | Guest Engagement](https://wexio.io/solutions/restaurant): Menu FAQs, reservation information, guest engagement, and loyalty program communication for restaurants. - [Professional Services | Client Inquiries](https://wexio.io/solutions/professional): Client inquiries, consultation requests, lead qualification, and project updates for professional service firms. - [Travel & Transportation | Booking Support](https://wexio.io/solutions/transport): Tour inquiries, travel FAQs, booking support, and trip updates for travel and transportation businesses. ## Pricing ### Free Plan - $0/month - 1 team seat - 1 flow - 100 operations/month - 7-day message retention - All messaging channels included - Basic support ### Standard Plan - $19.69/month ($16.50/month billed yearly) - 4 team seats - 10 flows - Up to 80,000 operations/month - 30-day message retention - AI auto-replies included - Email support ### Pro Plan - $35.45/month ($29.89/month billed yearly) - 20 team seats - 30 flows - Up to 3,000,000 operations/month - 90-day message retention - AI auto-replies included - Priority support ### Enterprise Plan - Custom pricing - Unlimited team seats - Unlimited flows - 3,000,000+ operations/month - Unlimited message retention - Dedicated support - SLA guarantees - Custom integrations ## FAQs - [Getting Started](https://wexio.io/faq): 5 questions about signing up, connecting channels, sending first messages, and platform basics. - [About Wexio.io](https://wexio.io/faq): 5 questions about what Wexio.io is, who it's for, and how it compares to alternatives. - [Pricing & Billing](https://wexio.io/faq): 14 questions about plans, upgrades, downgrades, cancellations, payment methods, refunds, and Enterprise pricing. - [Operations (Ops) Usage](https://wexio.io/faq): 10 questions about how operations are counted, usage limits, overages, and optimization. - [Unified Inbox](https://wexio.io/faq): 4 questions about multi-channel inbox features, team collaboration, and conversation management. - [Flow Builder](https://wexio.io/faq): 4 questions about the visual automation builder, AI cards, triggers, and publishing flows. - [Templates](https://wexio.io/faq): 4 questions about pre-built templates, customization, and sharing. - [Account & Team](https://wexio.io/faq): 5 questions about team management, roles, permissions, and account settings. - [AI Features](https://wexio.io/faq): 4 questions about AI classification, AI agents, text-to-speech, and document analysis. - [Security & Privacy](https://wexio.io/faq): 8 questions about data protection, GDPR compliance, encryption, infrastructure, and security practices. - [Getting Help](https://wexio.io/faq): 3 questions about support channels, documentation, and community resources. ## Legal - [Privacy Policy](https://wexio.io/privacy): How Wexio collects, uses, and protects your personal data. GDPR-compliant. - [Terms of Service](https://wexio.io/terms): Terms and conditions for using the Wexio.io platform. - [Cookie Policy](https://wexio.io/cookie-policy): How Wexio.io uses cookies and similar technologies. - [Security](https://wexio.io/security): Enterprise-level security practices including AES-256-GCM encryption, TLS 1.3, SOC 2 / ISO 27001 certified cloud providers, and EU infrastructure. - [Subprocessors](https://wexio.io/subprocessors): List of third-party subprocessors used by Wexio.io including AWS, Vercel, MongoDB Atlas, OpenAI, and Anthropic. - [Ethics](https://wexio.io/ethics): Our commitment to ethical AI, responsible messaging, and fair business practices. - [Disclaimer](https://wexio.io/disclaimer): Legal disclaimers for website content, third-party links, and service availability. ## Company - **Name**: Wexio - **Location**: Ukraine - **Infrastructure**: EU (Ireland) - AWS eu-west-1, Vercel, MongoDB Atlas, Upstash Redis - **Compliance**: GDPR, AES-256-GCM encryption, TLS 1.3 - **Auth**: Google, GitHub, Microsoft OAuth + Passkeys + 2FA - **Supported Languages**: English, German, Polish, Ukrainian, Italian, Portuguese - **Supported Channels**: WhatsApp, Web widget, Telegram, Instagram, Viber - **AI Partners**: OpenAI (GPT-4), Anthropic (Claude) - **CRM Integrations**: HubSpot, Salesforce, Google BigQuery --- # Welcome to Wexio (/docs) Wexio is a powerful platform that helps businesses manage customer conversations, automate workflows, and scale their communication - all from a single workspace. What is Wexio? [#what-is-wexio] Wexio connects your messaging channels (Telegram, WhatsApp, Instagram, Viber) into a unified inbox, lets you build automated flows, manage contacts, and leverage AI assistants - all without writing code. Quick Links [#quick-links] Core Features [#core-features] | Feature | Description | | ---------------- | ----------------------------------------------------------------------------------------------------- | | **Inbox** | Unified messaging hub - search, chat with message types, editor with AI, collections, contact profile | | **People** | Customer database with contacts, custom fields, CRM sync, and bulk operations | | **Flows** | Visual automation builder with 27 card types across 6 categories, 7 trigger types, version history | | **AI** | Assistants, copilot, 8 AI actions, knowledge base, multi-provider support (OpenAI, Anthropic) | | **Templates** | Browse and share reusable flow templates across the community | | **Library** | Quick replies with variables and WhatsApp message templates | | **Integrations** | 4 channels (Telegram, WhatsApp, Instagram, Viber), CRM (HubSpot, Salesforce, BigQuery), AI providers | | **Billing** | 5 plan tiers (Free → Enterprise) with operation-based pricing and AI token billing | | **Security** | AES-256-GCM encryption, SSO/SAML, 2FA, passkeys, RBAC (Owner/Admin/Editor/Agent) | | **Support** | Built-in ticket system for bug reports, feature requests, and help | Platform [#platform] # Ops Usage (/docs/ops-usage) Operations (ops) are the unit of measurement for activity in Wexio. Each action that processes or sends messages consumes ops from your monthly allocation. What Are Operations? [#what-are-operations] Your plan includes a monthly ops allocation that resets at the start of each billing cycle. Every message sent or received, every flow card executed, and every CRM sync consumes ops from this allocation. *** Message Operations [#message-operations] | Action | Ops Cost | | ------------------------- | -------- | | Receive a message | 1 | | Send a message (manual) | 1 | | Send a message (bot/flow) | 1 | | Send a message (AI) | 1 | *** Flow Card Operations [#flow-card-operations] When a flow executes, each card that sends a message or performs an action consumes ops. Basic Cards - 1 op each [#basic-cards---1-op-each] | Card | Description | | -------------- | ------------------------------------ | | Text | Send a text message | | Button | Send a message with buttons | | Media | Send a photo or video | | Audio | Send an audio file | | Document | Send a document | | Question | Ask a question and wait for response | | Flow | Trigger another flow | | Update Contact | Update a contact profile field | Advanced Cards - 2 ops each [#advanced-cards---2-ops-each] | Card | Description | | --------------- | ---------------------------- | | Media Button | Message with media + buttons | | Audio Button | Audio with buttons | | Document Button | Document with buttons | | AI Text | AI-generated dynamic content | | Conditional | Branching logic evaluation | | Fetch | External API request | CRM Card - 1 op per field [#crm-card---1-op-per-field] The CRM Card updates fields in your connected CRM during flow execution. Each field updated costs **1 op**. | Fields Updated | Ops Cost | | -------------- | -------- | | 1 field | 1 | | 3 fields | 3 | | 5 fields | 5 | No-Cost Cards - 0 ops [#no-cost-cards---0-ops] | Card | Description | | ----- | ----------------------------------- | | Note | Internal note (not sent to contact) | | Ghost | Placeholder for flow structure | *** CRM Sync Operations [#crm-sync-operations] CRM import and export operations consume ops based on the number of contacts and field mappings. | Operation | Formula | | ------------------- | ------------------------- | | **Export to CRM** | contacts × (0.2 × fields) | | **Import from CRM** | contacts × (0.3 × fields) | Import costs more than export because it requires validation, deduplication, and field transformation. Only successfully synced contacts are charged - failed records don't consume ops. Export Examples [#export-examples] | Contacts | Field Mappings | Ops Cost | | -------- | -------------- | -------- | | 10 | 30 | 60 | | 100 | 10 | 200 | | 500 | 5 | 500 | Import Examples [#import-examples] | Contacts | Field Mappings | Ops Cost | | -------- | -------------- | -------- | | 100 | 10 | 300 | | 500 | 20 | 3,000 | | 1,000 | 30 | 9,000 | *** AI Ops [#ai-ops] All AI features - both **AI Auto-Reply** and **AI Copilot** - use a unified token-based billing model. AI usage is tracked as **AI Ops** and counted against your plan's AI limit. How AI Ops Are Calculated [#how-ai-ops-are-calculated] ``` AI ops consumed = tokens used × token cost rate ``` Token cost rates vary by model tier: | Model Tier | Rate (per token) | 1,000 tokens = | Example Models | | ------------ | ---------------- | -------------- | -------------------------- | | **Economy** | 0.0004 | 0.4 AI ops | GPT-4.1 Mini, GPT-4.1 Nano | | **Standard** | 0.0008 | 0.8 AI ops | GPT-4.1, Claude Sonnet | | **Premium** | 0.0015 | 1.5 AI ops | GPT-4.5, O1, Claude Opus | Example Costs Per Action [#example-costs-per-action] | Action | Model Tier | \~Tokens | AI Ops | | ---------------------------- | ---------- | -------- | ------ | | Fix grammar / simplify text | Economy | \~300 | 0.12 | | Suggest reply with context | Economy | \~800 | 0.32 | | Suggest reply with context | Standard | \~800 | 0.64 | | Summarize conversation | Economy | \~1,500 | 0.6 | | AI auto-reply (with history) | Economy | \~1,200 | 0.48 | | Custom prompt (long) | Premium | \~2,000 | 3.0 | AI Limits by Plan [#ai-limits-by-plan] AI limits scale with your plan's ops limit: | Plan | Ops | AI Limit | | ------------ | --------- | -------- | | Free | 100 | 0 | | Standard 10K | 10,000 | 1,000 | | Standard 80K | 80,000 | 8,000 | | Pro 10K | 10,000 | 1,000 | | Pro 1M | 1,000,000 | 100,000 | | Pro 2M | 2,000,000 | 200,000 | | Enterprise | Custom | Custom | What Counts as AI [#what-counts-as-ai] Both auto-reply and copilot share the same AI ops pool: * **AI Auto-Reply** - Bot sends AI-generated responses to contacts * **AI Copilot** - Operators use suggest reply, translate, summarize, tone change, etc. Assistants using your **own API key** (BYOK) are not counted against your AI limit - you pay the provider directly. *** Understanding Ops Consumption [#understanding-ops-consumption] Flow Execution Example [#flow-execution-example] ``` 1. Text Card: "Welcome!" → 1 op 2. Button Card: "Choose an option" → 1 op 3. Conditional Card: Check selection → 2 ops 4. Text Card: "You selected X" → 1 op Total: 5 ops ``` AI Conversation Example [#ai-conversation-example] ``` 1. Contact sends message → 1 op (receive) 2. AI generates response → 1 op (send) + AI ops Total: 2 ops + AI ops ``` Important Notes [#important-notes] * **Ops are charged when messages are sent** - if a Question Card is waiting for input, ops are charged when the question is displayed, not when the response is processed. * **Re-processing doesn't double-charge** - if a contact provides invalid input and the same question is shown again, it's only charged once per actual message sent. * **Failed operations** - if a card fails to execute, ops are not charged for the failure. *** Ops Limits by Plan [#ops-limits-by-plan] | Plan | Monthly Ops | | ---------- | ------------------ | | Free | 100 | | Standard | 10,000 – 80,000 | | Pro | 10,000 – 3,000,000 | | Enterprise | Custom | What Happens When You Run Out [#what-happens-when-you-run-out] * Flows stop executing * AI responses are disabled * Manual inbox messages still work (with a warning) * You can purchase additional ops or upgrade your plan Do Unused Ops Roll Over? [#do-unused-ops-roll-over] No - ops reset at the start of each billing cycle. *** Monitoring Usage [#monitoring-usage] You can track your ops and AI ops consumption in two places: Inbox - Live Usage Widget [#inbox---live-usage-widget] Click the **Usage** button in the bottom-right corner of the inbox to see a live summary of your current usage: * **Operations** - current ops used out of your plan limit (e.g. 39.50 / 10K) * **AI Operations** - current AI ops used out of your AI limit (e.g. 64.84 / 1K) This updates in real time as messages are sent and received. Settings - Billing Page [#settings---billing-page] Go to **Settings → Billing** for a full overview of your subscription and usage: * Plan name and status (Active) * Renewal date and days remaining * **Operations** progress bar - used vs. allocation with percentage * **AI Operations** progress bar - used vs. allocation with percentage * Change Plan or Cancel Subscription actions *** Optimizing Ops Usage [#optimizing-ops-usage] Combine Messages [#combine-messages] Use one message with multiple lines instead of multiple Text Cards. ``` ❌ Inefficient (4 ops): Text Card → "Hello" Text Card → "How can I help?" Text Card → "Please choose:" Button Card → [Options] ✅ Efficient (1 op): Button Card → "Hello! How can I help?\nPlease choose:" + [Options] ``` Other Tips [#other-tips] * **Use conditional logic wisely** - place Conditional Cards strategically to avoid unnecessary message sends * **Optimize AI usage** - AI responses consume both ops and AI ops; use them where they add the most value * **Choose the right AI model tier** - Economy models cost significantly less AI ops than Premium models for routine tasks *** Related [#related] # Security (/docs/security) Wexio implements a comprehensive, multi-layered security architecture to protect your data and ensure compliance with global data protection regulations. This page provides an overview of the security measures in place across the platform. Encryption [#encryption] Multi-Layered Encryption [#multi-layered-encryption] All sensitive data in Wexio is encrypted using **AES-256-GCM**, the industry-standard encryption algorithm. The platform uses a per-organisation, per-context key derivation system to ensure maximum security isolation. **Key derivation** follows [RFC 5869 (HKDF)](https://datatracker.ietf.org/doc/html/rfc5869) with SHA-512, meaning each organisation gets unique encryption keys derived from a master secret. This ensures that even if one key were compromised, other organisations and contexts remain protected. Encryption Contexts [#encryption-contexts] Different types of sensitive data are encrypted with separate, context-specific keys. See [Integrations](/docs/settings/integrations) for connecting channels and [AI Settings](/docs/settings/ai-settings) for AI provider configuration. | Context | What's Encrypted | | --------------- | ------------------------------ | | AI Integrations | OpenAI, Anthropic API keys | | Telegram | Bot tokens | | Viber | Bot tokens | | Webhooks | Webhook secrets and signatures | | System AI | System-level AI configuration | | Authentication | Authentication tokens | Each encryption operation includes **Additional Authenticated Data (AAD)** binding the ciphertext to its organisation and context, preventing context confusion attacks where encrypted data could be moved between organisations. Data in Transit [#data-in-transit] All data transmitted between your browser and Wexio servers is protected by: * **TLS 1.2 / TLS 1.3** encryption on all connections * Modern cipher suites only (ECDHE-based) * Automatic HTTP → HTTPS redirect * **HSTS** (HTTP Strict Transport Security) with a 1-year max-age WebSocket connections (used for real-time chat updates) are also encrypted via WSS (WebSocket Secure). *** Infrastructure Security [#infrastructure-security] Wexio's infrastructure runs on enterprise-grade cloud providers that hold SOC 2 Type II and ISO 27001 certifications (AWS and MongoDB Atlas). These certifications belong to those providers; Wexio itself is not independently SOC 2 audited yet, though it is on our roadmap. | Provider | Role | Region | Certifications | | ----------------- | ----------------------------------- | ----------------------- | ----------------------------------------------------------- | | **AWS** | Primary cloud infrastructure | EU (Ireland, eu-west-1) | SOC 1/2/3, ISO 27001, ISO 27017, ISO 27018, PCI DSS Level 1 | | **Vercel** | Edge hosting and serverless compute | EU (Ireland, dub1) | Global CDN with DDoS protection and WAF | | **MongoDB Atlas** | Managed database | EU (Ireland) | SOC 2 Type II, ISO 27001 | | **Upstash Redis** | Rate limiting, caching, sessions | EU (Ireland) | Encrypted at rest and in transit | * **Network segmentation** - Production, staging, and development environments are strictly isolated using Virtual Private Clouds (VPCs) with granular security group rules. No direct internet access to backend services. * **DDoS protection** - Multi-layer DDoS mitigation through AWS Shield and Vercel's edge network with automatic traffic scrubbing and rate limiting at the edge. * **Continuous monitoring** - Real-time infrastructure monitoring with automated alerting for anomalous activity, resource utilisation, and security events. *** Network Security [#network-security] CORS Policy [#cors-policy] Wexio enforces a strict Cross-Origin Resource Sharing (CORS) policy. Only requests from trusted `*.wexio.io` origins and authorised local development servers are accepted - all other origins are rejected. Security Headers [#security-headers] The platform sets robust security headers on all responses: | Header | Purpose | | --------------------------------- | -------------------------------------- | | `Strict-Transport-Security` | Forces HTTPS for all future requests | | `X-Frame-Options: DENY` | Prevents clickjacking attacks | | `X-Content-Type-Options: nosniff` | Prevents MIME-type confusion attacks | | `Cross-Origin-Resource-Policy` | Controls cross-origin resource sharing | WebSocket connections use WSS (encrypted WebSocket) with the same JWT authentication as HTTP requests, preventing unauthenticated real-time connections. *** Data Isolation & Multi-Tenancy [#data-isolation--multi-tenancy] All organisations share a common platform by default, with strict logical isolation at the database level. For customers with advanced compliance requirements, Pro and Enterprise plans offer the option to provision a completely dedicated MongoDB database and Redis instance for full physical data separation. * **Tenant-scoped queries** - A middleware layer automatically injects the authenticated organisation ID into every database operation, preventing cross-tenant data leakage even in the event of application-level bugs. * **Dedicated database option** - Organisations on Pro and Enterprise plans can migrate to a physically isolated MongoDB instance and/or Redis, with full data migration or fresh-start modes. * **Environment separation** - Production, staging, and development environments are completely isolated. Testing never uses production data. * **Organisation deletion cascade** - When an organisation is removed, all associated data (messages, media, integrations, flows, contacts) is permanently deleted across all storage layers. *** Authentication & Authorization [#authentication--authorization] Authentication Methods [#authentication-methods] Wexio supports multiple authentication methods to suit your security requirements. No passwords are stored on the platform - all authentication is handled through secure OAuth 2.0 providers, passkeys, or enterprise SSO. See [Account Security](/docs/account/security) for setup instructions. | Method | Description | | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | **OAuth 2.0** | Sign in with Google, Microsoft, or GitHub - no password-based authentication exists, eliminating an entire class of credential attacks | | **Two-Factor Authentication (2FA)** | TOTP-based 30-second codes with 10 one-time recovery codes. Failed login attempts trigger account lockout to prevent brute-force attacks | | **Passkeys / WebAuthn** | Hardware security key and biometric authentication via the WebAuthn standard, providing phishing-resistant credentials | | **Enterprise SSO (SAML 2.0)** | Centralised access management through your own identity provider | Sessions [#sessions] JWT access tokens (short-lived) with refresh token rotation. Database-backed sessions with 30-day expiry and device fingerprinting, ensuring that each login is tracked and can be individually revoked. Google reCAPTCHA is applied on all public-facing forms for bot detection and abuse prevention. Role-Based Access Control (RBAC) [#role-based-access-control-rbac] Every user in an organisation is assigned a role that determines their permissions. See [Team Settings](/docs/settings/team) for role management. | Role | Capabilities | | ---------- | ---------------------------------------------------------------- | | **Owner** | Full access including billing, org deletion, and team management | | **Admin** | Manage settings, integrations, and team members | | **Editor** | Manage flows, templates, and content; limited settings access | | **Agent** | Chat with contacts, view people, limited settings access | Organisation Isolation [#organisation-isolation] Every database query is automatically scoped to your organisation - users can never access data from other organisations, ensuring complete data isolation. Every GraphQL resolver is protected by layered guards: JWT authentication, organisation access, chat-level access, role verification, and plan usage limits. Enterprise SSO (SAML 2.0) [#enterprise-sso-saml-20] Enterprise organisations can authenticate users through their own Identity Provider using SAML 2.0. See [Account Security](/docs/account/security) for SSO setup. | Feature | Description | | --------------------- | ----------------------------------------------------------------------------------------------------------------- | | **Supported IdPs** | Okta, Azure AD (Entra ID), OneLogin, Google Workspace, Auth0, PingFederate | | **Domain mapping** | Automatic IdP detection based on the user's email domain | | **SSO enforcement** | Require all org members to authenticate through the configured IdP, disabling direct OAuth login | | **Auto-provisioning** | New users automatically created on first SSO login with a configurable default role | | **Account linking** | Existing Wexio accounts (via Google, GitHub, or Microsoft OAuth) can be linked to the organisation's SSO provider | *** Input Validation & Injection Prevention [#input-validation--injection-prevention] GraphQL Security [#graphql-security] All API inputs are validated through multiple layers: * **Schema-level validation** - Type checking enforced by the GraphQL schema * **DTO validation** - All input fields validated with strict rules (length limits, format checks, type constraints) * **Query depth limiting** - Maximum 12 levels of nesting to prevent recursive query abuse * **Introspection disabled in production** - API schema not exposed to potential attackers Injection Prevention [#injection-prevention] * **NoSQL injection** - All user-provided search patterns are escaped before use; no dangerous operators are exposed * **XSS protection** - HTML content is filtered through strict allowlists, permitting only safe formatting tags * **Input length limits** - All text inputs have maximum length constraints Webhook Verification [#webhook-verification] All incoming webhooks from third-party providers are cryptographically verified. See [Integrations](/docs/settings/integrations) for channel setup. | Provider | Verification Method | | --------------- | ------------------------------------------ | | Stripe | Signature verification with webhook secret | | WhatsApp / Meta | HMAC-SHA256 signature verification | | Viber | Token-based verification | | Telegram | Bot token verification | *** Rate Limiting & Spam Protection [#rate-limiting--spam-protection] Wexio implements a comprehensive, multi-layered rate limiting and spam protection system to protect both the platform and your end users from abuse. See [Spam Protection Settings](/docs/settings/inbox/spam-protection) for configuration. Rate Limiting Layers [#rate-limiting-layers] | Layer | Scope | Limit | Purpose | | ------------ | ---------------- | -------------------- | --------------------------- | | Network | Per IP | 10 req/s, burst 20 | Prevents network-level DDoS | | Request body | Per request | 10 MB JSON | Prevents oversized payloads | | File upload | Per request | 10 MB/file, 10 files | Prevents upload abuse | | API | Per organisation | Tiered limits | Prevents API abuse | Inbound Spam Protection [#inbound-spam-protection] When end users send excessive messages to your bots, Wexio automatically: 1. **Throttles** - Silently skips processing for rapid-fire messages while still saving them to chat history 2. **Warns** - Sends a system message asking the user to slow down 3. **Blocks** - Temporarily or permanently blocks the user based on your organisation's settings Messages from blocked users are still saved to the database for audit purposes - only automated processing (flows, AI) is skipped. Operators can always view the messages. Message Deduplication [#message-deduplication] Wexio uses a unique "latest wins" deduplication system. When a user rapidly sends multiple identical messages (e.g., spamming `/start`), only the **last** message is processed - preventing duplicate flow executions and bot responses. Outbound Spam Protection [#outbound-spam-protection] The platform protects your end users from accidental message flooding caused by misconfigured flows or infinite loops: Per-Chat Protection [#per-chat-protection] * **Loop detection** - If a flow card is visited 5+ times in one execution, the flow is automatically cancelled * **Message flood cap** - If a single flow execution sends 50+ messages, it's cancelled * **Rate limiting** - If 30+ messages are sent to one chat within 40 seconds, outbound is paused for that chat Organisation-Level Escalation [#organisation-level-escalation] If 3 or more chats trigger outbound violations within 5 minutes, Wexio temporarily blocks all outbound messaging for the entire organisation for 5 minutes: * All active flows are cancelled * Inbound messages **continue to arrive** normally (no data loss) * Operators can still view chats and read messages * All online org members receive a real-time WebSocket notification * Outbound automatically resumes after 5 minutes Organisation-level blocks are designed as a safety net. They automatically expire after 5 minutes, and all block events are persisted for audit purposes. Spam Protection Settings [#spam-protection-settings] Each organisation can configure their inbound spam protection behaviour: | Setting | Options | Default | | ------------------- | ------------------------------------------------- | --------------- | | **Spam protection** | Enabled / Disabled | Enabled | | **Block mode** | Throttle only / Temporary block / Permanent block | Temporary block | | **Block duration** | 1–60 minutes | 5 minutes | Rate limit thresholds and outbound protection limits are hardcoded with smart defaults and are not configurable - this ensures consistent protection across all organisations. API Rate Limits [#api-rate-limits] Public API endpoints include rate limiting with standard response headers: ``` X-RateLimit-Limit: 200 # Max requests in current window X-RateLimit-Remaining: 142 # Requests left in current window X-RateLimit-Reset: 1710453795 # Unix timestamp when window resets ``` When rate limited, the API returns `429 Too Many Requests` with a `Retry-After` header. *** File Upload Security [#file-upload-security] All file uploads go through multiple security checks: | Protection | Description | | -------------------------- | --------------------------------------------------------------------------------------------------------- | | **Size limits** | 10 MB per file, 10 files per request | | **Filename sanitization** | Filenames are sanitized with UUID prefixes and special character replacement | | **Type validation** | File types are validated against an allowlist | | **Magic bytes validation** | Actual file content is verified to match the claimed MIME type - prevents disguised executables | | **Malware scanning** | Files are scanned in real time by AWS GuardDuty. Infected files are automatically quarantined and deleted | | **Storage isolation** | Files are stored in organisation-scoped folders | | **Signed URLs** | Downloads use presigned URLs with automatic expiry | *** Data Retention & GDPR Compliance [#data-retention--gdpr-compliance] Automatic Data Retention [#automatic-data-retention] Wexio implements configurable data retention policies that automatically clean up old messages based on your plan and settings. A daily cleanup job runs at 3:00 AM to delete messages older than the configured period. See [Database Settings](/docs/settings/database) for retention configuration. Retention Limits by Plan [#retention-limits-by-plan] Retention limits depend on your [plan tier](/docs/settings/billing): | Plan | Default Retention | Maximum Retention | Configurable | | -------------- | ----------------- | ----------------- | ------------ | | **Free** | 7 days | 7 days | No | | **Standard** | 30 days | 30 days | Yes | | **Pro** | 90 days | 90 days | Yes | | **Enterprise** | 180 days | Unlimited | Yes | Retention Settings [#retention-settings] Organisations on eligible plans can configure: * **Message retention period** - How long messages are kept before automatic deletion * **Media retention** - Whether to follow message retention or set a separate period * **Delete media with messages** - Automatically remove associated media files when messages are deleted * **Preserve starred chats** - Keep messages in starred chats regardless of retention policy GDPR Compliance [#gdpr-compliance] Wexio is designed with privacy by design and implements key GDPR requirements: | GDPR Article | Requirement | Implementation | | ------------ | ---------------------- | -------------------------------------------------------- | | Art. 5(1)(e) | Storage limitation | Automatic retention cleanup | | Art. 17 | Right to erasure | Organisation deletion with full data cascade | | Art. 25 | Privacy by design | Per-organisation encryption keys with context separation | | Art. 32 | Security of processing | AES-256-GCM encryption for all sensitive data | *** Compliance & Certifications [#compliance--certifications] Wexio is committed to maintaining compliance with applicable data protection regulations and industry standards. * **GDPR** - Wexio processes personal data in accordance with (EU) 2016/679. We act as a Data Processor and maintain Data Processing Agreements (DPAs) with all customers covering Article 28 requirements. * **Infrastructure certifications** - All services hosted in EU (Ireland). AWS maintains SOC 1/2/3, ISO 27001, ISO 27017, ISO 27018, and PCI DSS Level 1 certifications. MongoDB Atlas is SOC 2 Type II and ISO 27001 certified. * **PCI DSS** - Payment processing is fully delegated to Stripe, a PCI DSS Level 1 certified provider. Wexio does not store, process, or transmit cardholder data. * **Vulnerability management** - Continuous dependency scanning with Dependabot and Snyk, with critical security patches prioritised. Third-party penetration testing and a bug bounty programme are planned as we grow. *** Incident Response [#incident-response] Wexio maintains a formal Incident Response Plan following NIST SP 800-61 guidelines, with clear procedures for detecting, containing, investigating, and recovering from security incidents. * **Documented response plan** covering identification, containment, eradication, recovery, and post-incident review phases with defined severity levels and escalation paths. * **Breach notification** within 72 hours to affected customers and relevant supervisory authorities as required by GDPR Article 33, including the nature of the breach, affected data, and remediation steps. * **Founder-led incident response today** - security events are monitored and triaged directly by the founder, with defined severity levels and escalation paths. Formal on-call rotations and tabletop exercises will be introduced as the team grows. * **Blameless post-mortem analysis** for every security incident - root causes identified, corrective actions tracked to completion, and lessons learned incorporated into security practices. *** Security Layers Overview [#security-layers-overview] Wexio's security is built in five layers, each providing defence in depth: | Layer | Components | | ---------------------- | ------------------------------------------------------------------------------------------------------- | | **1. Network** | TLS 1.3, CORS policy, rate limiting, security headers, DDoS protection | | **2. Authentication** | JWT tokens, refresh token rotation, OAuth2, 2FA, passkeys, SSO/SAML, session management | | **3. Authorization** | Role-based access (RBAC), organisation isolation, plan-based feature gating, resource-level permissions | | **4. Data Encryption** | Per-org HKDF-derived keys, AES-256-GCM for sensitive fields, context separation, TLS in transit | | **5. Data Lifecycle** | Automatic retention cleanup, organisation deletion cascade, audit logging, backup encryption | *** Compliance Status [#compliance-status] | Category | Feature | Status | | --------------------- | ------------------------------------------------ | ------ | | **Encryption** | Per-org key derivation (HKDF) | ✅ | | **Encryption** | AES-256-GCM with AAD binding | ✅ | | **Encryption** | Context separation | ✅ | | **Infrastructure** | EU-hosted (AWS, Vercel, MongoDB Atlas, Upstash) | ✅ | | **Infrastructure** | Network segmentation + DDoS protection | ✅ | | **Data Isolation** | Tenant-scoped queries | ✅ | | **Data Isolation** | Dedicated database option (Pro/Enterprise) | ✅ | | **Retention** | Configurable per org with plan-based limits | ✅ | | **Retention** | Automatic cleanup scheduler | ✅ | | **Auth** | OAuth2 + 2FA + Passkeys + SSO/SAML | ✅ | | **Auth** | Role-based access (RBAC) | ✅ | | **Auth** | Organisation isolation | ✅ | | **Network** | TLS 1.2/1.3 + HSTS | ✅ | | **Network** | CORS policy + security headers | ✅ | | **Validation** | Input validation + injection prevention | ✅ | | **Validation** | GraphQL depth limiting + introspection disabled | ✅ | | **Rate Limiting** | Multi-layer rate limiting | ✅ | | **Rate Limiting** | Inbound + outbound spam protection | ✅ | | **File Upload** | Validation + malware scanning (AWS GuardDuty) | ✅ | | **Compliance** | GDPR (DPA, storage limitation, right to erasure) | ✅ | | **Compliance** | PCI DSS (via Stripe) | ✅ | | **Incident Response** | NIST SP 800-61 + 72-hour breach notification | ✅ | *** Security Contact [#security-contact] If you have security concerns, wish to report a vulnerability, or need to request security documentation (such as a DPA), please contact us at **[security@wexio.io](mailto:security@wexio.io)**. # Support Tickets (/docs/support) Create support tickets to report bugs, request features, or get help - all without leaving your workspace. Track the status of every request and communicate with the support team through ticket comments. Ticket Dashboard [#ticket-dashboard] The Support page gives you an overview of all your tickets with status counters at the top: * **Open Tickets** - Newly submitted tickets awaiting review * **In Progress** - Tickets the support team is actively working on * **Needs Response** - Tickets waiting for your reply * **Resolved** - Completed tickets The ticket list shows every ticket with its subject, type, status, priority, and last updated time. Use the **All Tickets** dropdown to filter by status. *** Creating a Ticket [#creating-a-ticket] Click **+ New Ticket** in the top-right corner to open the ticket form. Fill in the following fields: | Field | Description | | --------------- | --------------------------------------------------------------------------------- | | **Type** | Select a category: Question, Bug Report, Feature Request, or Account | | **Subject** | Brief summary of your issue | | **Description** | Detailed explanation - the more context you provide, the faster the team can help | | **Priority** | Urgency level (Low, Medium, High, Urgent) | | **Attachments** | Upload screenshots, videos, PDFs, or documents (max 10 MB per file) | Choose **Urgent** priority only for critical production issues that are blocking your operations. Click **Submit Ticket** to create the request, or **Cancel** to discard. *** Ticket Types [#ticket-types] | Type | When to use | | ------------------- | --------------------------------------------------- | | **Question** | General questions about features or how things work | | **Bug Report** | Report a problem or unexpected behaviour | | **Feature Request** | Suggest a new feature or improvement | | **Account** | Account access or billing issues | *** Priority Levels [#priority-levels] | Priority | When to use | | ---------- | ---------------------------------------------- | | **Low** | Non-urgent, can wait for a standard response | | **Medium** | Standard issues affecting your workflow | | **High** | Important issues that need prompt attention | | **Urgent** | Critical production issues blocking operations | *** Tracking a Ticket [#tracking-a-ticket] Click any ticket in the list to open its detail view. Here you can see the full ticket information and communicate with the support team. The ticket detail view includes: **Description** - Your original issue description. **Activity** - A threaded conversation where you and the support team can exchange comments. Type your message, optionally attach files, and click **Send**. **Details panel** - Shows ticket metadata: | Field | Description | | ---------------- | ------------------------------------------------------- | | **Organisation** | The workspace the ticket belongs to | | **Reporter** | Who created the ticket | | **Assignee** | Support team member handling the ticket (or Unassigned) | | **Created** | When the ticket was submitted | | **Updated** | Last activity timestamp | *** Ticket Lifecycle [#ticket-lifecycle] | Status | Description | | ------------------ | -------------------------------------------------- | | **Open** | Ticket has been submitted and is awaiting review | | **In Progress** | Support team is actively working on it | | **Needs Response** | The team has replied and is waiting for your input | | **Resolved** | Issue has been addressed and the ticket is closed | *** Share Feedback [#share-feedback] Use the **Share Feedback** button in the top-right corner to send general feedback about the platform. This is a quick way to share ideas or suggestions without creating a formal ticket. # Account (/docs/account) Your account settings control your personal profile, security options, and UI preferences. These are separate from workspace-level settings and apply to your individual login. Account Sections [#account-sections] Accessing Account Settings [#accessing-account-settings] Click your avatar in the bottom-left of the sidebar, then select **Account** to open your personal settings. Account settings apply to **you** across all workspaces. Workspace-level settings are configured separately under **Settings**. # Organisation Invitations (/docs/account/invitations) The Invitations page shows all pending invitations to join organisations. Accept or decline invitations from this page. Pending Invitations [#pending-invitations] Each invitation card shows: | Detail | Description | | ---------------- | ------------------------------------------------------------ | | **Organisation** | The organisation you've been invited to join | | **Role** | The role you'll be assigned (Owner, Admin, Editor, or Agent) | | **Invited** | How long ago the invitation was sent | Responding to Invitations [#responding-to-invitations] Each invitation has two actions: * **Accept** - Join the organisation with the assigned role. After accepting, the page refreshes and the organisation becomes available in your organisation switcher. * **Decline** - Reject the invitation. The invitation is removed from your list. If you have no pending invitations, the page displays an empty state. *** Related [#related] # Preferences (/docs/account/preferences) Customize your interface layout and default states. Each preference is a toggle that controls whether a UI element starts open or collapsed. Available Preferences [#available-preferences] | Preference | Description | Default | | --------------------------- | ------------------------------------------------ | ------- | | **Main Sidebar** | Keep the main navigation sidebar open by default | Off | | **Inbox Sidebar Collapsed** | Start with the inbox sidebar in collapsed mode | Off | | **People Sidebar** | Keep the people sidebar open by default | Off | | **AI Bar** | Show the AI preferences bar in the chat input | Off | | **Chats Collection** | Expand the chats collection by default | On | | **Custom Collection** | Expand the custom collection by default | On | | **System Fields** | Expand the system fields section by default | On | | **Custom Fields** | Expand the custom fields section by default | On | Preferences are saved per-user and persist across browser sessions. They apply only to **your** browser - other team members have their own preferences. Changes apply instantly when toggled. # Profile (/docs/account/profile) Manage your identity and how you appear across Wexio workspaces. Profile Fields [#profile-fields] | Field | Description | | -------------- | ------------------------------------------------ | | **First name** | Your given name | | **Last name** | Your family name | | **Email** | Your login email address | | **Avatar** | Profile photo displayed in chats and the sidebar | Updating Your Profile [#updating-your-profile] 1. Navigate to **Account → Profile** 2. Edit your name, email, or upload a new avatar 3. Click **Save** Deleting Your Account [#deleting-your-account] Account deletion is permanent and cannot be undone. Before deleting your account: * You must transfer ownership of any workspaces you own * All your data across all workspaces will be removed To delete your account: 1. Go to **Account → Profile** 2. Scroll to the danger zone 3. Click **Delete Account** 4. Confirm the deletion # Account Security (/docs/account/security) Protect your account with multiple layers of security. Two-Factor Authentication (2FA) [#two-factor-authentication-2fa] Add an extra verification step when logging in. **Setting up 2FA:** 1. Go to **Account → Security** 2. Click **Enable 2FA** 3. Scan the QR code with an authenticator app (Google Authenticator, Authy, 1Password) 4. Enter the 6-digit verification code 5. Save your recovery codes in a safe place Once enabled, you'll need to enter a code from your authenticator app each time you log in. Passkeys [#passkeys] Use biometric authentication for passwordless login - fingerprint, Face ID, or hardware security keys. **Registering a passkey:** 1. Go to **Account → Security** 2. Click **Add Passkey** 3. Follow your browser's WebAuthn prompt (Touch ID, Windows Hello, USB key, etc.) 4. Give your passkey a name for identification **Supported transport types:** * Internal (Touch ID, Face ID, Windows Hello) * USB (YubiKey, other FIDO2 keys) * NFC * Bluetooth (BLE) * Hybrid (cross-device authentication) Active Sessions [#active-sessions] View and manage all devices where you're currently logged in. 1. Go to **Account → Security → Sessions** 2. See a list of active sessions with device info and last activity 3. Click **Revoke** to sign out from a specific device 4. Use **Sign out all** to terminate all sessions except the current one If you notice a session you don't recognize, revoke it immediately and consider changing your password. *** Related [#related] # AI (/docs/ai) Wexio's AI features let you automate conversations, enhance operator productivity, and leverage large language models from OpenAI and Anthropic. AI settings overview Capabilities [#capabilities] | Feature | Description | | --------------------------------------------- | --------------------------------------------------------------------- | | [**AI Assistants**](/docs/ai/assistants) | Configure autonomous AI bots with custom context and knowledge | | [**AI Actions**](/docs/ai/actions) | Suggest replies, translate, rephrase, generate images, text-to-speech | | [**Knowledge Base**](/docs/ai/knowledge-base) | Upload documents for domain-specific AI responses | | [**Flow AI Cards**](/docs/flows/cards#ai) | AI Text, Agent, Classification, and TTS cards in flows | How AI Works in Wexio [#how-ai-works-in-wexio] Resolution Chain [#resolution-chain] When an AI assistant receives a message, it uses a **4-level resolution chain**: 1. **Knowledge base** - Search uploaded documents for relevant information 2. **Conversation context** - Use recent message history for continuity 3. **Assistant instructions** - Follow configured identity, objective, and tone 4. **General model knowledge** - Fall back to the model's training data Per-Purpose Models [#per-purpose-models] Configure different AI models for different tasks: | Purpose | Description | | ----------------------- | --------------------------------------------------- | | **Chat** | Primary model for conversation replies | | **Image generation** | Model for creating images (DALL-E, GPT Image, etc.) | | **Text-to-speech** | Model for voice generation | | **Audio transcription** | Model for speech-to-text (Whisper) | | **Vision** | Model for understanding images | Best Practices [#best-practices] * **Be specific** in your context - define the tone, knowledge boundaries, and escalation rules * **Set temperature low** (0.1–0.3) for customer support to get consistent, factual responses * **Include escalation instructions** - Tell the AI when to hand off to a human operator * **Upload relevant docs** to the knowledge base for domain-specific accuracy * **Test thoroughly** before deploying to production conversations # Knowledge Base (/docs/ai/knowledge-base) The Knowledge Base allows you to upload documents that your [AI assistant](/docs/ai/assistants) can reference when answering questions, providing accurate and contextual responses grounded in your content. How It Works [#how-it-works] 1. Upload documents to an assistant's knowledge base 2. When the assistant receives a message, it searches the knowledge base for relevant information 3. Matching content is included as context for generating the response 4. Knowledge base information takes priority over general model knowledge Uploading Documents [#uploading-documents] 1. Go to **Settings → AI** and select an assistant 2. Navigate to the **Knowledge Base** section 3. Click **Upload** 4. Select one or more files 5. Documents are processed and indexed automatically Supported Formats [#supported-formats] | Format | Description | | ------------ | --------------------------- | | **PDF** | Documents, manuals, guides | | **TXT** | Plain text files | | **Markdown** | `.md` files with formatting | Resolution Priority [#resolution-priority] When answering questions, the assistant checks sources in this order: 1. **Knowledge base** (highest priority) 2. **Conversation context** 3. **Assistant instructions** 4. **General model knowledge** (lowest priority) Managing Documents [#managing-documents] | Action | Description | | ------------- | ------------------------------------------- | | **View** | See all uploaded documents and their status | | **Delete** | Remove a document from the knowledge base | | **Re-upload** | Replace a document with an updated version | Best Practices [#best-practices] * **Upload comprehensive documentation** - Product docs, FAQs, policy documents * **Keep documents up-to-date** - Remove outdated files and upload new versions * **Use clear formatting** - Well-structured documents produce better search results * **Organize by topic** - Upload focused documents rather than one massive file Knowledge base processing may take a few minutes for large documents. Once processed, the assistant can immediately reference the content. # AI Providers (/docs/ai/providers) Wexio supports multiple AI providers. You need to connect at least one before [creating an AI assistant](/docs/ai/assistants). Supported Providers [#supported-providers] OpenAI [#openai] Connect your OpenAI account to use GPT models, DALL-E, Whisper, and TTS. **Setup:** 1. Go to **Settings → Integrations** 2. Find **OpenAI** and click **Connect** 3. Enter your OpenAI API key 4. Save **Capabilities:** * Chat - all available models returned by the OpenAI API, including the latest releases * Image generation * Text-to-speech * Audio transcription * Vision (multimodal models) Anthropic [#anthropic] Connect Anthropic to use Claude models for AI-powered conversations. **Setup:** 1. Go to **Settings → Integrations** 2. Find **Anthropic** and click **Connect** 3. Enter your Anthropic API key 4. Save **Capabilities:** * Chat - all available models returned by the Anthropic API, including the latest releases Anthropic does not support image generation, text-to-speech, or audio transcription. Use OpenAI for those capabilities. Wexio automatically fetches the full list of models from each provider's API, so new models are available as soon as the provider releases them - no update required. API keys are managed in [Settings → Integrations → AI Providers](/docs/settings/integrations/ai-providers). Choosing a Provider [#choosing-a-provider] | Factor | OpenAI | Anthropic | | -------------------- | -------------------------------------- | ------------------------------ | | **Best for** | Full-featured AI (chat, images, audio) | Nuanced conversations | | **Chat models** | All available OpenAI models | All available Anthropic models | | **Image generation** | ✅ | ❌ Not supported | | **Text-to-speech** | ✅ | ❌ Not supported | | **Transcription** | ✅ | ❌ Not supported | You can connect both providers and use different ones for different assistants. # Audience Filter (/docs/broadcasts/audience) The audience is the set of contacts the broadcast will fire for. It is defined by a list of profile conditions ANDed together, edited in [Step 1 - Audience](/docs/broadcasts/create#step-1--audience) of the wizard. Filter Shape [#filter-shape] Each condition is a triplet: ``` fieldKey operator value ``` * **fieldKey** - any field on a person: a system field (`tags`, `country`, `language`, `lastSeenAt`, `firstSeenAt`, etc.) or a **custom field** defined by your org. * **operator** - see the table below. * **value** - the comparison value. Ignored for `IS_EMPTY` and `IS_NOT_EMPTY`. Conditions are joined with **AND** - every one must match for a contact to be included. There is no OR connector in v1. To express OR semantics, create separate broadcasts (one per branch) or restructure the data so the same `tag`/field is set for all relevant contacts. Empty filter [#empty-filter] If you add no conditions, the audience is **every contact in the organisation**. This is occasionally what you want (org-wide announcement) - most of the time you'll want at least one condition like `optedOut NOT_EQUALS true` or `language EQUALS en`. Operators [#operators] | Operator | Applies to | Notes | | ----------------------- | ------------- | ---------------------------------------------------------------------- | | `EQUALS` | Any | Exact match. Strings are case-sensitive. | | `NOT_EQUALS` | Any | Inverse of `EQUALS`. | | `CONTAINS` | String, array | Substring match for strings, membership for arrays (e.g. `tags`). | | `NOT_CONTAINS` | String, array | Inverse of `CONTAINS`. | | `STARTS_WITH` | String | Prefix match. | | `ENDS_WITH` | String | Suffix match. | | `IN_ARRAY` | Any | Field's value is one of the listed values. | | `NOT_IN_ARRAY` | Any | Field's value is not in the listed values. | | `GREATER_THAN` | Number, date | Strict `>`. | | `GREATER_THAN_OR_EQUAL` | Number, date | `>=`. | | `LESS_THAN` | Number, date | Strict `<`. | | `LESS_THAN_OR_EQUAL` | Number, date | `<=`. | | `IS_EMPTY` | Any | Field is missing, null, empty string, or empty array. No value needed. | | `IS_NOT_EMPTY` | Any | Field has a non-empty value. No value needed. | | `MATCHES_REGEX` | String | Full regex match. Use sparingly - slow on large directories. | Common Field Examples [#common-field-examples] | Field | Use case | Example | | ------------- | -------------------- | --------------------------------------- | | `tags` | Audience segments | `tags CONTAINS "vip"` | | `language` | Localisation | `language EQUALS "en"` | | `country` | Geographic targeting | `country IN_ARRAY ["ES", "PT"]` | | `firstSeenAt` | Recency cohorts | `firstSeenAt GREATER_THAN "2026-04-01"` | | `lastSeenAt` | Re-engagement | `lastSeenAt LESS_THAN "2026-03-01"` | | Custom fields | Domain logic | `subscriptionTier EQUALS "pro"` | Custom fields are listed alongside system fields in the field picker. Define them in **People → Custom Fields**. Live Preview Count [#live-preview-count] While you edit, the wizard shows a count next to the filter - `"≈ 1 284 contacts match"`. * The query runs against your **current** people directory. * It's debounced (300ms) so it doesn't fire on every keystroke. * For template-mode broadcasts, the preview accounts for [template routes](/docs/broadcasts/routing#template-mode--template-routes) - only contacts whose chat is on one of your route integrations are counted, since the rest can't receive the template anyway. * It's an **estimate**, not a guarantee. The actual fan-out is computed at fire time. The number is also shown on the broadcast detail page as long as the broadcast is `SCHEDULED`, so you can revisit it without re-opening the wizard. Estimate vs Actual [#estimate-vs-actual] The broadcast stores **two** audience numbers: | Field | When it's set | What it represents | | ---------------------- | ------------- | ---------------------------------------------------------------------- | | `audienceSizeEstimate` | At save time | The live preview count when you confirmed the wizard | | `audienceSizeActual` | At fire time | The count produced by re-running the filter against the live directory | After firing, the detail page shows both side-by-side so you can spot drift: > Estimated: 1 284 · Actual: 1 251 (delta −33) A delta is normal - contacts can be added, removed, or have their fields change between save time and fire time. Re-evaluation Semantics [#re-evaluation-semantics] The filter runs **at fire time**, not at save time. | Change between save and fire | Effect | | --------------------------------------------- | --------- | | Contact created that matches the filter | Included. | | Contact deleted or archived | Excluded. | | Contact's field changed and now matches | Included. | | Contact's field changed and no longer matches | Excluded. | This is "everyone currently tagged X on Apr 30" semantics - almost always what you want for marketing-style sends. If you need a frozen audience, capture the contact IDs into a custom field at save time and filter on that. Capacity [#capacity] There's no hard limit on audience size - broadcasts of 100 000+ contacts work. The wizard shows a soft warning above \~100k recommending you tighten the filter, but the save still succeeds. The dispatcher streams the audience in pages (default 500 contacts per page) so memory stays flat regardless of audience size. Per-batch concurrency is bounded so providers don't get overwhelmed; rate-limit responses are absorbed automatically with exponential back-off. Targeting Specific Contacts (Power-User) [#targeting-specific-contacts-power-user] For advanced flows that already know exactly which contacts to target - typically `retryFailedContacts` or external orchestration - the audience can be a **hand-picked list of contact IDs**. This bypasses filter resolution entirely. It's used internally by [Retry Failed Contacts](/docs/broadcasts/retry) and is exposed via the API but not via the standard wizard. # Create a Broadcast (/docs/broadcasts/create) The broadcast wizard has **five steps**, in this order: Audience → What to send → Pick the payload → When to run → Review. You can move forward only after each step is valid; the broadcast is persisted at the end of step 5. Step 1 - Audience [#step-1---audience] Define **who** receives the broadcast. The audience is a list of profile conditions ANDed together. | Field | Description | | --------------------- | --------------------------------------------------------------------------------------------------------- | | **Filter conditions** | Each condition is a `(field, operator, value)` triplet. Add as many as you need. | | **Live count** | Updates as you edit (debounced 300ms). Shows the current matching contact count from your live directory. | * **Empty filter** → every contact in the organisation. (Usually you'll want at least one condition like `language EQUALS "en"` or `tags CONTAINS "newsletter"`.) * The filter is **re-evaluated at fire time**, not at save time - people added or removed before the broadcast fires are picked up automatically. See [Audience Filter](/docs/broadcasts/audience) for the full operator reference and field options. Step 2 - What do you want to send? [#step-2---what-do-you-want-to-send] Pick one of two send-kinds: | Send-kind | What runs | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------- | | **Flow** | One of your published flows runs end-to-end for each contact. Supports any cards (text, media, AI, conditions, sub-flows, templates). | | **Template** | A single WhatsApp template is sent per contact through one of your WA Business integrations. | Each broadcast also has a **name** (shown in the broadcasts list) - the wizard prompts for it in this step alongside the send-kind picker. The choice locks the next step's picker. You can't switch send-kind after saving - clone the broadcast as a new one if you need that. Step 3 - Pick a Flow or Pick a Template [#step-3---pick-a-flow-or-pick-a-template] The picker depends on what you chose in step 2. If you chose "Flow" [#if-you-chose-flow] | Field | Description | | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Flow** | One of your **published** flows. Drafts are hidden from the picker. | | **Template fallbacks** *(optional)* | A list of WhatsApp template fallbacks. If the flow can't reach a contact through their primary channel - typically because the WhatsApp 24h window is closed - the system falls back to one of these templates via the matching integration. | Add as many fallbacks as you need; the dispatcher picks the one whose integration matches the contact's WhatsApp chat. See [Fallbacks & Template Routes](/docs/broadcasts/routing) for the per-contact decision logic and how multiple fallbacks are resolved. If you chose "Template" [#if-you-chose-template] | Field | Description | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | **Template routes** | One or more `(template, integration)` pairs. Each route pairs a WhatsApp template with the integration that should deliver it. | The system picks the route whose integration matches each contact's chat. If a contact has no chat on any of the configured route integrations, that contact is skipped. Multiple routes let one broadcast cover several WhatsApp numbers (e.g. one for EU contacts, one for US contacts) without splitting it into separate broadcasts. Step 4 - When should this run? [#step-4---when-should-this-run] | Field | Description | | ------------ | ------------------------------------------------------------------ | | **Run at** | Datetime picker, minute precision. | | **Timezone** | IANA zone (e.g. `Europe/Kyiv`). Defaults to your current timezone. | The timezone is for **display and round-tripping** - the engine fires on the absolute UTC time. The zone preserves the original local-time label across DST transitions and across users in different timezones. **Minimum lead time:** the run-at time must be at least **2 minutes** in the future. The scheduler ticks every 60 seconds, so anything closer risks slipping past the first tick. The wizard's confirm button is disabled until you pick a time that satisfies the lead-time check. Recurrence is **not exposed** for broadcasts. Use a [recurring scheduled trigger](/docs/flows/editor/triggers/scheduled) for ongoing automations. See [Scheduling](/docs/broadcasts/scheduling) for the full details on lead time, late-fire policy, and what happens when a worker misses a tick. Step 5 - Review [#step-5---review] The Review step shows everything in one summary card: * **Summary line** - `"Flow F will run for ≈ N contacts on Apr 30, 2026 at 10:00 Europe/Kyiv"`. * **Provider breakdown** - `≈ X Telegram · Y WhatsApp · Z Instagram · W Viber`. Computed from your audience's reachable chats - useful for compliance and cost sanity-checks. * **24h-window warning** - final reminder if the audience contains WhatsApp contacts whose first card isn't a template and you haven't configured fallbacks. * **Confirm** - persists the broadcast with status `SCHEDULED`. The wizard is **edit-friendly**: clicking any earlier step from the review summary takes you back without losing data on the steps you've already filled. After Confirming [#after-confirming] The broadcast is now visible on the broadcasts list with a `SCHEDULED` status chip and a countdown to fire time. * **Open** the detail page to inspect or edit. * **Edit** any field - only while `SCHEDULED`. Once it transitions to `SENDING`, the schedule, audience, and payload are locked. * **Cancel** any time before firing completes - see [Cancellation](/docs/broadcasts/lifecycle#cancellation). # Delivery & Logs (/docs/broadcasts/delivery) Once a broadcast is `SENDING` (and forever after), every targeted contact has a row in the history table with one of three outcomes. The aggregate counters on the broadcast roll those up. Per-Contact Outcomes [#per-contact-outcomes] Every contact in the audience ends up in **one of three states**: | Outcome | Meaning | | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `DELIVERED` | The message or flow was successfully dispatched and the provider accepted it. | | `FAILED` | The dispatch was attempted but the provider returned an error (4xx, auth failure, blocked bot, etc.). | | `SKIPPED` | The contact was filtered out at dispatch time - no reachable channel, opted out, missing required field, no matching template route, or another pre-condition wasn't met. | These three are what you see in the broadcast's history table and in the rolled-up counters on the detail page header. **Behind the scenes** the dispatcher tracks finer-grained reasons for skips and dispatch-time failures (e.g. "no chat on any matching route integration" vs "duplicate dedup hit" vs "flow unpublished mid-run"). Those finer states are aggregated into the three operator-facing outcomes for the UI. If you need them at the row level, the GraphQL API exposes the underlying status string. `DELIVERED` [#delivered] The provider accepted the send. For Telegram, this is confirmed synchronously. For WhatsApp / Instagram / Viber, the provider's delivery callback is wired in a follow-up PR - until then, "delivered" effectively means "the dispatcher's send call returned without an inline error." `FAILED` [#failed] The provider returned an error. Each row carries an `errorReason` string for context - typical examples: | Error reason | Meaning | | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | | `telegram:403:blocked_by_user` | The contact blocked your bot. Retry won't help. | | `whatsapp:131047:re_engagement_message_required` | The 24h window was closed and your flow's first card wasn't a template. Add a fallback template and retry. | | `whatsapp:1:Unsupported message type` | Template parameter or media format is wrong. Fix the template and create a new broadcast (don't retry). | | `:auth_failed` | Integration credentials are broken. Fix auth and retry. | | Transient `5xx` from any provider | Provider blip. Retry usually clears it. | `FAILED` rows are **retryable** - see [Retry Failed Contacts](/docs/broadcasts/retry). `SKIPPED` [#skipped] The contact was filtered at dispatch time. Common causes: * **No reachable channel** - the contact has no chat record in your org. * **Channel mismatch** - the contact's chat isn't on any of the configured template routes (template mode) or fallback integrations (flow mode with WA-window closed). * **Opted out / missing required field** - flow logic or template parameter requirement failed pre-flight. * **Flow unpublished** - the flow was unpublished between scheduling and firing. * **Cancelled** - the broadcast was cancelled before this contact's turn. Skipped contacts are **not retryable** - re-running with the same config produces the same skips. Address the underlying cause (add a fallback, expand the template routes, republish the flow) and create a new broadcast. Aggregate Counters [#aggregate-counters] The broadcast detail page shows live-updating counters: | Counter | What it counts | | -------------------------- | --------------------------------------------------------- | | `audienceSizeEstimate` | The count when you saved the wizard (snapshot). | | `audienceSizeActual` | The count at fire time, after the filter was re-resolved. | | `delivered` | Contacts whose row ended in `DELIVERED`. | | `failed` | Contacts whose row ended in `FAILED`. | | `skipped` | Contacts whose row ended in `SKIPPED`. | | `startedAt` / `finishedAt` | Wall-clock duration of the run. | Derived metrics shown on the detail page: * **Delivery rate** = `delivered / audienceSizeActual`. * **Drop rate** = `(failed + skipped) / audienceSizeActual`. History Table [#history-table] The detail page's history table shows one row per contact: | Column | Detail | | -------------------------- | ------------------------------------------------------------------------------------------ | | **Contact** | Name + avatar; click to open the contact in People. | | **Channel** | Which integration the dispatch went through (or would have). | | **Status** | `DELIVERED`, `FAILED`, or `SKIPPED` chip. | | **Error reason** | Provider error code, only shown when `FAILED`. | | **Used fallback** | Marker if a flow-mode contact received a WA fallback template instead of the primary flow. | | **Sent at / Delivered at** | Timestamps. `Delivered at` may lag for async providers. | The table is **paginated, searchable, and filterable** by status. CSV export is available for ops investigations. Rows live for **30 days**, after which they age out. Aggregate counters on the broadcast itself never expire. Live updates [#live-updates] While the broadcast is `SENDING`, new rows prepend to the table in real time via a GraphQL subscription. You don't have to refresh - keep the detail page open during the run. Used Fallback Marker [#used-fallback-marker] For flow-mode broadcasts with [template fallbacks](/docs/broadcasts/routing#flow-mode--template-fallbacks), each row records whether the dispatcher fired the primary flow or a fallback template: * **Primary** - most rows. The contact was reachable through the configured flow. * **Fallback** - a marker on the row indicates a fallback template was sent instead. If the fallback marker fires often, your audience has a lot of dormant WhatsApp contacts and the broadcast effectively reduces to "send the fallback template" for those - worth knowing for cost and copywriting purposes. Rate Limiting [#rate-limiting] Broadcasts never blast the whole audience at once. The dispatcher paces sends so providers don't reject and the worker stays healthy under load. How pacing works [#how-pacing-works] | Stage | What happens | | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Audience streaming** | The audience is streamed in pages (default 500 contacts per page) instead of materialised in memory. A 100k-contact audience uses no more memory than a 500-contact one. | | **Batched dispatch** | Each page is broken into smaller batches (default 50 contacts per batch) and enqueued onto the message dispatch queue. | | **Concurrent batches** | Multiple batches process in parallel per worker (default 10), with bounded per-contact concurrency inside each batch (default 5 in flight). This keeps memory and DB connections steady regardless of audience size. | | **Per-provider throttling** | When the provider returns a rate-limit response (429), the unfinished tail of the batch is automatically re-enqueued with **exponential backoff**. The broadcast stays in `SENDING` until the queue drains. | You don't configure any of this - the pacing is automatic and tuned for typical Wexio org sizes. What this looks like in practice [#what-this-looks-like-in-practice] * A 1 000-contact broadcast typically completes in **\<1 minute** (Telegram) to **a few minutes** (WhatsApp template). * A 10 000-contact broadcast typically completes in **5–15 minutes** depending on provider and template complexity. * A 100k+ broadcast can take **hours** - that's expected. The provider rate-limits us, we back off, the queue drains. The broadcast stays `SENDING` and you can watch the counters tick up live. If a run takes much longer than expected, check the integration's status in **Settings → Integrations** - a degraded provider shows degraded send rates. What rate-limit retries are *not* [#what-rate-limit-retries-are-not] * **Not visible to operators.** Rate-limit retries happen inside the dispatcher; they don't show up as `FAILED` rows or count against retry limits. The contact ends up in `DELIVERED` once the retry succeeds. * **Not unbounded.** Each batch has an internal retry budget. If a provider is fully down, batches eventually exhaust the budget and contacts in those batches end up `FAILED` with `:rate_limit_exhausted` reasons. * **Not the same as `retryFailedContacts`.** That's an operator-triggered redrive of `FAILED` rows into a new broadcast - see [Retry Failed Contacts](/docs/broadcasts/retry). Reading the Numbers [#reading-the-numbers] A few common patterns and what they mean: "100% delivered, 0% failed, 0% skipped" [#100-delivered-0-failed-0-skipped] Healthy run. Everyone got the message and the provider confirmed. "60% delivered, 0% failed, 40% skipped" [#60-delivered-0-failed-40-skipped] Either: * **Audience drift** - 40% of your audience had no reachable chat, was opted out, or had its filter-matching field change between save and fire. Tighten the filter (e.g. require `lastSeenAt IS_NOT_EMPTY`) and re-create. * **Channel mismatch** (template mode) - 40% of contacts have no chat on any of the configured route integrations. Add more routes or restrict the audience filter to match the routes. "85% delivered, 15% failed with `whatsapp:131047`" [#85-delivered-15-failed-with-whatsapp131047] 15% of your WhatsApp contacts had a closed 24h window and your flow's first card wasn't a template. Configure a [WhatsApp fallback](/docs/broadcasts/routing#flow-mode--template-fallbacks) and use [Retry Failed Contacts](/docs/broadcasts/retry) to redrive. "0% delivered, 100% skipped" [#0-delivered-100-skipped] The flow was unpublished between scheduling and firing, or the audience was empty by fire time. Republish and re-create - Retry won't work because skipped rows aren't retryable. # Broadcasts (/docs/broadcasts) A **broadcast** is a one-shot, scheduled send. You pick **a moment in time + a filter + a payload (flow or template)** and Wexio fires it exactly once for everyone who matches. Use a broadcast whenever you want a bounded, auditable one-shot send: * "Notify everyone tagged `vip` 30 minutes before the webinar starts." * "Send the seasonal-sale WhatsApp template to all customers in Spain on Thursday at 9am." * "Run the post-checkout follow-up flow for everyone who bought in the last 7 days, this Friday." * "Re-engage cold leads on the first of each month" (created as a series of one-shot broadcasts). Broadcasts are gated to **Pro and Enterprise** plans. Free / Standard orgs see an upgrade prompt instead of the create button. Two Kinds [#two-kinds] Every broadcast is one of two kinds, picked at creation time: | Kind | What you set | What runs | | ------------ | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | **Flow** | Pick one of your published flows | The flow runs end-to-end for each contact - supports any cards (text, media, AI, branches, conditions, even templates) | | **Template** | Pick a WhatsApp template + a WA integration | A single WhatsApp template is sent per contact through the chosen integration | **When to use which:** * **Flow** - branched, conversational, captures responses, mixes channels. Optionally attach [WhatsApp template fallbacks](/docs/broadcasts/routing#flow-mode--template-fallbacks) so contacts the flow can't reach (typically a closed WA 24h window) still receive something. * **Template** - static announcement, promotion, or notification. Configure one or more [template routes](/docs/broadcasts/routing#template-mode--template-routes) - `(template, integration)` pairs - and the system picks the right route per contact. Each route is itself window-safe; no fallback is needed. How Broadcasts Differ From Schedule Triggers [#how-broadcasts-differ-from-schedule-triggers] Same execution plumbing under the hood - different lifecycle and ownership. | | Broadcast | [Scheduled Trigger](/docs/flows/editor/triggers/scheduled) | | --------------- | ----------------------------------------------------- | ----------------------------------------------------------------- | | **Lifecycle** | One-off; disappears from the active list after firing | Permanent while the flow is published | | **Recurrence** | Fires once | One-off, recurring (cron), or relative to a contact date field | | **Ownership** | Standalone entity | A row in the flow's trigger list | | **Typical use** | "Send the Black Friday push on Nov 29 at 10:00" | "Run this flow every Monday at 09:00" or "2h before each booking" | | **Audit view** | Per-broadcast history page with stats | Cross-run logs only | | **Cancellable** | Yes, until firing starts | Disable by unpublishing the flow | Reach for a broadcast for **bounded, one-shot sends** with a per-run audit trail. Reach for a scheduled trigger for **ongoing automations** that fire whenever a clock or contact date matches. What Broadcasts Are *Not* [#what-broadcasts-are-not] * **Not recurring.** Each broadcast fires once. To repeat, create another broadcast (or use a recurring scheduled trigger). * **Not a trigger.** Triggers fire in response to an event (message received, field changed, webhook received). Broadcasts fire at a clock time. * **Not transactional.** Broadcasts are bulk. For 1:1 receipts, OTPs, or order-status messages, send templates from a flow or webhook handler. * **Not a campaign manager.** No A/B split, no journey, no funnel dashboard - just delivered/skipped/failed counts. Cohorting and experimentation belong in the flow editor. * **Not throttled by user-set rate limits.** Dispatch speed is paced by the messaging provider (e.g. WhatsApp Business API tier), not by broadcast configuration. Common Sections [#common-sections] Glossary [#glossary] * **Audience filter** - the list of conditions that defines who receives the broadcast. * **Audience size estimate** - the count shown in the wizard, computed against the live directory at the moment you're editing. * **Audience size actual** - the count computed at fire time, when the audience is re-resolved. * **Template route** - a `(template, integration)` pair used by template-mode broadcasts to direct delivery through the right WhatsApp number. * **Fire time** - the moment the worker dispatches the broadcast (within seconds of the scheduled time). * **Dispatch** - the act of sending the flow or template to a single contact during the `SENDING` phase. * **Retry of a broadcast** - a new broadcast targeting only the failed contacts of a prior one, linked via the source's history. # Lifecycle & Cancellation (/docs/broadcasts/lifecycle) A broadcast moves through five statuses from create to terminal. Knowing which status it's in tells you what actions are available and what the engine is doing. Status Reference [#status-reference] | Status | Meaning | Editable? | Cancellable? | | ----------- | ----------------------------------------------------- | --------- | -------------------------- | | `SCHEDULED` | Persisted, waiting for fire time | ✓ | ✓ | | `SENDING` | Worker has picked it up; dispatch in flight | - | ✓ (best-effort, see below) | | `COMPLETED` | All contacts processed; counters finalised | - | - | | `CANCELLED` | Cancelled before or during firing | - | - | | `FAILED` | The broadcast itself failed (not individual contacts) | - | - | ``` create firing tick all contacts done ──────▶ SCHEDULED ─────────▶ SENDING ───────────────▶ COMPLETED │ │ │ cancel │ sweeper rescue / missed-window ▼ ▼ CANCELLED FAILED ``` `SCHEDULED` [#scheduled] The broadcast is persisted and counts against your [plan quota](/docs/broadcasts/limits#combined-quota). The detail page shows the audience preview, scheduled time, and a countdown. You can edit any field - name, send-kind, schedule, audience, channels, fallback. You cannot switch send-kind from Flow to Template (or vice versa) once saved; that's a different broadcast. You can cancel any time. Cancellation is immediate and safe. `SENDING` [#sending] The scheduler tick has fired and the dispatch pipeline is fanning out. The detail page shows live-updating counters (sent, delivered, failed, skipped). The history table prepends rows as contacts are processed. You **cannot edit** schedule, audience, or payload - partial dispatch is in flight and edits would produce inconsistent results across the audience. You **can request cancellation**, but semantics are best-effort: * Already-dispatched contacts stay dispatched (they were already sent; we can't unsend). * Contacts not yet dispatched are skipped at the next contact boundary. * The processor checks the broadcast's status before claiming each contact, so cancellation propagates within seconds. This is the right semantics for push-style delivery - there's no "rollback" once a message is at the provider. Cancellation just means "stop dispatching new messages." `COMPLETED` [#completed] All contacts have been processed (every audience member ended up in one of `DELIVERED`, `FAILED`, or `SKIPPED`). The detail page shows final counters and the full history table. There's nothing to edit, cancel, or retry at the broadcast level - but you can [retry just the failed contacts](/docs/broadcasts/retry) into a new broadcast. `CANCELLED` [#cancelled] Terminal state. Set when the user cancelled either before fire time (`SCHEDULED → CANCELLED`) or during dispatch (`SENDING → CANCELLED`). If cancelled during `SENDING`, partial counters are preserved - already-dispatched contacts stay in their outcome (`DELIVERED` / `FAILED`), and remaining contacts are recorded as `SKIPPED`. Per-contact rows show whether each one made it before cancellation took effect. `FAILED` [#failed] Terminal state. Set when something goes wrong with the broadcast as a whole, not with individual contacts. The `failureReason` field explains which: | `failureReason` | When | What to do | | -------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | | `MISSED_WINDOW` | Worker picked up the broadcast more than 5 minutes after `scheduledAt`. The broadcast did not fire. | Use the **Retry** button to reschedule with the same audience and payload. | | `WORKER_STALLED` | Worker crashed mid-dispatch and the sweeper rescued the broadcast with zero successful sends. | Investigate worker health (logs, alerts). Retry to re-attempt. | | `ALL_BATCHES_FAILED` | Every batch the worker tried to dispatch errored out (provider down, all integrations invalid, etc.). | Check integration health and re-run after fixing. | Per-contact failures (`FAILED` outcome on individual rows) do **not** put the broadcast into `FAILED`. The broadcast can `COMPLETE` with every contact failed - that's a successful broadcast that produced bad outcomes, which is what [Retry Failed Contacts](/docs/broadcasts/retry) is designed to redrive. Cancellation [#cancellation] The cancel button is on the detail page header. The confirm dialog tells you exactly what will happen given the current status: * **From `SCHEDULED`** → "Cancel this broadcast? It won't fire." * **From `SENDING`** → "Cancel this broadcast? Already-dispatched contacts will keep their outcome; remaining contacts will be skipped." * **Disabled in `COMPLETED` / `CANCELLED` / `FAILED`** → terminal, nothing to cancel. Cancellation is recorded in the audit log along with who triggered it and when. Stuck-Broadcast Sweeper [#stuck-broadcast-sweeper] A worker process can crash mid-fan-out, leaving a broadcast pinned in `SENDING` with no worker coming back for it. A periodic sweeper (every **2 minutes**) handles this with two passes: Repair pass [#repair-pass] For every `SENDING` broadcast, re-aggregate per-contact outcomes from the dispatch ledger and patch counter drift. If every contact is accounted for (`processed >= audienceSizeActual`), flip the broadcast to `COMPLETED`. This catches counter rollups that silently dropped - the source-of-truth is the per-contact dispatch rows, and the sweeper makes sure the rolled-up counters on the broadcast match. Rescue pass [#rescue-pass] For broadcasts stuck **>10 minutes** AND with no new dispatches written in the last **5 minutes** (the dispatch-quiet window), apply this rule: * If at least one contact ended in `DELIVERED`, `FAILED`, or `SKIPPED` → transition to `COMPLETED` (partial success - already processed what it could). * Otherwise → transition to `FAILED` with `failureReason = WORKER_STALLED`. The 10-minute timeout is the upper bound for "the worker should have made *some* progress by now." Combined with the 5-minute dispatch-quiet check, it's both faster and safer than naïve "kill anything stuck for N minutes" - it won't kill an in-progress fan-out. You don't interact with the sweeper directly. It's mentioned here so the timing model is transparent: if a broadcast goes silent for 10+ minutes, the system is going to make a final disposition rather than leaving it hanging. What's Locked, What's Editable [#whats-locked-whats-editable] Quick reference of what you can change in each status: | Field | `SCHEDULED` | `SENDING` | Terminal | | --------------------------- | ----------- | --------------- | ------------------------------------------------------ | | Name | ✓ | - | - | | Send-kind | - | - | - | | Flow / template picker | ✓ | - | - | | Template fallbacks / routes | ✓ | - | - | | Audience filter | ✓ | - | - | | Schedule | ✓ | - | - | | Cancel button | ✓ | ✓ (best-effort) | - | | Retry failed contacts | - | - | ✓ (when `COMPLETED` or `CANCELLED` with `FAILED` rows) | `Send-kind` is locked in every status because changing kinds is effectively a different broadcast - clone the broadcast as a new one if you need that. Audit Trail [#audit-trail] Every state transition is recorded with timestamp and (when applicable) the user who triggered it. The detail page's activity sidebar shows: * Created at - by user. * Edited at - by user (one entry per edit). * Started at - when the worker picked it up. * Completed at / Cancelled at / Failed at - terminal transition. This is enough to reconstruct what happened to any broadcast even weeks later. Per-contact dispatch rows have their own retention (30 days by default) and link to the broadcast. # Limits & Permissions (/docs/broadcasts/limits) Broadcasts are gated three ways: by **plan**, by **role**, and by a **combined quota** that broadcasts share with scheduled triggers. Plan Gating [#plan-gating] | Plan | Broadcasts | | -------------- | ---------------------------------------------------- | | Free | Locked. Upgrade prompt instead of the create button. | | Standard | Locked. Upgrade prompt. | | **Pro** | Available. Combined scheduled quota: **25**. | | **Enterprise** | Available. Unlimited. | On Free / Standard, every broadcast operation (list, create, update, cancel, history, preview, subscriptions) returns a `FORBIDDEN_RESOURCE` error. The UI renders this as an upgrade gate, not a raw error - agents and admins on those plans see the broadcasts page with a "Pro feature - upgrade to unlock" overlay. Role Gating (RBAC) [#role-gating-rbac] Within a Pro+ org, action is split by role: | Role | Read | Create / Edit / Cancel / Retry | | ---------- | ---- | ------------------------------ | | **OWNER** | ✓ | ✓ | | **ADMIN** | ✓ | ✓ | | **EDITOR** | ✓ | ✓ | | **AGENT** | ✓ | - | Agents can view broadcasts and the history table - useful for support context - but cannot create, edit, cancel, or retry. The mutation buttons are hidden in the UI for agents; the API also rejects with `FORBIDDEN`. Master / system orgs bypass these checks via their global "manage all" grant. Combined Quota [#combined-quota] Broadcasts and `SCHEDULED` triggers share **one cap** because they're the two ways your org queues outbound work for the future. A single cap matches plan-tier intent - without it, an org could configure 25 broadcasts + 25 schedule triggers against the same operational budget. | Plan | Combined cap (active broadcasts + active scheduled triggers) | | ---------- | ------------------------------------------------------------ | | Pro | **25** | | Enterprise | Unlimited | What counts toward the cap [#what-counts-toward-the-cap] | Item | Counts? | | ---------------------------------------------------------------- | ----------------------------------------------------- | | Broadcasts with `status = SCHEDULED` | ✓ | | Broadcasts in `SENDING` / `COMPLETED` / `CANCELLED` / `FAILED` | - | | Scheduled triggers with `RELATIVE` mode | ✓ (always - re-evaluated against contacts every tick) | | Scheduled triggers with `SPECIFIC` cron + recurring | ✓ (always - ongoing recurrence) | | Scheduled triggers with `SPECIFIC` one-off, future `scheduledAt` | ✓ | | Scheduled triggers with `SPECIFIC` one-off, past `scheduledAt` | - (already fired or missed) | | Scheduled triggers in **draft** flows (not yet published) | ✓ | Drafts count so you get immediate feedback at create time, not a surprise failure when you try to publish the flow. Counter on the UI [#counter-on-the-ui] The broadcasts list and the trigger panel both show the live usage: > **17 / 25 used** Once you hit the cap, the create button greys out and shows a tooltip: "You've reached the combined limit of 25 active broadcasts + scheduled triggers. Cancel one or upgrade to Enterprise." The same number is enforced on the server - the API returns `FORBIDDEN_RESOURCE` with feature `broadcasts` (or `scheduled_triggers`) past the cap. The FE counter and the server-side gate read from the same source-of-truth, so they can never disagree. How to free a slot [#how-to-free-a-slot] | Item | How to remove from cap | | ----------------------------------- | ----------------------------------------------------------- | | Broadcast | Cancel it (`SCHEDULED → CANCELLED`) or wait for it to fire. | | Scheduled trigger (one-off, past) | Already not counted. | | Scheduled trigger (one-off, future) | Delete the trigger or change to a non-scheduled type. | | Scheduled trigger (recurring) | Delete the trigger or unpublish the flow. | | Scheduled trigger (relative) | Same as recurring. | Switching a trigger AWAY from `SCHEDULE_TRIGGERED` frees a slot - no quota check on the down-direction. Audience Size Cap [#audience-size-cap] There is no hard cap on audience size, but the wizard shows a soft warning over **\~100k contacts**: > "Your audience is very large. Consider tightening the filter - even at 100k+ this will dispatch fine, but the run will take longer." The save still succeeds. The dispatch pipeline is designed to handle 100k+ contacts without blocking, but very large fan-outs (1M+) start to compete with regular flow execution for worker capacity. Reach out to support before scheduling those. Other Limits [#other-limits] | Limit | Value | | ---------------------------------- | -------------------------------------------------------------- | | Minimum lead time | 2 minutes (`scheduledAt` must be at least 2 min in the future) | | Late-fire grace window | 5 minutes (transitions to `MISSED_WINDOW` past that) | | Per-contact dispatch row retention | 30 days | | Aggregate counter retention | Forever | | Broadcast row retention | Forever (no auto-archive) | | Stuck-broadcast rescue timeout | 10 minutes (no progress in last 5 min) | API Permissions Surface [#api-permissions-surface] The `appPermissions` GraphQL query exposes everything the FE needs to render the right UI: | Field | Source | Use | | -------------------------- | ------------------------------- | ----------------------------------------------------- | | `role.canManageBroadcasts` | Role grant | Hide create / edit / cancel UI for agents. | | `plan.canManageBroadcasts` | Plan feature flag | Show upgrade prompt on Free/Standard. | | `plan.maxScheduled` | Plan quota (`null` = unlimited) | Render the cap. | | `usage.scheduled` | Live count | Render "17 / 25 used"; grey out create button at cap. | These are the same numbers the server checks on every mutation, so what you see is what you can do. Frequently Asked [#frequently-asked] **Can I exceed the cap by scheduling broadcasts in advance?** No. The cap counts active scheduled work regardless of fire time - 25 broadcasts scheduled across the next year is still 25 active items. **Does cancelling a `SENDING` broadcast immediately free the slot?** Yes - once it transitions to `CANCELLED`, it's terminal and stops counting. **Are retries quota-counted separately?** Each retry creates a new broadcast that counts the same as any other. Plan accordingly when chaining retries. **Can agents request a broadcast?** Not via mutations. Workflow-wise, agents flag the need to an editor / admin who creates and confirms. The agent can view the result like any read-only user. # Retry Failed Contacts (/docs/broadcasts/retry) Some contacts will fail in any broadcast - provider 4xxs, blocked bots, expired tokens, transient network errors. Instead of rebuilding the audience filter to find them, **Retry Failed Contacts** spawns a new broadcast targeting exactly those rows. When the Button Appears [#when-the-button-appears] The Retry button is enabled on broadcasts that: * Are in a terminal state (`COMPLETED` or `CANCELLED`). * Have at least one row with outcome `FAILED`. It is **disabled** for: * Broadcasts still `SCHEDULED` or `SENDING` - wait for the run to finish first. * Broadcasts with zero `FAILED` rows - there's nothing to retry. * Broadcasts whose dispatch rows have aged out (more than 30 days old) - the failed-contact list is gone, see [Retention](#retention) below. `SKIPPED` rows are **not** retried automatically. They represent intentional non-deliveries (no reachable channel, no matching template route, opted out, flow unpublished) and re-running with the same config would produce the same skips. Address the underlying cause and create a new broadcast instead. What Retry Creates [#what-retry-creates] Clicking Retry opens a small dialog: | Field | Description | | --------------------- | ---------------------------------------------------------------- | | **Run at** | When to fire the retry. Defaults to `now + 2 min`. | | **Contacts to retry** | Read-only count - the failed contacts from the source broadcast. | Confirm and Wexio creates a **new** broadcast: * Same flow / template / fallbacks / template routes as the source. * Audience is the **explicit list of failed contact IDs** - bypassing filter resolution entirely. The retry doesn't re-run the audience filter, so contacts that were in the original failed-set but no longer match the filter are still retried. * Linked back to the source broadcast for audit (visible on both detail pages). * Status `SCHEDULED`. The original broadcast is unchanged. Retry chains link via `retryOfBroadcastId` so you can trace the lineage. Retry Counts Toward Quota [#retry-counts-toward-quota] Each retry is a **new broadcast** and counts against your [combined scheduled quota](/docs/broadcasts/limits#combined-quota) the same as any other. If you're at the cap, cancel a draft to free a slot first. Iterative Retries [#iterative-retries] You can retry the retry. Each subsequent retry targets only the failures from the previous one. Practical pattern: 1. Original broadcast: 10 000 contacts. 9 800 `DELIVERED`, 200 `FAILED`. 2. Retry #1: 200 contacts. 180 `DELIVERED`, 20 `FAILED`. 3. Retry #2: 20 contacts. 19 `DELIVERED`, 1 `FAILED`. 4. ... The chain terminates naturally when failures hit zero or when remaining failures are systemic (the contact blocked your bot - no amount of retry will fix that). The audit chain lets you trace each retry back to the original, so you can audit "this customer received the campaign on the third retry." Common Reasons to Retry [#common-reasons-to-retry] | Failure pattern | Diagnosis | Action | | ------------------------------------------------ | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `whatsapp:131047:re_engagement_message_required` | WA 24h window closed and no fallback configured | Edit the source to add a [template fallback](/docs/broadcasts/routing#flow-mode--template-fallbacks), then retry. (You can edit the source only when its retry hasn't started yet - otherwise clone and re-create.) | | `telegram:429:Too Many Requests` | Provider rate-limit; the dispatcher already retried within the run and exhausted its budget. | Retry - fresh budget. | | `whatsapp:1:Unsupported message type` | Template parameter or media format issue | Don't retry. Fix the template/media in the source flow first, then create a **new** broadcast. | | `:auth_failed` | Integration credentials broken | Fix the integration auth, then retry. | | Transient 5xx from any provider | Provider blip during the run | Retry - usually clears. | For systemic failures (blocked bot, deactivated account), retry won't help - the same error will reproduce. The drop rate metric on the original broadcast already accounts for these as best-case losses. API Surface [#api-surface] `Retry Failed Contacts` is exposed via GraphQL as a mutation that returns the new broadcast. Programmatic callers can: * Schedule retries days in advance. * Chain retries automatically when the original completes (via webhook subscription on broadcast lifecycle events). * Override the audience entirely with a different ID list (advanced - bypasses both the filter and the failed-contact lookup). For the standard "I want to redrive my failures" case, use the in-product button - it covers 95% of needs without code. Retention [#retention] Per-contact dispatch rows have a **30-day TTL**. After 30 days, the rows are aged out and the failed-contact list is gone - Retry returns a `BAD_USER_INPUT` error explaining there are no failed contacts to look up. The aggregate counters on the broadcast (`delivered`, `failed`, `skipped`) **don't expire** - you can still see the run summary forever. But the per-contact granularity is what Retry needs, and that's the part that ages out. If you want to redrive a broadcast older than 30 days, you have two options: 1. Clone the broadcast as a new one with the same audience filter and run it normally - but you'll re-target everyone, not just the prior failures. 2. Capture failures into a custom contact field at run time (advanced - typically done from a flow that runs as part of the broadcast itself), then filter on that field in a follow-up broadcast. What Retry is *Not* [#what-retry-is-not] * **Not automatic.** There is no "auto-retry every failure for 24 hours" toggle. Each retry is an explicit operator action (or programmatic call). * **Not a delivery guarantee.** Retry attempts the failed contacts again with the same payload. If the underlying cause persists (blocked bot, expired template), the retry fails the same way. * **Not a partial cancel-and-redo.** It doesn't cancel, mutate, or replace the original broadcast. The original stays as-is in `COMPLETED`/`CANCELLED`; the retry is a separate, linked broadcast. # Fallbacks & Template Routes (/docs/broadcasts/routing) The picker step ([Step 3 - Pick a Flow or Pick a Template](/docs/broadcasts/create#step-3--pick-a-flow-or-pick-a-template)) has a small but important second half: * **Flow mode** lets you attach a list of **WhatsApp template fallbacks** for contacts the flow can't reach. * **Template mode** lets you configure one or more **template routes** so a single broadcast can cover several WhatsApp numbers. Both exist because of WhatsApp's **24-hour customer-service window** - Meta rejects freeform messages to contacts whose window is closed, and a one-size-fits-all primary doesn't always cover every contact. WhatsApp Window - The Underlying Rule [#whatsapp-window---the-underlying-rule] | Window state | Meaning | | ------------ | ------------------------------------------------------------------------------------------------------ | | **Open** | The contact has sent your org a WhatsApp message in the last 24 hours. Freeform messages are accepted. | | **Closed** | More than 24h since the contact's last inbound. Only **pre-approved templates** can be sent. | Templates are window-safe by definition: they always go through, regardless of the window. Freeform text, media, and AI-generated cards are not. Flow Mode - Template Fallbacks [#flow-mode---template-fallbacks] When you pick a flow, you can optionally attach **a list of WhatsApp template fallbacks**. Each fallback is a `(template, integration)` pair. **At dispatch time**, for each contact: 1. The dispatcher checks whether the flow can reach the contact through its primary channel. 2. If it can't (typically because the contact's WhatsApp window is closed and the flow's first card isn't a template), the dispatcher looks at the configured fallback list. 3. It picks the fallback whose integration matches the contact's WhatsApp chat and sends that template instead. 4. The per-contact history row is marked as having used the fallback. If you don't configure any fallbacks, contacts the flow can't reach are recorded as **skipped** - no fallback fires. So if your audience contains WhatsApp contacts and your flow's first card is freeform, you almost certainly want at least one fallback. Per-contact decision [#per-contact-decision] | Contact's chat | WA window | Flow's first card | Outcome | | ----------------------------- | --------- | ----------------- | --------------------------------------------------------------------------------------------------- | | WhatsApp | Open | Any | Run the flow | | WhatsApp | Open | WA Template | Run the flow | | WhatsApp | Closed | WA Template | Run the flow (template is window-safe) | | WhatsApp | Closed | Any other card | **Send a fallback template** if one's configured for this WA integration; otherwise mark as skipped | | Telegram / SMS / Viber / etc. | n/a | Any | Run the flow | Why a list of fallbacks? [#why-a-list-of-fallbacks] If you have several WhatsApp numbers, a single fallback template won't work - templates exist per integration. By configuring a list of fallbacks, you can cover EU, US, and APAC WA numbers in one broadcast. The dispatcher picks the fallback whose integration matches each contact's chat. If a contact's WA chat is on an integration **not** covered by any of your fallbacks, no fallback fires - the contact is skipped. Template Mode - Template Routes [#template-mode---template-routes] In template mode, you supply one or more **template routes**. Each route is a `(template, integration)` pair, the same shape as a flow-mode fallback - but routes are the **primary** delivery path for template mode, not a fallback. **At dispatch time**, for each contact, the system picks the route whose integration matches the contact's chat and sends that template. | Contact's chat | Matching route configured? | Outcome | | --------------------------------- | -------------------------- | -------------------------- | | Has a chat on a route integration | ✓ | Send that route's template | | No chat on any route integration | - | Skipped | Multi-region patterns [#multi-region-patterns] A single broadcast with multiple routes can cover several WhatsApp numbers without splitting into separate broadcasts: * One route per region (EU template + EU number, US template + US number, APAC template + APAC number). * One route per language (English template + English-only number, Spanish template + Spanish-only number). * One route per use-case (transactional number for receipts, marketing number for promos - still in one broadcast if the audience spans both). Each contact receives at most one template - the one routed to their chat's integration. Why no flow-style fallback for template mode [#why-no-flow-style-fallback-for-template-mode] Templates are already window-safe. There's nothing to fall back from - if a route matches, the template lands; if no route matches, the contact is skipped. The wizard hides any fallback-list field on template mode and the API rejects fallback config to prevent dead config. Provider Breakdown [#provider-breakdown] The Review step shows a per-provider count derived from your audience and (for template mode) your route integrations: > ≈ 845 Telegram · 312 WhatsApp · 78 Instagram · 16 Viber Use this for compliance ("this audience reaches 312 EU WhatsApp contacts under our DPA") and cost sanity-checks (template fees, Viber out-of-session pricing). It's recomputed any time you change Steps 1, 2, or 3. Common Patterns [#common-patterns] Conversational marketing flow with a safety net [#conversational-marketing-flow-with-a-safety-net] * **Step 2:** Flow. * **Step 3:** Pick the flow (starts with a text card asking a question). Add one fallback template per WhatsApp integration in your audience - the fallback announces the same campaign in template form. * Result: in-window contacts get the conversational experience; out-of-window WA contacts get the announcement. Telegram contacts always get the flow. Multi-region template announcement [#multi-region-template-announcement] * **Step 2:** Template. * **Step 3:** Add one route per region: `(template_es, WA_Spain)`, `(template_pt, WA_Portugal)`, `(template_en, WA_UK)`. * Result: each contact receives the template for their region's WA number. Contacts whose most-recent chat isn't on any of these numbers are skipped. One-channel-only flow [#one-channel-only-flow] * **Step 2:** Flow. * **Step 3:** Pick the flow. No fallbacks. * Result: contacts the flow can reach get it; contacts it can't (closed-window WA) are skipped. Use this when you don't want to send anything to those contacts at all. What Routing Doesn't Cover [#what-routing-doesnt-cover] * **Flow-to-flow window routing.** If you want a *different flow* (not just a fallback template) to run when the WA window is closed, that's not in v1. Use a flow-mode broadcast with template fallbacks instead. * **Cross-channel preference.** If a contact has both Telegram and WhatsApp, you can't say "prefer Telegram for this broadcast" - the dispatcher uses the contact's most-recent chat. * **SMS / email channels.** Broadcasts dispatch through whatever messaging integrations you've connected - Telegram, WhatsApp, Instagram, Viber. SMS and email are not first-class channels for broadcasts at this time. # Scheduling (/docs/broadcasts/scheduling) A broadcast has two time fields: `scheduledAt` (absolute UTC) and `timezone` (IANA zone). The engine fires on `scheduledAt`; the timezone is for **display** and DST round-tripping in the UI. How the Time is Stored [#how-the-time-is-stored] | Field | Stored as | Example | | ------------- | -------------------------- | -------------------------- | | `scheduledAt` | Absolute UTC ISO timestamp | `2026-04-30T07:00:00.000Z` | | `timezone` | IANA zone name | `Europe/Kyiv` | When you pick "Apr 30 at 10:00 Europe/Kyiv", the wizard converts that to UTC for storage and the engine fires on the UTC timestamp. The timezone label is preserved separately so the UI can render the original local time consistently across users in different timezones and across DST transitions. Minimum Lead Time [#minimum-lead-time] `scheduledAt` must be at least **2 minutes** in the future when you confirm. | Why | Detail | | ---------------------- | ------------------------------------------------------------------------------------------------------------------- | | Scheduler tick | The broadcast scheduler polls every 60s. A broadcast scheduled 30s in the future may slip past the first tick. | | Late-fire grace window | The next-tick safety net only covers a 5-minute lateness window - anything tighter risks transitioning to `FAILED`. | The wizard's confirm button is disabled until your time satisfies the 2-minute lead time. The API returns a `BAD_USER_INPUT` error with a clear message if you bypass the UI and call the mutation directly. When the Broadcast Fires [#when-the-broadcast-fires] The scheduler tick runs every **60 seconds**. On each tick: 1. Find all broadcasts where `status = SCHEDULED` and `scheduledAt <= now + 30s`. 2. For each, acquire a firing lock, transition to `SENDING`, resolve the audience, stream contacts in pages of 500, enqueue dispatches. 3. As batches drain, counters roll up and the broadcast eventually transitions to `COMPLETED`. Practical timing: if you schedule a broadcast for `10:00:00`, it almost always fires within `10:00:00 – 10:01:00` UTC. The 30-second look-ahead in the query lets the tick that runs at `09:59:30` already start work for `10:00:00` rather than waiting for the `10:00:30` tick. Late-Fire Policy [#late-fire-policy] If a worker is down or a deploy misses a tick, a broadcast can be picked up after its scheduled time. The policy is: | Age of `scheduledAt` at pickup | Action | | --------------------------------- | ------------------------------------------------------------------------------------------------------ | | `now − 5 min ≤ scheduledAt ≤ now` | **Fire now.** Operator intent is honoured; the small grace window covers normal scheduler hiccups. | | `scheduledAt < now − 5 min` | Transition `SCHEDULED → FAILED` with `failureReason = MISSED_WINDOW`. The broadcast does **not** fire. | We will never silently fire a broadcast hours late. If your "9am Monday" send didn't go out by 9:05am Monday, it transitions to `FAILED` instead of becoming a "9pm Monday" send by accident. When this happens, the detail page shows the failure reason and a **Retry** button that opens a fresh wizard with the same audience and payload pre-filled, ready to re-schedule. DST and Timezone Edge Cases [#dst-and-timezone-edge-cases] The stored `timezone` solves the most common headaches: * **Spring-forward / fall-back transitions** - picking "March 30 02:30 Europe/London" works fine; the UI converts to UTC at save time using the current rules. (If the chosen local time is ambiguous because of a DST jump, the wizard surfaces a hint and lets you pick which interpretation you mean.) * **Cross-timezone teams** - colleagues in different regions still see the run time labelled with the original zone you picked. There is no silent conversion. * **Org policy changes** - moving your default timezone in **Settings** affects only the default for new wizards; existing scheduled broadcasts keep the timezone you originally picked. Editing the Schedule [#editing-the-schedule] You can change `scheduledAt` and `timezone` while the broadcast is `SCHEDULED`. Once it transitions to `SENDING`, the schedule is locked - partial dispatch is in flight. The same 2-minute lead-time check applies to edits. You cannot push the time so far back that it would already have fired by the next tick. What Recurrence Means Here [#what-recurrence-means-here] Broadcasts **do not recur**. The Schedule step has no cron field, no "every Monday" toggle. Two reasons: 1. **Auditability.** Each fire has its own per-contact history page. A recurring broadcast would smear that across N runs. 2. **Plumbing fit.** Recurring sends are exactly what [scheduled triggers](/docs/flows/editor/triggers/scheduled) are for, and the trigger system already supports cron expressions and "relative to a contact date field" semantics. If you genuinely need a recurring send, model it as a recurring scheduled trigger on a flow. If you need "the same audience and payload, three weeks in a row," create three broadcasts. Soft Deadlines and Worker Health [#soft-deadlines-and-worker-health] The scheduler runs on the worker process (separate from the API process), with these properties: * A periodic **stuck-broadcast sweeper** rescues broadcasts pinned in `SENDING` for >10 minutes with no recent dispatch activity. See [Lifecycle → Sweeper](/docs/broadcasts/lifecycle#stuck-broadcast-sweeper) for what it does. * A **firing lock** prevents two workers from fanning the same broadcast out twice - even during failover. * Per-batch dispatch is bounded by **rate-limit handling** at the provider layer; if WhatsApp returns 429, the unfinished tail re-enqueues with backoff and the broadcast stays in `SENDING` until the queue drains. Operators don't need to tune any of this; it's mentioned here only so the timing model is transparent. # Flows (/docs/flows) **Flows** is Wexio's visual automation builder that lets you create powerful workflows without writing code. Overview [#overview] Flows use a node-based canvas where you connect triggers, conditions, and actions to automate customer interactions. Each flow is a directed graph of **cards** (nodes) that execute in sequence, built on top of a modular execution engine. Getting Started [#getting-started] 1. Navigate to **Flows** from the sidebar 2. Click **Create Flow** 3. Give your flow a name and description 4. You'll be taken to the [visual flow editor](/docs/flows/editor) Flow Lifecycle [#flow-lifecycle] Every flow follows a **Draft → Published** lifecycle: | State | Description | | ------------- | ----------------------------------------------------------- | | **Draft** | Edits happen here - the bot does **not** execute this state | | **Published** | An immutable snapshot of the draft, actively running | Editing a published flow is always safe - your changes stay in the draft and never affect live bot execution until you explicitly publish again. Each publish creates a new [version](/docs/flows/editor/version-history) and an entry in the [activity log](/docs/flows/editor/version-history#activity-log). When you unpublish a flow, the bot stops triggering it. Your draft, version history, and activity log are all preserved - you can re-publish at any time. Flow Management [#flow-management] The flows list page shows all your automations: | Column | Description | | ----------------- | -------------------------------------- | | **Name** | Flow identifier | | **Status** | Published (active) or Draft (inactive) | | **Trigger** | What starts the flow | | **Last modified** | When it was last updated | | **Created by** | Who created the flow | Quick Actions [#quick-actions] | Action | Description | | ----------------------- | ------------------------------- | | **Edit** | Open the flow editor | | **Duplicate** | Create a copy of the flow | | **Publish / Unpublish** | Activate or deactivate the flow | | **Delete** | Remove the flow permanently | How Execution Works [#how-execution-works] When a flow is published and a trigger fires: 1. The execution engine loads the **published snapshot** (not the draft) 2. A new execution context is created with [variables](/docs/flows/editor/variables) (contact, chat, system, org) 3. Cards execute in sequence following the connection paths 4. When a card requires user input (e.g., [Question](/docs/flows/cards/question), [Button](/docs/flows/cards/button)), the flow pauses and waits 5. On user reply, the flow resumes from where it left off 6. The flow completes when it reaches a card with no outgoing connection, or when the [execution timeout](/docs/flows/settings) expires Timeouts [#timeouts] Each flow has a configurable execution timeout (default: 5 minutes). If the flow doesn't complete within this time, it's automatically terminated. [Sub-flows](/docs/flows/cards/sub-flow) have their own independent timeout (default: 1 minute). Strict Mode [#strict-mode] When strict mode is enabled in [flow settings](/docs/flows/settings), the flow stops on any card error instead of silently continuing. You can configure a custom error message shown to the contact. Use Cases [#use-cases] * **Welcome messages** - Automatically greet new contacts * **Auto-assignment** - Route conversations to the right team member * **Follow-ups** - Send reminders after a period of inactivity * **Data collection** - Ask questions and save responses to contact fields * **Lead qualification** - Classify and score leads with AI * **Support automation** - Handle common requests with [AI agents](/docs/flows/cards/ai-agent) * **Escalation** - Route complex issues to senior operators via [Assign Operator](/docs/flows/cards/assign-operator) * **CRM sync** - Push contact data to external CRM systems # Getting Started (/docs/getting-started) Welcome to Wexio! This guide will walk you through setting up your workspace, connecting your first messaging channel, and sending your first message. Step 1: Create an Account [#step-1-create-an-account] Sign up at [app.wexio.io](https://app.wexio.io) using one of these methods: * **Google** - Sign in with your Google account * **GitHub** - Sign in with your GitHub account * **Microsoft** - Sign in with your Microsoft account * **Passkey** - Use biometric authentication (Touch ID, Face ID, security keys) * **SSO / SAML** - Enterprise single sign-on via your Identity Provider (Enterprise plan only) Step 2: Create Your Organisation [#step-2-create-your-organisation] After signing up, you'll be prompted to create your first organisation through the [onboarding wizard](/docs/getting-started/onboarding). This is your workspace where all team members, conversations, and automations live. * **Organisation name** - Choose a descriptive name for your team or business * **Organisation slug** - A URL-friendly identifier (auto-generated from name) Wexio getting started onboarding Step 3: Connect a Messaging Channel [#step-3-connect-a-messaging-channel] During onboarding, you'll be guided to connect your first messaging integration: * **Telegram** - Connect your Telegram bot to start receiving messages * **WhatsApp** *(coming soon)* - WhatsApp Business API integration * **Instagram** *(coming soon)* - Instagram Direct messaging * **Viber** *(coming soon)* - Viber bot integration You can skip this step and connect integrations later from **Settings → Integrations**. Step 4: Set Up Billing [#step-4-set-up-billing] Choose a plan that fits your needs. Wexio offers plans from Free to Enterprise - view full pricing details and compare plans at [wexio.io/pricing](https://wexio.io/pricing). Next Steps [#next-steps] # Onboarding (/docs/getting-started/onboarding) The onboarding wizard guides you through the initial setup of your Wexio workspace. It walks you through creating your organisation, connecting a messaging channel, and choosing a plan. Step 1: Create Organisation [#step-1-create-organisation] The first step is creating your organisation - your workspace where all team members, conversations, and automations live. Fill in the following fields: | Field | Required | Description | | --------------------- | -------- | -------------------------------------------------------------------------------------------------------------- | | **Organisation Name** | Yes | A descriptive name for your team or business | | **Organisation URL** | Yes | A URL-friendly slug (auto-generated from name, editable). Only lowercase letters, numbers, `-` and `_` allowed | | **Organisation Type** | Yes | Select the type of organisation | | **Business Type** | Yes | Select your industry | | **Work Email** | Yes | Pre-filled from your account email | | **Country** | Yes | Searchable country selector | | **Website** | No | Your company website URL | Click **Create Organisation** to proceed. Step 2: Connect a Bot [#step-2-connect-a-bot] After creating your organisation, you'll be prompted to connect a messaging channel. Currently, **Telegram** is the primary supported channel - WhatsApp, Viber, and Instagram are coming soon. Connecting Telegram [#connecting-telegram] 1. Open [@BotFather](https://t.me/BotFather) on Telegram 2. Send the `/newbot` command and follow the prompts 3. Copy the **bot token** provided by BotFather 4. Paste it into the **Bot Token** field 5. Click **Connect Bot** You can **skip** this step and connect a bot later from [Settings → Integrations](/docs/settings/integrations). Step 3: Choose Your Plan [#step-3-choose-your-plan] Select a billing plan that fits your needs. Wexio offers plans from Free to Enterprise - view full pricing details at [wexio.io/pricing](https://wexio.io/pricing). Free Plan [#free-plan] If you choose the **Free** plan, it activates instantly - no payment details required. You'll be redirected straight to your Inbox and can upgrade anytime from [Settings → Billing](/docs/settings/billing). Paid Plans [#paid-plans] If you choose a paid plan, you'll be asked to add your payment details. All payments are securely processed through [Stripe](https://stripe.com). Every paid plan includes a **free trial** period so you can explore all features before being charged. Enter your card details and click **Start Free Trial** to proceed. You can cancel or change your plan at any time from [Settings → Billing](/docs/settings/billing). Step 4: Done! [#step-4-done] Once you've completed the wizard, you'll be redirected to your [Inbox](/docs/inbox) where you can start managing conversations. # Workspace Overview (/docs/getting-started/workspace) Your Wexio workspace (organisation) is the central hub where all your team's activity happens. Navigation [#navigation] The main sidebar provides access to all features: * **Inbox** - Customer conversations * **People** - Contact database * **Flows** - Automation workflows * **Library** - Templates and quick replies * **Analytics** - Performance dashboards * **Integrations** - Connected services * **Settings** - Workspace configuration Organisation Switcher [#organisation-switcher] If you belong to multiple organisations, you can switch between them using the organisation switcher in the top-left corner of the sidebar. Roles & Permissions [#roles--permissions] Wexio uses role-based access control with four roles: | Role | Description | | ---------- | ----------------------------------------------------------------------------------- | | **Owner** | Full access, billing management, can delete organisation | | **Admin** | Full access to all features, can manage team members and settings | | **Editor** | Can create and edit flows, templates, and contacts - cannot manage team or settings | | **Agent** | Inbox-only access - can view and respond to conversations | Permission breakdown [#permission-breakdown] | Permission | Owner | Admin | Editor | Agent | | ------------------------------- | :---: | :---: | :----: | :---: | | Access Inbox & respond to chats | ✅ | ✅ | ✅ | ✅ | | Manage contacts (People) | ✅ | ✅ | ✅ | ❌ | | Create & edit Flows | ✅ | ✅ | ✅ | ❌ | | Create & edit Templates | ✅ | ✅ | ✅ | ❌ | | Manage Integrations | ✅ | ✅ | ❌ | ❌ | | Manage Team members | ✅ | ✅ | ❌ | ❌ | | Manage Organisation settings | ✅ | ✅ | ❌ | ❌ | | Manage Billing & subscription | ✅ | ❌ | ❌ | ❌ | | Delete Organisation | ✅ | ❌ | ❌ | ❌ | Some features like **Integrations management** and **Team settings** require Admin or Owner role. *** Related [#related] # Collections (/docs/inbox/collections) Collections are saved filter configurations that create custom views of your inbox. They appear in the left sidebar for one-click access to specific groups of conversations. Default Collections [#default-collections] Every workspace comes with built-in collections: | Collection | Description | | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | **All Chats** | Every conversation across all channels | | **Assigned to Me** | Chats assigned to the current user | | **[@Mentions](/docs/inbox/mentions)** | Internal notes that mention you - switches the right panel from a chat list to a **flat list of mentions** with unread/read filters and search | | **Blocked** | Conversations marked as blocked | | **Urgent** | Conversations flagged as urgent | | **Unassigned** | Conversations not assigned to any operator | The **Blocked**, **Urgent**, and **Unassigned** collections can be reordered in the sidebar but cannot be edited or deleted. **@Mentions** behaves a little differently from the other built-ins. The other collections all show a filtered list of **chats**; @Mentions shows a flat list of **messages** (specifically internal notes that mention you), with its own unread badge, filters (All / Unread / Read), and "Mark all read" affordance. See [Mentions & Internal Notes](/docs/inbox/mentions) for the full surface. Creating a Collection [#creating-a-collection] 1. Open the **Filter** panel and configure your filter criteria (direction, labels, channels, assignee, etc.). 2. Once your filters are applied, click **Save as Collection** in the filter bar. 3. In the modal, enter a **Name**, **Slug** (URL-friendly identifier), and optional **Description**. 4. Choose an **icon** for visual identification. 5. Click **Create Collection**. The new collection appears in the sidebar under **Collections** with a live count of matching conversations. Managing Collections [#managing-collections] | Action | Applies To | Description | | ----------- | ----------------------- | ------------------------------------------------ | | **Reorder** | All collections | Drag and drop to change sidebar position | | **Edit** | Custom collections only | Update name, description, icon, and filter rules | | **Delete** | Custom collections only | Remove the collection | Deleting a collection does not delete the conversations within it - they remain accessible in "All Chats". Teammates [#teammates] The sidebar also shows your teammates and their online status, so you can quickly see who's available for reassignment. Real-time Updates [#real-time-updates] Collections update in real time - chat counts refresh as conversations are added or removed based on filter criteria. # Inbox (/docs/inbox) The Inbox is the heart of Wexio - a unified hub where all customer messages from connected channels appear in real time. Overview [#overview] When you navigate to **Inbox**, you'll see a three-panel layout: * **Left sidebar** - Collections, teammate statuses, and navigation * **Center panel** - Chat list with search, filters, and unread counts * **Right panel** - Active conversation thread with message input and contact profile Layout [#layout] The inbox uses a responsive layout that adapts to your screen size. On mobile, panels stack vertically with swipe navigation. Collections Sidebar [#collections-sidebar] The left sidebar shows your [chat collections](/docs/inbox/collections) - custom filtered views of your conversations. Built-in collections include **All Chats**, **Assigned to Me**, **Unassigned**, **Urgent**, and **Blocked**. You can create custom collections with [filter rules](/docs/inbox/search). A separate **[@Mentions](/docs/inbox/mentions)** row sits next to **Assigned to Me** - it switches the right panel from a chat list to a flat list of internal notes that mention you, with an unread count badge. Chat List [#chat-list] The center panel shows conversations matching the current collection. Each item displays: * Contact name and avatar * Last message preview with timestamp * Unread message count badge * Chat status indicator (color-coded) * Channel provider icon (Telegram, WhatsApp, etc.) Real-time subscriptions keep the list updated as new messages arrive, statuses change, or chats are reassigned. Chat View [#chat-view] The right panel opens when you select a conversation, showing the full [message thread](/docs/inbox/chat/messages), [input editor](/docs/inbox/chat/editor), and [AI controls](/docs/inbox/chat/ai-bar). Chat Status [#chat-status] Each conversation has a status to help you track progress: | Status | Color | Description | | --------------- | ------ | ----------------------------------- | | **Pending** | Yellow | New conversation awaiting response | | **In Progress** | Blue | Actively being handled | | **Resolved** | Green | Issue resolved, conversation closed | | **Closed** | Gray | Manually closed by operator | | **Issue** | Red | Flagged for follow-up | Priority Levels [#priority-levels] Mark conversations as **Low**, **Medium**, or **High** priority to ensure critical issues surface first. Operator Assignment [#operator-assignment] Assign conversations to specific team members. View all chats assigned to you via the **"Assigned to me"** collection. Chat Actions [#chat-actions] Use the action menu on a conversation for: * **Assign operator** - Route to a team member * **Change status** - Update the conversation status * **Set category** - Categorize the conversation * **Set priority** - Mark as urgent or normal * **Block/Unblock** - Block a contact from messaging AI & Flow Status [#ai--flow-status] See at a glance if a chat has: * **AI Active** - An [AI assistant](/docs/ai) is responding * **Flow running** - An [automation flow](/docs/flows) is in progress 24-Hour Window [#24-hour-window] For channels like WhatsApp, Instagram, and Viber, there's a 24-hour messaging window. Once a user replies, all messages within that 24-hour session are free. After the window expires: * **WhatsApp & Instagram** - Outbound messages are blocked. You must use [approved templates](/docs/inbox/chat/editor/templates) to re-initiate contact. * **Viber** - You can send a new initiated message, but it is a paid outbound message. # Mentions & Internal Notes (/docs/inbox/mentions) Need to loop a teammate into a conversation? Tag them right in the chat - no Slack hop, no copy-pasting context, no "hey can you check this?" message. Type `@`, pick a teammate, write your question. They get pinged. The customer never sees it. Mentions and internal notes are available on **every plan** - Free, Standard, Pro, and Enterprise. The [live co-editor](/docs/inbox/chat/collaboration) is the Pro+ part of teamwork; tagging a teammate works for everyone. Sending an Internal Note [#sending-an-internal-note] 1. Open any customer conversation. 2. In the composer, type `@`. A picker appears with your org's teammates. 3. Pick the one you want to loop in. A coloured chip lands in the composer (e.g. `@Alice`). 4. The composer instantly shifts into **internal-note mode** so you see the change before pressing Send: * Background tints amber. * The send button relabels and turns amber too. * A small banner reads: *"Internal note - won't be sent to the customer."* 5. Type your question or context: *"@Alice can you double-check the refund eligibility on this account before I respond?"* 6. Hit Send. The note lands in the chat thread with a yellow-tinted bubble and an "INTERNAL NOTE" label. The customer's WhatsApp / Telegram / Instagram / Viber **never receives it**. You can mention multiple teammates in one note (`@Alice @Bob`). Each one gets their own notification. Mentioning multiple teammates [#mentioning-multiple-teammates] You can stack several chips in one note. Each chip resolves to a specific teammate, and each gets a notification. Mentioning the same person twice is fine - Wexio deduplicates so they only get pinged once. Removing the last chip [#removing-the-last-chip] Removing the last chip flips the composer back to **regular customer-bound mode** - amber tint disappears, banner goes away, the next Send dispatches to the customer's channel as a normal reply. The internal-note state is **driven by chips, not by a toggle**, so you always know which way the message is going. Stray `@` is safe [#stray--is-safe] A stray `@` in plain text - `user@example.com`, `@main branch`, an email signature - never opens the picker, never produces a chip, and never accidentally turns your reply into a private note. The autocomplete is the gate; only chips count. What the Mentioned Teammate Sees [#what-the-mentioned-teammate-sees] 1\. The bell-dropdown notification [#1-the-bell-dropdown-notification] Top-right of the app, in real time: > **Ralph J. Lesperance** > *Mentioned you in* **`Vasily Putra`** ← amber chip > \[View] Click "View" or anywhere on the row → jump to the chat with the note in view. 2\. The @Mentions sidebar tab [#2-the-mentions-sidebar-tab] A new top-level row in the inbox sidebar, right next to **Assigned to me**: ``` Inbox All chats 53 Assigned to me 1 @Mentions 3 ← unread count Chats (3) Urgent Blocked … ``` Click **@Mentions** and the right panel switches from a chat list to a flat list of mentions. | Control | Behavior | | ----------------------- | ------------------------------------------------------- | | **All / Unread / Read** | Filter the feed by read state. | | **Search** | Case-insensitive substring match against the note text. | | **Mark all read** | Top-right; clears every unread mention in one click. | Each row shows the customer chat (avatar, name, provider badge, assigned operator), the time, and a snippet of the note body. Unread rows have an amber left rail and a small dot on the avatar. Click any row to open the chat at the exact mention point. 3\. Auto mark-as-read [#3-auto-mark-as-read] You don't have to click anything to clear the badge. As you scroll through the chat thread, any internal notes that scroll into view get marked read automatically. The read state syncs across all your tabs and devices instantly. "Read by …" Indicator [#read-by--indicator] After you post an internal note, look at the bottom-right of the note bubble. You'll see one of three states: | Indicator | Meaning | | ----------------------- | ------------------------------------------ | | ⏱ **Awaiting reads** | No one's seen it yet. | | ✓ **Read by 1 of 2** | One teammate caught up; one still pending. | | ✓✓ **Read by everyone** | Full team is in sync. | Hover the indicator and a panel slides out with each recipient: * Their avatar. * Their name. * Either *"2 minutes ago"* (if they've read it) or *"Unread"*. Read recipients sort first (most recent on top); unread fall to the bottom - same pattern as iMessage / WhatsApp delivery views. Editing an Internal Note [#editing-an-internal-note] You can edit the chips and the body just like any other message. The behavior is symmetric: | Action | Effect | | --------------------- | ----------------------------------------------------------------------------------------------------- | | **Add a new mention** | That teammate gets a fresh notification + a new entry in their **@Mentions** tab. | | **Remove a mention** | That teammate's notification + sidebar row disappear; their unread count decrements if it was unread. | | **Edit the text** | The chat-thread bubble updates for everyone. | The note keeps its **internal-note identity** even if you remove every chip. The yellow tint and inline placement stay; it just means nobody specific is being pinged anymore. You **can't promote** a regular customer message into an internal note (or vice versa). The type is set at send time and locked - the first would be dishonest (the text already shipped to the provider), the second would silently leak previously-private content. Re-adding a previously-mentioned teammate [#re-adding-a-previously-mentioned-teammate] If you remove someone and later add them back, they get a **fresh receipt** - a new notification, badge increment, unread state. Read state from the previous lifecycle doesn't carry over. This is the intuitive "you were re-mentioned" behavior. Deleting a Note [#deleting-a-note] Soft-delete works the same as any message - the bubble shows a *"Note deleted"* tombstone. All mention receipts associated with it are filtered out of the **@Mentions** tab and the sidebar badge decrements for any operator who hadn't read it yet. Receipts stay in the audit history (so a future "all mentions Alice ever received" report still includes the deleted note), but the sidebar feed and unread badge ignore them. Email Fallback [#email-fallback] If a mentioned teammate is **offline** when you send the note, they get an email automatically - no opt-in needed. The email contains a deep link to the chat, scrolled to the note. Click it and you're right there. Online teammates skip the email - the in-app toast and the bell notification are enough; emailing them on top would be noisy. The email template is localised in English, German, Ukrainian, Italian, Portuguese, and Polish. Reply-to + Mention [#reply-to--mention] You can attach a mention to a **reply-to** of a customer message. The note threads under the customer message in the chat view, and the mentioned teammate jumping in from the sidebar lands directly in context. Example: * **Customer:** *"I want a refund."* * **You** (Reply → on the customer's message): *"@manager refund eligible?"* * **Manager** clicks the bell → lands in the chat scrolled to your note, which renders threaded under the refund request. Useful when several operators are discussing the same customer issue - internal replies form a thread anchored to the original mention. Where Notes Show Up [#where-notes-show-up] Internal notes are real messages - they live in the chat history forever and surface in: * **The chat thread** (yellow-tinted, inline with customer/operator messages). * **In-chat search** results (when the search term matches the note text). * **Exports** and GDPR data dumps. What they **never** do is leave the org boundary - no provider call, no AI-generated auto-reply, no outbound webhook. The dispatch layer refuses based on the message type itself, not a flag, so even future code paths can't accidentally leak. FAQ [#faq] **Does the customer ever see internal notes?** No. Never. They live in the chat history but never get dispatched to WhatsApp / Telegram / Instagram / Viber. Even an AI auto-reply skips them. **Can I @-mention the customer?** No. The picker only shows org members. A stray `@` in customer reply text (like `user@example.com`) stays as plain text - no chip, no surprise internal note. **What if I post a note without mentioning anyone?** You can't - the moment you remove the last chip, the composer flips back to normal customer-bound mode. To save context for yourself without notifying anyone else, mention yourself (you'll see it in the chat thread but won't get a notification - self-mentions are skipped server-side). **My teammate edited their post and added me. Do I get notified?** Yes - adding a mention via edit produces the same notification as a fresh post. The opposite holds: removing a chip via edit retracts the notification. **Can I mention the same teammate twice?** You can paste the chip twice, but Wexio deduplicates. They'll only get one notification per recipient per note. **Multiple chats, multiple mentions - can I bulk-clear?** Yes - open the **@Mentions** tab and click **Mark all read** in the top right. Single click, all unread mentions clear, badge goes to 0. **Can a teammate who's no longer in the org receive new mentions?** No. The picker hides removed members, and even if a stale UI somehow tries to mention them, the server short-circuits the notification at publish time. Their old receipts stay in audit history but they don't get pinged. **Are notes counted toward operations cost?** No. Internal notes don't dispatch through any provider, so they don't consume the per-message ops budget. They cost storage like any other message and that's it. Related [#related] * [Live Collaboration](/docs/inbox/chat/collaboration) - the other half of teamwork in the inbox: shared cursors, presence avatars, and live co-edit of the composer (Pro+). * [Chat Editor](/docs/inbox/chat/editor) - composer features and how `@` autocomplete fits with attachments, AI, and templates. * [Search & Filters](/docs/inbox/search) - find conversations and message-level matches, including notes. # Contact Profile (/docs/inbox/profile) The profile sidebar appears on the right side of the chat view, showing detailed information about the contact you're conversing with. Opening the Profile [#opening-the-profile] Toggle the profile sidebar by clicking the **profile icon** in the chat header. The sidebar slides in from the right. Profile Information [#profile-information] Contact Details [#contact-details] | Field | Description | | ------------ | --------------------------------------------------- | | **Avatar** | Profile picture from the messaging channel | | **Name** | Contact's display name | | **Username** | Channel-specific username | | **Phone** | Phone number (if available) | | **Email** | Email address (if available) | | **Channel** | Which messaging platform (Telegram, WhatsApp, etc.) | Custom Fields [#custom-fields] View and edit custom fields defined in your workspace. Only custom fields are editable directly from the profile - click any custom field value to modify it. Changes are saved automatically. * Text fields, number fields, date fields * Dropdown selections * Boolean toggles Related Contacts [#related-contacts] If the same person has contacted you through multiple channels (e.g. Telegram and WhatsApp), their related profiles are shown here. Click a related contact to navigate to their conversation on that channel. For managing profile fields, privacy settings, and creating custom fields, see [People Settings](/docs/settings/people). For bulk contact management, see [People](/docs/people). # Search & Filters (/docs/inbox/search) Wexio provides powerful search and filtering to help you find specific conversations or messages instantly. Search Modes [#search-modes] The search bar supports two distinct modes, toggled by the search mode icon: Search by Chats [#search-by-chats] Search across conversation names and contact details. Results show matching chat threads with preview snippets. Search by Messages [#search-by-messages] Full-text search across all message content. Results show individual messages with highlighted matches and links to their parent conversations. Filter Sidebar [#filter-sidebar] Click the **filter icon** (funnel) to open the advanced filter sidebar. The sidebar has two states: * **Search** - Quick filtering with active filters displayed as badges * **Edit** - Full filter configuration mode with rule builder Filter Fields [#filter-fields] | Filter | Description | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Direction** | All, Inbound, or Outbound | | **Message Types** | [Text](/docs/inbox/chat/messages/text), [Media](/docs/inbox/chat/messages/media), [Buttons](/docs/inbox/chat/messages/buttons), [Media Buttons](/docs/inbox/chat/messages/media-buttons), Media Group, [Contact](/docs/inbox/chat/messages/contact), [Buttons List](/docs/inbox/chat/messages/list), [Poll](/docs/inbox/chat/messages/poll), [Template](/docs/inbox/chat/messages/template), [Game](/docs/inbox/chat/messages/game), [Dice](/docs/inbox/chat/messages/dice), [Location](/docs/inbox/chat/messages/location) | | **Labels** | Filter by [message labels](/docs/inbox/chat/messages) (e.g. Media, Question, Important, Follow-up, Resolved, Feedback, Bug Report, Feature Request, Sales Lead, Support, Pending) | | **Bot** | Filter by a specific bot or select All Bots - see [Integrations](/docs/settings/integrations) | | **Profile Fields** | Filter by any custom [contact profile](/docs/inbox/profile) field | Rule Logic [#rule-logic] All filter conditions are combined with **AND** logic - every condition must match for a conversation to appear in the results. Active Filters [#active-filters] Active filters appear as badges above the chat list. Click **×** on any badge to remove it, or use the **Reset** button to clear all filters at once. Filters you configure in the sidebar can be saved as a Collection for quick reuse. See [Collections](/docs/inbox/collections). Saving Filters as Collections [#saving-filters-as-collections] When you've configured a useful filter combination, you can save it as a [Collection](/docs/inbox/collections) for quick reuse: 1. Click **Save as Collection** in the filter sidebar 2. Give it a name and optional description 3. Choose an icon for visual identification 4. The collection appears in the left sidebar for one-click access Learn more about managing collections in the [Collections](/docs/inbox/collections) documentation. In-Chat Message Search [#in-chat-message-search] You can also search within a specific conversation. Click the **search icon** in the [chat header](/docs/inbox/chat/header) to open the in-chat search bar. * Type your query to find matching messages within the current chat * The counter shows the current match and total results (e.g. **3 of 9**) * Use the **up/down arrows** or your keyboard to navigate between matches * Click on a result to jump directly to that message in the thread * Press **×** to close the search bar # Bulk Operations (/docs/people/bulk-operations) Wexio supports bulk export and import of contacts through [CRM integrations](/docs/settings/integrations/crm). Access bulk operations from the **People** page via the **Export** and **Import** buttons. Export to CRM [#export-to-crm] Push contacts from Wexio to your connected CRM ([HubSpot](/docs/settings/integrations/crm#hubspot), [Salesforce](/docs/settings/integrations/crm#salesforce), [BigQuery](/docs/settings/integrations/crm#bigquery)). Steps [#steps] 1. Click **Export** on the People page 2. Select the target CRM integration 3. Optionally filter which contacts to export: * **Search** by name, email, phone * **Advanced Filters** - build conditions on any field (e.g. Wexio ID contains, Chat Status equals) * **Bot filter** - only export people from a specific bot (useful with multiple bots) 4. Start the export Filtering [#filtering] You can export: * **All people** - no filter applied * **Filtered subset** - apply search and/or advanced filters before exporting Advanced filters support the same operators as the [People table](/docs/people#filtering) - combine multiple conditions on any system or custom field. Make sure [field mappings](/docs/settings/integrations/crm) are configured before exporting. Only mapped fields will be synced to the CRM. Import from CRM [#import-from-crm] Pull contacts from your CRM into Wexio. Steps [#steps-1] 1. Click **Import** on the People page 2. Select the source CRM integration 3. Optionally associate a bot (see below) 4. Optionally set a record limit 5. Start the import Bot Association [#bot-association] Associate imported contacts with a messaging bot to enable matching by channel-specific IDs (e.g. `telegram_id`, `username`). Without a bot association, records are matched using: 1. **CRM Record ID** - native CRM ID if the contact was previously synced 2. **Wexio ID** - internal ID stored in the CRM With a bot selected, additional matching keys become available (e.g. `telegram_id`, `email`, custom field). Record Limit [#record-limit] Set a maximum number of contacts to import. Leave empty for no limit. Make sure [field mappings](/docs/settings/integrations/crm) are configured before importing. Only mapped fields will be synced from the CRM. File Export [#file-export] Export contacts as a downloadable file from the People page: * Formats: CSV, Excel, JSON * Modes: Selected contacts, all contacts, or filtered results File exports appear in the [Jobs → File Exports](/docs/people/jobs) tab with a download link that expires after 1 day. Bulk Delete [#bulk-delete] Select multiple contacts with checkboxes on the [People](/docs/people) table, then click **Delete** to remove them in bulk (requires permission). Job Tracking [#job-tracking] All export and import operations run as background jobs. Track progress and results in [People → Jobs](/docs/people/jobs). # CRM Sync (/docs/people/crm-sync) Wexio can synchronize contact data bidirectionally with external CRM systems, keeping your customer data consistent across platforms. Overview [#overview] CRM Sync connects your Wexio contacts with an external CRM (like HubSpot, Salesforce, or custom solutions): * **Push** - Send Wexio contact data to your CRM * **Pull** - Import CRM data into Wexio contact fields * **Bidirectional** - Keep both systems in sync continuously Setup [#setup] 1. Connect your CRM in [Settings → Integrations](/docs/settings/integrations/crm) 2. Configure field mappings (which Wexio fields map to which CRM fields) 3. Enable sync direction (push, pull, or both) 4. Set sync frequency Field Mapping [#field-mapping] Map Wexio fields to CRM fields: | Wexio Field | CRM Field | | ------------- | --------------------- | | First name | first\_name | | Last name | last\_name | | Email | email\_address | | Phone | phone\_number | | Custom fields | Custom CRM properties | Sync Modes [#sync-modes] | Mode | Description | | ----------------- | ----------------------------------------- | | **Push only** | Wexio → CRM | | **Pull only** | CRM → Wexio | | **Bidirectional** | Both directions, with conflict resolution | Bulk Operations [#bulk-operations] CRM sync integrates with [bulk operations](/docs/people/bulk-operations): * **Bulk export** - Export selected contacts to CRM * **Bulk import** - Import CRM contacts into Wexio * Background processing via job queue for large datasets Operations Cost [#operations-cost] CRM sync operations consume ops based on the number of records processed. See [Ops Usage](/docs/ops-usage) for the formula. CRM Sync runs in the background using a job queue (BullMQ), so large sync operations don't block the UI. # People (/docs/people) The **People** section is your customer relationship database - a central place to manage all contacts who interact with your business through any connected channel. Table [#table] The People table displays all contacts with powerful data management features. Columns [#columns] Configure which columns are visible via the **Columns** button: * Toggle individual column visibility * Pin columns to left or right (up to 3 per side) * Name, CRM status, and actions are always visible * Column preferences are saved per workspace Search [#search] The search bar performs full-text search across contact fields with instant results. Filtering [#filtering] **Quick filters** appear as dropdowns above the table: * **Messages** - Filter by message count * **Language** - Contact's preferred language * **Chat Status** - Pending, In Progress, Resolved, Closed, Issue * **Chat Category** - Support, Sales, Feedback, General, Inquiry * **Priority** - Low, Medium, High, Urgent **Advanced filters** open a multi-condition filter builder with operators per field type: | Field type | Operators | | -------------- | ------------------------------------------------------------------------------ | | **Text** | Equals, Contains, Not contains, Starts with, Ends with, Is empty, Is not empty | | **Number** | Equals, Greater than, Less than, Greater or equal, Less or equal, Is empty | | **Date** | Equals, Before, After, Between, Is empty, Is not empty | | **Boolean** | Equals, Is empty | | **Enum/Array** | In, Not in, Is empty, Is not empty | Sorting [#sorting] Click any column header to sort ascending or descending. Sort state persists in the URL. Pagination [#pagination] * Page sizes: 10, 25, 50, 100 * Virtual scrolling for smooth navigation System Fields [#system-fields] Every contact has built-in system fields (38 by default). Click a contact row to open the profile sidebar: Identity [#identity] | Field | Description | | ---------------- | ------------------------------ | | **Wexio ID** | Unique ID in Wexio (read-only) | | **Telegram ID** | Telegram user ID (read-only) | | **WhatsApp ID** | WhatsApp phone ID (read-only) | | **Viber ID** | Viber user ID (read-only) | | **Instagram ID** | Instagram user ID (read-only) | | **Username** | Messaging platform username | | **Email** | Contact email address | | **Phone** | Phone number | Profile [#profile] | Field | Description | | ------------------- | ------------------------------ | | **Opt-in WhatsApp** | Whether contact opted in | | **Language** | Preferred language | | **Company** | Company name | | **Location** | Contact location | | **Is valid** | Whether the contact is valid | | **Is blocked** | Whether the contact is blocked | Chat & Classification [#chat--classification] | Field | Description | | -------------------- | --------------------------------------------- | | **Chat status** | Pending, In Progress, Resolved, Closed, Issue | | **Chat category** | Support, Sales, Feedback, General, Inquiry | | **Priority** | Low, Medium, High, Urgent | | **Lead source** | Where the lead came from | | **Lead status** | Lead lifecycle stage | | **Interest level** | Low, Medium, High | | **Product interest** | Product or service interest | Activity Metrics (read-only) [#activity-metrics-read-only] | Field | Description | | ----------------------------- | ---------------------------------- | | **Registration date** | When the contact was created | | **Total messages sent** | Messages sent by contact | | **Total messages received** | Messages received by contact | | **Last message sent at** | Timestamp of last outbound message | | **Last message received at** | Timestamp of last inbound message | | **Last seen at** | When contact was last active | | **First message received at** | Timestamp of very first message | | **Total flows started** | Number of flows triggered | | **Total flows completed** | Number of flows finished | | **Flow completion rate** | Percentage of completed flows | | **Last flow completed** | When the last flow finished | | **Total AI messages sent** | AI-generated messages count | | **Total bot messages sent** | Bot messages count | | **Total user messages sent** | Operator messages count | Custom Fields [#custom-fields] Beyond system fields, you can define your own fields to track business-specific data. See [Custom Fields](/docs/settings/people) for details. For CRM field synchronization, see [CRM Sync](/docs/people/crm-sync). Contact Profile Sidebar [#contact-profile-sidebar] Click any contact to open a profile sidebar showing all system fields, custom fields, and values. Fields with `•••` are masked for privacy - click to reveal. Actions [#actions] Row Actions [#row-actions] Each contact row has an actions menu (`•••`) for individual operations. Bulk Operations [#bulk-operations] Select multiple contacts with checkboxes to perform bulk actions: * **Bulk delete** - Delete selected contacts (requires permission) See [Bulk Operations](/docs/people/bulk-operations) for export and import. Export & Import [#export--import] Export [#export] * Formats: CSV, Excel, JSON * Modes: Selected contacts, all contacts, or filtered results * Optional field value inclusion Import [#import] Contacts can be imported via [CRM integrations](/docs/settings/integrations/crm) (HubSpot, Salesforce, BigQuery). Direct file import is not currently supported. Jobs [#jobs] The **Jobs** button opens a dedicated page to track all async operations. See [Jobs](/docs/people/jobs) for details. CRM Integrations [#crm-integrations] People integrates with CRM platforms for manual import and export. CRM status appears as a badge per contact in the table. | Integration | Description | | ------------------------------------------------------------ | ------------------------------------------------- | | [**HubSpot**](/docs/settings/integrations/crm#hubspot) | Contact sync with OAuth and field mapping | | [**Salesforce**](/docs/settings/integrations/crm#salesforce) | Contact synchronization with custom field mapping | | [**BigQuery**](/docs/settings/integrations/crm#bigquery) | Export data for analytics and reporting | CRM integrations require the **Pro** plan or higher. Configure them in [Settings → Integrations](/docs/settings/integrations/crm). # Jobs (/docs/people/jobs) The **Jobs** page tracks all async export and sync operations. Access it via the **Jobs** button on the [People](/docs/people) page. File Exports [#file-exports] The **File Exports** tab lists all contact export jobs with a count badge showing active exports. | Column | Description | | ------------ | ------------------------------------------ | | **Status** | Job status (Processing, Completed, Failed) | | **Format** | Export format (CSV, Excel, JSON) | | **Progress** | Progress bar with percentage | | **Records** | Number of exported records | | **Size** | File size of the export | | **Created** | When the job was created | | **Expires** | When the download link expires | | **Actions** | Download or delete the export | Export files are available for download until they expire (typically 1 day). CRM Sync [#crm-sync] The **CRM Sync** tab lists all CRM import and export jobs. | Column | Description | | --------------- | -------------------------------------------- | | **Status** | Job status (Processing, Completed, Failed) | | **Integration** | CRM platform (HubSpot, Salesforce, BigQuery) | | **Type** | Import or Export | | **Progress** | Progress bar with percentage | | **Synced** | Number of successfully synced records | | **Failed** | Number of failed records | Job Details [#job-details] Click a CRM Sync job row to open the detail sidebar: **Summary** | Metric | Description | | ------------- | ----------------------------------- | | **Synced** | Records successfully synced (green) | | **Processed** | Total records processed | | **Failed** | Records that failed to sync (red) | **Record Breakdown** | Metric | Description | | ----------------- | ---------------------------------------- | | **Created** | New records created in the target system | | **Updated** | Existing records updated | | **Skipped** | Records skipped (no changes needed) | | **Total Records** | Total records in the job | **Timestamps** | Field | Description | | ------------- | ----------------------- | | **Created** | When the job was queued | | **Started** | When processing began | | **Completed** | When the job finished | | **Duration** | Total processing time | **Status** - A summary line showing the outcome, e.g. "Completed: 1 synced, 1 created, 0 updated". Use **Delete Job** to remove a completed job from history, or **Clear History** to remove all CRM sync jobs. Actions [#actions] * **Refresh** - Reload the jobs list to check for updates * **Clear History** - Remove all CRM sync job history * **Delete Job** - Remove an individual job # AI Settings (/docs/settings/ai-settings) Configure AI-powered features for your workspace - create assistants, upload knowledge base documents, connect providers, and manage AI actions. Assistants [#assistants] AI Assistants are autonomous agents that handle conversations, answer questions, and escalate to humans when needed. Creating an Assistant [#creating-an-assistant] 1. Go to **Settings → AI** 2. Click **Create Assistant** 3. Select your AI provider (OpenAI or Anthropic) 4. Configure the settings below You can also start from a [pre-built template](/docs/ai/assistants/templates) for your industry - templates provide ready-to-use context and model settings that you can customise. Basic Settings [#basic-settings] | Setting | Description | | --------------- | ------------------------------------------------------------------- | | **Name** | Identifier for the assistant | | **AI Provider** | OpenAI or Anthropic (must be [connected](/docs/ai/providers) first) | | **Model** | Primary AI model for chat responses | Context Configuration [#context-configuration] Define how your assistant behaves: | Setting | Description | | ---------------- | ------------------------------------------------------------ | | **Identity** | Who the assistant is - name, role, company | | **Objective** | What the assistant should accomplish | | **Tone** | Communication style - formal, friendly, professional, casual | | **Language** | Preferred response language | | **Capabilities** | What the assistant can and cannot do | | **Instructions** | Detailed behavioural guidelines | Advanced Settings [#advanced-settings] Fine-tune the model's behaviour: | Setting | Description | Default | | --------------------- | -------------------------------------------- | ------------- | | **Temperature** | Creativity level (0 = precise, 2 = creative) | 0.7 | | **Top-P** | Nucleus sampling parameter | 1.0 | | **Frequency penalty** | Reduce repetition of common phrases | 0 | | **Presence penalty** | Encourage topic diversity | 0 | | **Max tokens** | Maximum response length | Model default | Per-Purpose Models [#per-purpose-models] Configure specialised models for different tasks: | Purpose | Description | | ----------------------- | ----------------------------------------- | | **Chat** | Primary model for conversation replies | | **Image generation** | DALL-E or GPT Image for creating images | | **Text-to-speech** | TTS model for voice generation | | **Audio transcription** | Whisper for speech-to-text | | **Vision** | Multimodal model for understanding images | For a full walkthrough including per-chat control and the AI preferences bar, see [Assistants](/docs/ai/assistants). *** Knowledge Base [#knowledge-base] Upload documents so your assistant can reference them when answering questions. Knowledge base content takes priority over the model's general knowledge. Uploading Documents [#uploading-documents] 1. Select an assistant in **Settings → AI** 2. Open the **Knowledge Base** section 3. Click **Upload** and select files Supported Formats [#supported-formats] | Format | Description | | ------------ | --------------------------- | | **PDF** | Documents, manuals, guides | | **TXT** | Plain text files | | **Markdown** | `.md` files with formatting | Resolution Priority [#resolution-priority] When answering, the assistant checks sources in this order: 1. **Knowledge base** (highest priority) 2. **Conversation context** 3. **Assistant instructions** 4. **General model knowledge** (lowest priority) For managing documents and best practices, see [Knowledge Base](/docs/ai/knowledge-base). *** Providers [#providers] Connect your AI provider API keys to power assistants and AI actions. | Provider | Models | | ------------- | ---------------------------------------------- | | **OpenAI** | GPT-4o, GPT-4.1, o4-mini, DALL-E, Whisper, TTS | | **Anthropic** | Claude Sonnet, Claude Haiku | Go to **Settings → AI → Providers**, click **Add Provider**, and enter your API key. For setup details, see [AI Providers](/docs/ai/providers). *** Actions [#actions] AI Actions are one-click tools available in the inbox editor. They use your connected provider to perform tasks on messages: * **Suggest reply** - draft a response based on conversation context * **Summarise** - condense a conversation into key points * **Translate** - translate messages to another language * **Improve writing** - fix grammar and improve clarity * **Transcribe** - convert audio messages to text * And more For the full list of actions and configuration, see [AI Actions](/docs/ai/actions). # Plan & Billing (/docs/settings/billing) Manage your subscription, view usage, and upgrade your plan. Current Plan [#current-plan] The top of the page shows your active subscription: * **Plan name** and status badge (Active, Trialing, Past Due, etc.) * **Renewal date** and days remaining until the next billing cycle * **Price** - monthly or yearly amount Usage [#usage] Two progress bars show how much of your plan's quota you've used in the current billing period: | Metric | Description | | ----------------- | ----------------------------------------------------------------------------- | | **Operations** | Message sends, flow executions, and other platform operations | | **AI Operations** | AI assistant responses, transcriptions, image analysis, and other AI features | Usage counters reset automatically at the start of each new billing period. Actions [#actions] * **Cancel Subscription** - cancels at the end of the current billing period. You keep access until the period ends, then revert to the **Free** plan. * **Change Plan** - upgrade or downgrade your subscription (see below). *** Changing Plans [#changing-plans] Upgrade (to a higher plan) [#upgrade-to-a-higher-plan] When you upgrade (e.g. Free → Standard, Standard → Pro): * Takes effect **immediately** * New limits apply right away * You are charged a **prorated** amount for the remainder of the current billing period * No cooldown - you can upgrade at any time Downgrade (to a lower plan) [#downgrade-to-a-lower-plan] When you downgrade (e.g. Pro → Standard, Enterprise → Free): * **Scheduled** for the end of your current billing period * You keep your current plan and limits until the period ends * **30-day cooldown** between downgrades to prevent abuse * No refund for the remaining period Upgrades are always available instantly. Downgrades are scheduled - your current plan stays active until the billing period ends. *** Billing Intervals [#billing-intervals] | Interval | How it works | | ----------- | ------------------------------------------------------------------- | | **Monthly** | Charged once per month on the same date | | **Yearly** | Charged once per year (full amount upfront) with a **15% discount** | Yearly billing is a single upfront payment, not 12 monthly instalments. *** Promotion Code [#promotion-code] Enter a promotion code on the right side of the page and click **Apply** to receive a discount on your subscription. *** Payment Methods [#payment-methods] Manage the payment methods used for your subscription. Click **Add payment method** to add a credit or debit card. At least one valid payment method is required for any paid plan. *** Invoices [#invoices] View and download all your billing invoices. Each invoice shows: | Column | Description | | ---------------- | ---------------------------------------------- | | **Invoice** | Invoice reference number | | **Billing date** | Date the invoice was issued | | **Amount** | Total charged | | **Status** | Payment status (Paid, Open, etc.) | | **Plan** | Plan tier at the time of billing | | **Actions** | **View** the invoice or **download** it as PDF | *** Payment Failures & Account Status [#payment-failures--account-status] If a payment fails, the system follows an escalation process to give you time to fix it: Organisation Status [#organisation-status] | Status | What happens | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Active** | Everything works normally | | **Past Due** | Payment failed - you have a **7-day grace period**. Flows and outbound messages are blocked, but you can still access settings and the billing page to update your payment method | | **Suspended** | Grace period expired - everything is blocked except the billing page | Escalation Timeline [#escalation-timeline] | Day | Action | | ----------- | ----------------------------------------------------------------- | | **Day 0** | Payment fails. Status changes to **Past Due**. Warning email sent | | **Day 1–4** | Grace period - no additional emails | | **Day 5** | Urgent reminder email (2 days remaining) | | **Day 6** | Final warning email (1 day remaining) | | **Day 7+** | Account **suspended**. All features blocked except billing page | Recovery [#recovery] Once you update your payment method and the payment succeeds: * Account status returns to **Active** immediately * All features are restored * Usage counters reset if a new billing period has started If your account is suspended, you can still access the billing page to update your payment method and restore access. *** Cancellation [#cancellation] When you cancel your subscription: 1. Your current plan remains active until the billing period ends 2. After the period ends, you revert to the **Free** plan 3. Usage limits reset to Free tier levels 4. You can re-subscribe at any time *** Related [#related] # Database (/docs/settings/database) The Database page shows the current state of your organisation's data infrastructure and lets you connect dedicated MongoDB and Redis instances. Dedicated database infrastructure requires the **Pro** or **Enterprise** plan. Only workspace **Owners** can configure database settings. Overview [#overview] By default, every organisation runs on Wexio's shared platform infrastructure. The Database page displays your current status: | Infrastructure | Default | Description | | -------------------- | ------- | ------------------------------------------------------------------- | | **MongoDB Database** | Shared | Stores all your chats, messages, contacts, flows, and configuration | | **Redis Cache** | Shared | Handles caching, sessions, and real-time features | You can see the total **document count** for your organisation and the connection status of each service. *** Dedicated Database Infrastructure [#dedicated-database-infrastructure] Isolate your organisation's data onto dedicated MongoDB and Redis instances for enhanced performance, security, and compliance. Benefits [#benefits] | Benefit | Description | | --------------------------- | -------------------------------------------------------------------------------------- | | **Complete Data Isolation** | Your data lives in its own database instance, fully separated from other organisations | | **Dedicated Performance** | No noisy neighbours - get consistent, predictable query performance and throughput | | **Custom Regions** | Choose where your data lives. Deploy to any supported cloud region for compliance | | **Enhanced Security** | Bring your own encryption keys, network policies, and access controls | *** Connecting a Dedicated Database [#connecting-a-dedicated-database] 1. Go to **Settings → Database**. 2. Click **Connect**. 3. Choose a **migration mode** (see below). 4. Enter your **MongoDB connection URI**. 5. Optionally enter a **Redis connection URL**. 6. Choose what to do with data in the source database after migration. 7. Click **Connect** to start the migration. Migration Modes [#migration-modes] When moving to a dedicated database, choose how to handle your existing data: | Mode | What's Copied | Best For | | ----------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------- | | **Migrate** | All data - chats, messages, contacts, flows, media, and configuration | Existing organisations with data to preserve | | **Fresh** | System configuration only - labels, custom fields, collections, AI settings, bots, integrations | New setups or starting clean | Source Data Action [#source-data-action] After migration completes, choose what happens to your data in the original (shared) database: | Action | Description | | ---------- | ------------------------------------------------------------------------------------------------------------- | | **Delete** | Remove your organisation's data from the shared database after migration (recommended - prevents duplication) | | **Keep** | Leave a copy in the shared database as a backup | Migration duration depends on your data volume. During migration, your workspace remains operational but may experience brief interruptions. *** Connection Requirements [#connection-requirements] Both MongoDB and Redis can be configured independently. You only need to provide the connection URIs. Go to **Database → Connect → Drivers** in your Atlas dashboard and copy the connection string. Replace the password placeholder with your database user's password. ``` mongodb+srv://user:pass@cluster0.xxxxx.mongodb.net/dbname ``` See [Atlas Connection Docs](https://www.mongodb.com/docs/atlas/driver-connection/) for details. Use a standard MongoDB connection string. Ensure the server is accessible from Wexio's platform and has authentication enabled. ``` mongodb://user:pass@host:27017/dbname?authSource=admin ``` Copy the `redis://` endpoint from your Upstash console. Upstash provides serverless Redis with global replication. ``` redis://default:xxxxx@us1-xxxxx.upstash.io:6379 ``` See [Upstash Getting Started](https://upstash.com/docs/redis/overall/getstarted) for details. Use the primary endpoint from your cluster. Ensure the security group allows inbound access from Wexio's platform IP addresses. ``` redis://user:pass@cluster.xxxxx.cache.amazonaws.com:6379 ``` Connection URIs are **encrypted at rest** and never displayed after submission. Ensure your database allows connections from Wexio's platform IP addresses. *** Connection Status [#connection-status] After connecting, the Database page shows the health of each service: | Status | Meaning | | ---------------- | ----------------------------------------------------------------------------------------------- | | **Shared** | Organisation uses the shared platform infrastructure (no dedicated instance) | | **Connected** | Dedicated instance is configured and healthy (latency displayed in ms) | | **Disconnected** | Dedicated instance is configured but unreachable - check your connection URI and network access | Use the **Refresh** button to re-check connection health and update document counts at any time. *** Data Collections [#data-collections] The Database page shows a breakdown of your data across MongoDB collections, split into two categories: System Collections [#system-collections] Configuration data that defines your workspace setup: | Collection | Description | | ------------------------ | -------------------------------- | | Labels | Organisation labels | | Chat collections | Inbox collections | | People field definitions | Custom contact field definitions | | AI assistants | AI assistant configuration | | Bots | Messaging bot configuration | | Integrations | Channel integrations | | WhatsApp templates | WhatsApp template configuration | Operational Collections [#operational-collections] Day-to-day data generated through usage: | Collection | Description | | ---------- | -------------------- | | Chats | Conversations | | Messages | Chat messages | | People | Contacts | | Flows | Automation flows | | Cards | Flow cards | | Media | Uploaded media files | Each collection shows its **document count** and **accessibility status**. The total document count is displayed at the top. *** Migrating Back to Shared [#migrating-back-to-shared] If you need to revert to the shared platform database: 1. Go to **Settings → Database**. 2. Click **Migrate Back to Shared**. 3. Choose the **source data action** - whether to keep or delete your data from the dedicated database after migration. 4. Confirm the action. If you choose **Delete**, your data will be removed from the dedicated database after migration. This cannot be undone. Your organisation's data will be copied back to the shared platform and the dedicated connection will be removed. *** Related [#related] # General Settings (/docs/settings/general) Manage your general organisation settings here.