[{"data":1,"prerenderedAt":1786},["ShallowReactive",2],{"search":3,"content-api\u002Finbound-channels":442,"surround-\u002Fapi\u002Finbound-channels":1781},[4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,140,144,148,152,156,160,164,168,172,176,180,184,188,192,196,200,204,208,212,216,220,224,228,232,236,240,244,248,252,256,260,264,268,272,276,280,284,288,292,296,300,304,308,312,316,320,323,327,331,335,339,343,347,351,355,359,363,367,371,375,379,383,387,391,395,399,403,407,411,415,419,423,426,430,434,438],{"path":5,"title":6,"description":7},"\u002Fguide","Guide","Product guides for Owlat — a modular, self-hosted email platform. Learn how to send campaigns, run a personal mailbox, manage a team inbox, and more.",{"path":9,"title":10,"description":11},"\u002Fguide\u002Fgetting-started","Welcome to Owlat","Set up your Owlat workspace and send your first email — from deploying the stack to verifying a domain, building your audience, and launching a campaign.",{"path":13,"title":14,"description":15},"\u002Fguide\u002Fcontact-properties","Contact Properties","Custom fields that extend built-in contact data with your own values for segmentation.",{"path":17,"title":18,"description":19},"\u002Fguide\u002Ftopics","Topics","Topics are explicit audience groups you manage by hand — ideal for opt-in subscribers, imported cohorts, and organized contact buckets you target with campaigns.",{"path":21,"title":22,"description":23},"\u002Fguide\u002Fsegments","Segments","Build dynamic, rule-based contact groups from properties, email activity, and topic membership, re-evaluated from current data each time they're used.",{"path":25,"title":26,"description":27},"\u002Fguide\u002Fforms","Forms","Form Endpoints collect new contacts from your website or landing pages by exposing a public endpoint that accepts submissions and feeds them into a topic.",{"path":29,"title":30,"description":31},"\u002Fguide\u002Fcampaigns","Campaigns & Reporting","Build and send marketing campaigns to a topic or segment with the five-step wizard, optional A\u002FB testing, and full delivery reporting.",{"path":33,"title":34,"description":35},"\u002Fguide\u002Fab-testing","A\u002FB Testing","Compare two variants of a campaign on a test group, then automatically or manually send the winning version to the rest of your audience.",{"path":37,"title":38,"description":39},"\u002Fguide\u002Fautomations","Automations","Send emails automatically based on triggers, delays, and conditions — build welcome series, trial flows, and follow-ups once and let Owlat run them.",{"path":41,"title":42,"description":43},"\u002Fguide\u002Ftransactional","Transactional Emails","One-to-one emails your application triggers in response to a user action — password resets, order confirmations, welcome emails, and similar notifications.",{"path":45,"title":46,"description":47},"\u002Fguide\u002Fcreate-campaign","Create a Campaign","Walk through Owlat's five-step campaign wizard: Basics, Audience, Content, A\u002FB Test, and Review & Send.",{"path":49,"title":50,"description":51},"\u002Fguide\u002Fsend-campaign","Send & Monitor a Campaign","How to send your campaign and track its performance with real-time metrics.",{"path":53,"title":54,"description":55},"\u002Fguide\u002Fquick-start","Quick Start","The fastest path from a blank Owlat workspace to a live email campaign, from your first template through sending and reviewing results.",{"path":57,"title":58,"description":59},"\u002Fguide\u002Ftransactional-setup","Transactional Email Setup","Set up and send transactional emails like password resets and order confirmations via the Owlat API and SDKs.",{"path":61,"title":62,"description":63},"\u002Fguide\u002Fdeliverability","Deliverability","Verify sending domains, manage your blocklist, monitor sending reputation, and stay compliant so your emails reach the inbox.",{"path":65,"title":66,"description":67},"\u002Fguide\u002Fapi-keys-webhooks","API Keys & Webhooks","Create API keys for programmatic access and set up outbound webhooks to receive real-time notifications for email and contact events.",{"path":69,"title":70,"description":71},"\u002Fguide\u002Ffeature-flags","Feature flags","Owlat is modular — every feature listed in this guide can be turned on or off. This page is the user-facing overview of how to do it.",{"path":73,"title":74,"description":75},"\u002Fguide\u002Fteam-permissions","Team & Permissions","Use role-based access to control what each member of your organization can do, with Owner, Admin, and Editor roles.",{"path":77,"title":78,"description":79},"\u002Fguide\u002Faudit-logs","Audit Logs","A chronological record of significant actions in your Owlat organization, so you can see who did what and when.",{"path":81,"title":82,"description":83},"\u002Fguide\u002Fshare-links","Share Links","Create temporary preview links to share email designs with stakeholders who don't have dashboard access.",{"path":85,"title":86,"description":87},"\u002Fguide\u002Fpostbox","Postbox — Personal Email","Per-user mailboxes with a webmail interface and native IMAP\u002FSMTP support. Run your own Gmail-equivalent personal mailbox on your Owlat instance.",{"path":89,"title":90,"description":91},"\u002Fguide\u002Fmigrate-from-google","Migrate from Google","Import your full Gmail history into Owlat over IMAP, and let your AI assistant learn from every imported conversation.",{"path":93,"title":94,"description":95},"\u002Fguide\u002Fteam-inbox","Team Inbox","Triage inbound email as a team: read AI-classified threads, approve, edit or reject agent drafts, work the review queue, and manage quarantine.",{"path":97,"title":98,"description":99},"\u002Fguide\u002Femail-editor","Email Editor","A block-based visual editor for building responsive emails that render consistently across desktop, mobile, Outlook, Gmail, and Apple Mail.",{"path":101,"title":102,"description":103},"\u002Fguide\u002Fai-agent","AI Agent & Autonomy","Configure the AI agent that classifies and drafts replies to inbound mail: auto-reply settings, the health dashboard, circuit breakers, autonomy rules, and the knowledge backfill.",{"path":105,"title":106,"description":107},"\u002Fguide\u002Fknowledge-graph","Knowledge Graph","Browse, search, and manage Owlat's typed organizational knowledge — the 7 entry types, source attribution, confidence decay, relations, and how entries are extracted from mail.",{"path":109,"title":110,"description":111},"\u002Fguide\u002Ffiles","Files","Upload, browse, search, tag, and version documents in the file library.",{"path":113,"title":114,"description":115},"\u002Fguide\u002Fchat","Team Chat","Use Owlat's built-in team chat: public and private channels, direct messages, mentions, attachments, and channels linked to an inbox conversation.",{"path":117,"title":118,"description":119},"\u002Fguide\u002Fcode-tasks","Code Tasks","Queue coding-agent tasks, watch them move from queued through review, and run the code-worker sidecar that opens the pull requests.",{"path":121,"title":122,"description":123},"\u002Fguide\u002Faudience-data","Audience Data: Identities, Relationships & Timeline","Unify a contact across email, phone, and messaging channels, merge duplicates, map relationships, and read the cross-channel interaction timeline.",{"path":125,"title":126,"description":127},"\u002Fguide\u002Fimporting-contacts","Importing & Exporting Contacts","Bring contacts into Owlat from a CSV or from Mailchimp and Stripe, export them back out, and run bulk operations on your audience.",{"path":129,"title":130,"description":131},"\u002Fguide\u002Faccount","Your Account & Data","Export your data as JSON or CSV, request account deletion with a 30-day grace period, and use the onboarding checklist and the public preference center.",{"path":133,"title":134,"description":135},"\u002Fguide\u002Fchannels","Communication Channels","Configure SMS, WhatsApp, and generic-webhook channels, monitor channel health, and understand which channels are fully live today.",{"path":137,"title":138,"description":139},"\u002Fguide\u002Fdesktop-app","Desktop App","Install the Owlat desktop app, connect one or more workspaces, switch between them, and use native notifications, tray badges, shortcuts, and deep links.",{"path":141,"title":142,"description":143},"\u002Fguide\u002Femail-templates","Email Templates","Reusable email designs that define the structure, content, and personalization of every campaign and transactional message you send in Owlat.",{"path":145,"title":146,"description":147},"\u002Fguide\u002Fai-assistant","AI Assistant","Owlat's multi-turn, streaming, tool-calling AI assistant — a private chat surface that can search your workspace and draft copy, plus @assistant replies inside team chat.",{"path":149,"title":150,"description":151},"\u002Fguide\u002Fsecurity-scanning","Sending Security & Scanning","Owlat's security scanning: a content check for spam and phishing, an attachment scan for malware, and a Google Safe Browsing URL check. Suspicious content goes to a review queue.",{"path":153,"title":154,"description":155},"\u002Fguide\u002Fsystem-updates","System & Updates","The owner-only System & Updates screen: your current Owlat version, container health, LLM spend, and the in-app one-click updater with history.",{"path":157,"title":158,"description":159},"\u002Fguide\u002Foperating-modes","Operating Modes","The different ways to run Owlat at a company — read external mailboxes over IMAP, send transactional or marketing email through a delivery provider, host your own mail server, or run a team inbox with AI — and the rules that keep each combination coherent.",{"path":161,"title":162,"description":163},"\u002Fguide\u002Fsaved-blocks","Saved Blocks","Create reusable, linked content blocks you can drop into any email — edit one and every email that uses it updates automatically.",{"path":165,"title":166,"description":167},"\u002Fguide\u002Fmedia-library","Media Library","Manage, organize, search, and reuse images and files across your emails from one centralized hub.",{"path":169,"title":170,"description":171},"\u002Fguide\u002Femail-theme","Email Theme","Set your organization's default colors, font, and email width so every new template starts from a consistent baseline.",{"path":173,"title":174,"description":175},"\u002Fguide\u002Ftranslations","Translations","Send one email in multiple languages: add per-language translations to a single template and Owlat picks the right version for each recipient.",{"path":177,"title":178,"description":179},"\u002Fguide\u002Fcontacts","Contacts","How to add, view, organize, and manage contacts in Owlat, including sources, the contact detail tabs, and subscription compliance.",{"path":181,"title":182,"description":183},"\u002Fapi","API Overview","Owlat exposes authenticated API endpoints under your Convex site URL.",{"path":185,"title":186,"description":187},"\u002Fapi\u002Fwebhooks","Webhooks","Owlat supports both outbound customer webhooks and inbound provider webhooks.",{"path":189,"title":190,"description":191},"\u002Fapi\u002Fpublic-endpoints","Public Endpoints","These routes are public-facing and usually accessed from email links or embedded forms.",{"path":193,"title":194,"description":195},"\u002Fapi\u002Fwebhook-payloads","Webhook Payloads","The authoritative wire contract for outbound webhooks: envelope, signature headers, per-event data shapes, and payload versioning.",{"path":197,"title":198,"description":199},"\u002Fapi\u002Finbound-channels","Inbound Channel Webhooks","Provider webhook reference for inbound SMS, WhatsApp, and generic-channel messages, plus the MTA mailbox and credential callbacks.",{"path":201,"title":202,"description":203},"\u002Fapi\u002Fauthentication","Authentication","Secure API access with organization-scoped API keys.",{"path":205,"title":206,"description":207},"\u002Fapi\u002Fsdk","TypeScript SDK","Typed client for the Owlat API, usable from Node.js, Bun, Deno, or any server-side JavaScript runtime.",{"path":209,"title":210,"description":211},"\u002Fapi\u002Fsdk-java","Java SDK","The official `owlat-sdk` package provides a typed client for interacting with the Owlat API from any JVM application. Requires Java 11+.",{"path":213,"title":214,"description":215},"\u002Fapi\u002Fcontacts","Contacts API","Manage contacts for your organization.",{"path":217,"title":218,"description":219},"\u002Fapi\u002Ftopics","Topics API","Manage topic membership through authenticated endpoints.",{"path":221,"title":222,"description":223},"\u002Fapi\u002Fevents","Events API","Send contact events to drive segmentation and automation triggers.",{"path":225,"title":226,"description":227},"\u002Fapi\u002Ftransactional","Transactional API","Send published transactional templates to a recipient.",{"path":229,"title":230,"description":231},"\u002Fapi\u002Fforms","Forms API","Capture subscribers through public form endpoints.",{"path":233,"title":234,"description":235},"\u002Fdeveloper","Developer Guide","Technical architecture, feature-flag model, and provider abstractions used by Owlat.",{"path":237,"title":238,"description":239},"\u002Fdeveloper\u002Fmta-system","MTA System","Owlat's custom Mail Transfer Agent for direct SMTP delivery with intelligent rate limiting, bounce processing, and IP warming.",{"path":241,"title":242,"description":243},"\u002Fdeveloper\u002Ffeature-flags","Feature flags — developer reference","How the Owlat feature flag system works: single source of truth, dependency resolution, docker profile mapping, and how to add a new flag.",{"path":245,"title":246,"description":247},"\u002Fdeveloper\u002Fhow-email-works","How Email Works","A technical deep-dive into how email actually works — from SMTP and DNS to authentication, deliverability, and the differences between marketing and private email.",{"path":249,"title":250,"description":251},"\u002Fdeveloper\u002Femail-security","Email Security","Content scanning, attachment validation, URL reputation checking, and malware detection for outbound emails.",{"path":253,"title":254,"description":255},"\u002Fdeveloper\u002Fpostbox-architecture","Postbox Architecture","How the Postbox personal-mail feature is wired — schema, IMAP server, app-password auth, outbound relay, inbound delivery, and external mailboxes.",{"path":257,"title":258,"description":259},"\u002Fdeveloper\u002Fproviders","Providers","Pluggable provider abstractions for LLM, email sending, notifications, vector stores, and analytics, selected per-deployment so self-hosters can swap implementations without code changes.",{"path":261,"title":262,"description":263},"\u002Fdeveloper\u002Fcampaign-internals","Campaign Internals","How the campaign backend works: two status machines, send pre-flight, the send orchestrator, emailSends records, and the priority workpools.",{"path":265,"title":266,"description":267},"\u002Fdeveloper\u002Faudience-internals","Audience Internals","Backend reference for contact resolution, the double opt-in lifecycle, topic subscription, the conditions registry, and segment evaluation.",{"path":269,"title":270,"description":271},"\u002Fdeveloper\u002Fautomation-internals","Automation Internals","How the automation run engine works: the step walker, the lifecycle state machine, trigger fanout, the three step types, and the resilience cron.",{"path":273,"title":274,"description":275},"\u002Fdeveloper\u002Fdeliverability-infrastructure","Deliverability Infrastructure","The Convex-side deliverability backend: provider routing, health-aware failover, sending reputation with auto-enforcement, IP warming cache, the blocklist, and the content-scan gate.",{"path":277,"title":278,"description":279},"\u002Fdeveloper\u002Farchitecture","Architecture Overview","Owlat follows a modern serverless architecture with real-time capabilities.",{"path":281,"title":282,"description":283},"\u002Fdeveloper\u002Fplatform-operations","Platform Operations","Operator reference for abuse status and the sending gate, the platform-admin roster, content review, org deletion, in-app self-update, dev endpoints, crons, and migrations.",{"path":285,"title":286,"description":287},"\u002Fdeveloper\u002Fscopes","Scopes","What each app and package in the Owlat monorepo is responsible for.",{"path":289,"title":290,"description":291},"\u002Fdeveloper\u002Fself-hosting","Self-Hosting","Deploy Owlat on your own infrastructure with Docker Compose. Complete guide from first boot to production.",{"path":293,"title":294,"description":295},"\u002Fdeveloper\u002Fself-hosting-config","Self-Hosting Configuration","Complete reference for Docker environment variables, Convex backend variables, service topology, and volume persistence.",{"path":297,"title":298,"description":299},"\u002Fdeveloper\u002Fself-hosting-dns-email","DNS & Email Setup","Configure DNS records, DKIM signing, SPF, DMARC, and bounce handling for reliable email delivery.",{"path":301,"title":302,"description":303},"\u002Fdeveloper\u002Fself-hosting-production","Production Deployment","Secure your self-hosted Owlat instance with TLS, firewall rules, backups, and monitoring.",{"path":305,"title":306,"description":307},"\u002Fdeveloper\u002Fself-hosting-maintenance","Maintenance & Updates","Keep your self-hosted Owlat instance up to date, manage backups, scale performance, and troubleshoot common issues.",{"path":309,"title":310,"description":311},"\u002Fdeveloper\u002Fself-hosting-desktop","Desktop Installer","Install Owlat on a bare Linux VPS straight from the desktop app over SSH — no terminal — with a live, animated provisioning timeline.",{"path":313,"title":314,"description":315},"\u002Fdeveloper\u002Fsetup-cli","Setup CLI & Installer","Operator reference for the Owlat self-host tooling: the install.sh one-liner, the owlat-setup CLI, the convex-deploy flow, and admin bootstrap.",{"path":317,"title":318,"description":319},"\u002Fdeveloper\u002Fconvex","Convex Backend","Owlat uses Convex as its serverless backend, providing real-time subscriptions, ACID transactions, and TypeScript-first development.",{"path":321,"title":202,"description":322},"\u002Fdeveloper\u002Fauthentication","Owlat uses BetterAuth with the Convex adapter for authentication and organization (team) management.",{"path":324,"title":325,"description":326},"\u002Fdeveloper\u002Femail-system","Email System","Owlat's email system consists of a visual editor, template management, and multi-provider sending infrastructure.",{"path":328,"title":329,"description":330},"\u002Fdeveloper\u002Femail-renderer","Email Renderer","The @owlat\u002Femail-renderer package converts editor JSON blocks into production-ready HTML emails with cross-client compatibility, CSS inlining, dark mode, and Outlook VML fallbacks.",{"path":332,"title":333,"description":334},"\u002Fdeveloper\u002Fenvironment-variables","Environment Variables","Reference for every environment variable Owlat reads across the Convex backend, web app, MTA, IMAP server, and mail-sync worker.",{"path":336,"title":337,"description":338},"\u002Fdeveloper\u002Fcomponents","Component Library","Reference for the reusable, auto-imported Vue UI components shipped in the packages\u002Fui layer.",{"path":340,"title":341,"description":342},"\u002Fdeveloper\u002Fdecisions","Architectural Decision Records","The architectural decision records for the Owlat project, each capturing the context, the decision, and the trade-offs involved.",{"path":344,"title":345,"description":346},"\u002Fdeveloper\u002Fdecisions\u002F009-model-routing","ADR-009: Task-Based Model Routing","Why Owlat supports per-task LLM model selection instead of using a single model for all pipeline steps.",{"path":348,"title":349,"description":350},"\u002Fdeveloper\u002Fdecisions\u002F010-listing-engine","ADR-010: Listing Engine","Why Owlat replaced four incompatible list-query contracts with one generic listing engine driven by per-entity descriptors.",{"path":352,"title":353,"description":354},"\u002Fdeveloper\u002Fdecisions\u002F001-custom-email-renderer","ADR-001: Custom Email Renderer Over MJML","Why Owlat built a custom table-based HTML email renderer instead of using MJML, gaining full control over VML, dark mode, and per-client rendering.",{"path":356,"title":357,"description":358},"\u002Fdeveloper\u002Fdecisions\u002F002-convex-backend","ADR-002: Convex as Backend","Why Owlat chose Convex over PostgreSQL and Firebase for real-time reactivity, co-located TypeScript logic, and zero-config scaling.",{"path":360,"title":361,"description":362},"\u002Fdeveloper\u002Fdecisions\u002F003-notion-like-builder","ADR-003: Notion-like Email Builder","Why Owlat replaced the traditional 3-panel email editor with a Notion-like single-column canvas for inline WYSIWYG editing.",{"path":364,"title":365,"description":366},"\u002Fdeveloper\u002Fdecisions\u002F004-monorepo-bun-workspaces","ADR-004: Monorepo with Bun Workspaces","Why Owlat uses a monorepo with Bun workspaces and Turborepo for fast installs, atomic cross-package changes, and cached CI.",{"path":368,"title":369,"description":370},"\u002Fdeveloper\u002Fdecisions\u002F005-custom-mta","ADR-005: Custom MTA","Why Owlat built a custom Mail Transfer Agent instead of relying solely on third-party email providers.",{"path":372,"title":373,"description":374},"\u002Fdeveloper\u002Fdecisions\u002F006-self-hosted-convex","ADR-006: Self-Hosted Convex","Why Owlat uses the open-source Convex backend for self-hosting instead of migrating to a different database.",{"path":376,"title":377,"description":378},"\u002Fdeveloper\u002Fdecisions\u002F007-pluggable-llm","ADR-007: Pluggable LLM Provider","Why Owlat uses the Vercel AI SDK with a provider abstraction layer instead of hardcoding a single LLM vendor.",{"path":380,"title":381,"description":382},"\u002Fdeveloper\u002Fdecisions\u002F008-process-architecture","ADR-008: Agent Process Architecture","Why Owlat processes inbound messages with a self-scheduling step walker plus a lifecycle coordinator instead of one sequential function.",{"path":384,"title":385,"description":386},"\u002Fexamples","Examples","Copy-pasteable integration patterns for common Owlat use cases.",{"path":388,"title":389,"description":390},"\u002Fexamples\u002Fwelcome-email","Welcome Email","Send a personalized welcome email when a new user signs up.",{"path":392,"title":393,"description":394},"\u002Fexamples\u002Fbilling-email","Billing Email","Send a billing receipt with an invoice PDF attached after a successful payment.",{"path":396,"title":397,"description":398},"\u002Fexamples\u002Fevent-automation","Event Automation","Trigger automations with custom events for trial lifecycle, feature adoption, and more.",{"path":400,"title":401,"description":402},"\u002Fexamples\u002Fcontact-sync","Contact Sync","Sync contacts from your database to Owlat using upsert patterns and bulk operations.",{"path":404,"title":405,"description":406},"\u002Fexamples\u002Fwebhook-handler","Webhook Handler","Handle Owlat delivery webhooks with signature verification and event routing.",{"path":408,"title":409,"description":410},"\u002Fexamples\u002Fmultilingual-email","Multilingual Email","Send emails in the recipient's preferred language using template translations.",{"path":412,"title":413,"description":414},"\u002Fvision","Vision","Where Owlat is heading — from email platform to unified communication intelligence powered by AI agents.",{"path":416,"title":417,"description":418},"\u002Fvision\u002Fself-hosting","Self-Hosting Architecture","How Owlat runs as a fully self-hosted stack using Docker Compose — open-source Convex backend, custom MTA, and a pluggable LLM provider.",{"path":420,"title":421,"description":422},"\u002Fvision\u002Fagent-pipeline","Agent Pipeline","Technical architecture for the inbound email agent pipeline — step modules, the walker, security scanning, threading, and human review.",{"path":424,"title":106,"description":425},"\u002Fvision\u002Fknowledge-graph","Technical architecture for Owlat's typed knowledge storage — how organizational knowledge is stored, searched, decayed, and maintained.",{"path":427,"title":428,"description":429},"\u002Fvision\u002Fmulti-channel","Multi-Channel & CRM","Technical architecture for channel adapters, unified messaging, contact identity unification, and the CRM hub.",{"path":431,"title":432,"description":433},"\u002Fvision\u002Ffile-system","Semantic File System","Technical architecture for Owlat's semantic file storage — version tracking with provenance today, plus the planned embedding-based retrieval and auto-tagging layer.",{"path":435,"title":436,"description":437},"\u002Fvision\u002Fdesktop-app","Desktop App & Advanced Agents","Architecture of the Owlat desktop shell, visualization agent, adaptive dashboard, agent health, graduated autonomy, and coding agents.",{"path":439,"title":440,"description":441},"\u002Fvision\u002Froadmap","Roadmap","What's planned next for Owlat — the documented-but-unbuilt pieces still being wired, and the enhancements on our radar.",{"id":443,"title":198,"body":444,"description":199,"extension":1776,"meta":1777,"navigation":840,"path":197,"seo":1778,"stem":1779,"__hash__":1780},"content\u002F2.api\u002F13.inbound-channels.md",{"type":445,"value":446,"toc":1761},"minimark",[447,456,478,483,494,497,585,607,617,621,624,707,719,722,795,806,848,851,862,869,873,883,888,958,969,976,1045,1055,1059,1076,1115,1135,1141,1145,1148,1219,1222,1334,1354,1432,1436,1450,1548,1551,1637,1647,1651,1679,1683,1700,1704,1726,1746,1750,1754,1757],[448,449,450,451,455],"p",{},"These are the server-to-server webhook endpoints that feed non-email customer messages into Owlat. Each one accepts an inbound payload from an external provider (Twilio, Meta, or your own system), verifies it cryptographically, and turns it into a unified-inbox message. They are not part of the authenticated ",[452,453,454],"code",{},"\u002Fapi\u002Fv1\u002F*"," surface and are not called from a browser — you configure them in the provider's dashboard and they post directly to your deployment.",[457,458,461],"callout",{"title":459,"type":460},"Inbound only","info",[448,462,463,464,468,469,473,474,477],{},"These endpoints ingest ",[465,466,467],"strong",{},"incoming"," messages. Outbound SMS\u002FWhatsApp\u002Fgeneric sending ",[470,471,472],"em",{},"is"," wired end-to-end (real Twilio\u002FMeta\u002Fwebhook adapters), but it runs only through the AI-agent reply path or a manual send from a contact's Unified Timeline — there is no per-message reply button in the Team Inbox. See ",[475,476,134],"a",{"href":133}," for what each channel can do today. This page covers the receiving side.",[479,480,482],"h2",{"id":481},"overview-how-inbound-channels-feed-the-unified-inbox","Overview: how inbound channels feed the unified inbox",[448,484,485,486,489,490,493],{},"All three channel webhooks share one pipeline (",[452,487,488],{},"apps\u002Fapi\u002Fconvex\u002Fwebhooks\u002Fpipeline.ts","). The shell is identical for every provider; only signature verification and payload parsing differ, and those live in per-provider adapters under ",[452,491,492],{},"apps\u002Fapi\u002Fconvex\u002Fwebhooks\u002Fadapters\u002F",".",[448,495,496],{},"The pipeline runs these steps in order:",[498,499,500,513,531,541,547,564],"ol",{},[501,502,503,509,510,493],"li",{},[465,504,505,506],{},"Reject non-",[452,507,508],{},"POST"," with ",[452,511,512],{},"405",[501,514,515,518,519,522,523,526,527,530],{},[465,516,517],{},"Rate-limit by client IP"," (",[452,520,521],{},"webhookIngestion",": token bucket, 50\u002Fsec, burst capacity 100). Over the limit returns ",[452,524,525],{},"429"," with a ",[452,528,529],{},"Retry-After"," header.",[501,532,533,536,537,493],{},[465,534,535],{},"Verify the signature"," via the provider adapter. Fail-closed — see ",[475,538,540],{"href":539},"#required-env-secrets-and-fail-closed-behavior","Required env secrets",[501,542,543,546],{},[465,544,545],{},"Audit-store the raw payload"," (best-effort; never fails the request).",[501,548,549,552,553,556,557,560,561,493],{},[465,550,551],{},"Parse the payload"," into a normalized ",[452,554,555],{},"channel.received"," event. A parse error returns ",[452,558,559],{},"400","; a payload that carries no actionable message (e.g. a Meta status update) is acknowledged with ",[452,562,563],{},"200 { \"success\": true, \"ignored\": true }",[501,565,566,569,570,573,574,576,577,580,581,584],{},[465,567,568],{},"Dispatch"," the event. The dispatcher (",[452,571,572],{},"apps\u002Fapi\u002Fconvex\u002Fwebhooks\u002Fdispatcher.ts",") routes ",[452,575,555],{}," to the ",[452,578,579],{},"processInboundChannel"," internal mutation, which resolves or creates the contact, finds or reopens the conversation thread, and inserts an inbound ",[452,582,583],{},"unifiedMessages"," row.",[448,586,587,588,591,592,595,596,599,600,603,604,606],{},"Contacts are resolved per channel, not by email: an inbound SMS or WhatsApp message creates a contact keyed on the sender's phone number or handle through ",[452,589,590],{},"contactIdentities",", and these contacts have no ",[452,593,594],{},"email",". Each channel is its own identity keyspace — a ",[452,597,598],{},"generic","-channel ",[452,601,602],{},"john@example.com"," is a different contact from an ",[452,605,594],{},"-channel one. Cross-channel unification is an explicit merge operation, not automatic.",[457,608,610],{"title":609,"type":460},"Base URL",[448,611,612,613,616],{},"All paths below are relative to your deployment's HTTP URL, e.g. ",[452,614,615],{},"https:\u002F\u002F\u003Cyour-deployment>.convex.site\u002Fwebhooks\u002Fsms",". Configure that full URL in the provider's webhook settings.",[479,618,620],{"id":619},"post-webhookssms-twilio","POST \u002Fwebhooks\u002Fsms (Twilio)",[448,622,623],{},"Inbound SMS from Twilio's Programmable Messaging webhook.",[625,626,627,640],"table",{},[628,629,630],"thead",{},[631,632,633,637],"tr",{},[634,635,636],"th",{},"Property",[634,638,639],{},"Value",[641,642,643,653,664,674,682,692],"tbody",{},[631,644,645,649],{},[646,647,648],"td",{},"Method",[646,650,651],{},[452,652,508],{},[631,654,655,658],{},[646,656,657],{},"Content type",[646,659,660,663],{},[452,661,662],{},"application\u002Fx-www-form-urlencoded"," (Twilio's standard)",[631,665,666,669],{},[646,667,668],{},"Signature header",[646,670,671],{},[452,672,673],{},"X-Twilio-Signature",[631,675,676,679],{},[646,677,678],{},"Signature scheme",[646,680,681],{},"HMAC-SHA1, Base64-encoded",[631,683,684,687],{},[646,685,686],{},"Secret env var",[646,688,689],{},[452,690,691],{},"TWILIO_AUTH_TOKEN",[631,693,694,697],{},[646,695,696],{},"Success response",[646,698,699,700,518,703,706],{},"TwiML empty ",[452,701,702],{},"\u003CResponse>\u003C\u002FResponse>",[452,704,705],{},"text\u002Fxml",")",[448,708,709,710,712,713,493],{},"The adapter reconstructs Twilio's canonical validation string — the full request URL followed by every form parameter concatenated in alphabetical order (key immediately followed by value, no separator) — then compares the HMAC-SHA1 against ",[452,711,673],{}," in constant time. This matches ",[475,714,718],{"href":715,"rel":716},"https:\u002F\u002Fwww.twilio.com\u002Fdocs\u002Fusage\u002Fsecurity#validating-requests",[717],"nofollow","Twilio's request-validation spec",[448,720,721],{},"Parsed fields:",[625,723,724,734],{},[628,725,726],{},[631,727,728,731],{},[634,729,730],{},"Form field",[634,732,733],{},"Maps to",[641,735,736,746,756,768,778],{},[631,737,738,743],{},[646,739,740],{},[452,741,742],{},"From",[646,744,745],{},"sender identifier (required)",[631,747,748,753],{},[646,749,750],{},[452,751,752],{},"Body",[646,754,755],{},"message text (required)",[631,757,758,763],{},[646,759,760],{},[452,761,762],{},"MessageSid",[646,764,765],{},[452,766,767],{},"externalMessageId",[631,769,770,775],{},[646,771,772],{},[452,773,774],{},"MediaUrl0",[646,776,777],{},"first media URL (optional)",[631,779,780,792],{},[646,781,782,785,786,785,789],{},[452,783,784],{},"FromCity",", ",[452,787,788],{},"FromState",[452,790,791],{},"FromCountry",[646,793,794],{},"stored as metadata",[448,796,797,798,800,801,803,804,493],{},"If ",[452,799,742],{}," or ",[452,802,752],{}," is missing, parsing fails and the endpoint returns ",[452,805,559],{},[807,808,813],"pre",{"className":809,"code":810,"language":811,"meta":812,"style":812},"language-txt shiki shiki-themes github-light github-dark-dimmed","POST \u002Fwebhooks\u002Fsms\nX-Twilio-Signature: \u003Cbase64 HMAC-SHA1>\nContent-Type: application\u002Fx-www-form-urlencoded\n\nFrom=%2B15551234567&Body=Hello&MessageSid=SM123&FromCity=Berlin\n","txt","",[452,814,815,823,829,835,842],{"__ignoreMap":812},[816,817,820],"span",{"class":818,"line":819},"line",1,[816,821,822],{},"POST \u002Fwebhooks\u002Fsms\n",[816,824,826],{"class":818,"line":825},2,[816,827,828],{},"X-Twilio-Signature: \u003Cbase64 HMAC-SHA1>\n",[816,830,832],{"class":818,"line":831},3,[816,833,834],{},"Content-Type: application\u002Fx-www-form-urlencoded\n",[816,836,838],{"class":818,"line":837},4,[816,839,841],{"emptyLinePlaceholder":840},true,"\n",[816,843,845],{"class":818,"line":844},5,[816,846,847],{},"From=%2B15551234567&Body=Hello&MessageSid=SM123&FromCity=Berlin\n",[448,849,850],{},"On success Twilio receives the empty-Response TwiML envelope (no auto-reply is sent from this endpoint):",[807,852,856],{"className":853,"code":854,"language":855,"meta":812,"style":812},"language-xml shiki shiki-themes github-light github-dark-dimmed","\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\u003CResponse>\u003C\u002FResponse>\n","xml",[452,857,858],{"__ignoreMap":812},[816,859,860],{"class":818,"line":819},[816,861,854],{},[457,863,866],{"title":864,"type":865},"No replay protection","warning",[448,867,868],{},"Twilio does not include a timestamp in its signature, so this layer cannot detect replays. The worst case is a duplicate inbound message, not a forged state transition — acceptable for an inbound channel, but do not reuse this scheme for anything that mutates billing or auth.",[479,870,872],{"id":871},"post-and-get-webhookswhatsapp-meta-verification-challenge","POST and GET \u002Fwebhooks\u002Fwhatsapp (Meta verification challenge)",[448,874,875,876,878,879,882],{},"WhatsApp Business messages arrive from Meta's Cloud API. The same path handles two methods: ",[452,877,508],{}," for inbound messages and ",[452,880,881],{},"GET"," for the one-time subscription verification challenge.",[884,885,887],"h3",{"id":886},"post-inbound-message","POST — inbound message",[625,889,890,898],{},[628,891,892],{},[631,893,894,896],{},[634,895,636],{},[634,897,639],{},[641,899,900,908,917,926,936,945],{},[631,901,902,904],{},[646,903,648],{},[646,905,906],{},[452,907,508],{},[631,909,910,912],{},[646,911,657],{},[646,913,914],{},[452,915,916],{},"application\u002Fjson",[631,918,919,921],{},[646,920,668],{},[646,922,923],{},[452,924,925],{},"X-Hub-Signature-256",[631,927,928,930],{},[646,929,678],{},[646,931,932,933],{},"HMAC-SHA256 over the raw body, hex-encoded, prefixed ",[452,934,935],{},"sha256=",[631,937,938,940],{},[646,939,686],{},[646,941,942],{},[452,943,944],{},"META_APP_SECRET",[631,946,947,949],{},[646,948,696],{},[646,950,951,954,955,706],{},[452,952,953],{},"200 OK"," (plain text body ",[452,956,957],{},"OK",[448,959,960,961,963,964,493],{},"The adapter strips the ",[452,962,935],{}," prefix from the header and compares the hex HMAC-SHA256 of the raw body in constant time, per ",[475,965,968],{"href":966,"rel":967},"https:\u002F\u002Fdevelopers.facebook.com\u002Fdocs\u002Fwhatsapp\u002Fcloud-api\u002Fwebhooks",[717],"Meta's webhook docs",[448,970,971,972,975],{},"It reads the first message from the nested ",[452,973,974],{},"entry[0].changes[0].value.messages[0]"," envelope:",[625,977,978,987],{},[628,979,980],{},[631,981,982,985],{},[634,983,984],{},"Source field",[634,986,733],{},[641,988,989,998,1008,1019,1032],{},[631,990,991,996],{},[646,992,993],{},[452,994,995],{},"messages[0].from",[646,997,745],{},[631,999,1000,1005],{},[646,1001,1002],{},[452,1003,1004],{},"messages[0].text.body",[646,1006,1007],{},"message text",[631,1009,1010,1015],{},[646,1011,1012],{},[452,1013,1014],{},"messages[0].id",[646,1016,1017],{},[452,1018,767],{},[631,1020,1021,1029],{},[646,1022,1023,800,1026],{},[452,1024,1025],{},"messages[0].image.url",[452,1027,1028],{},"document.url",[646,1030,1031],{},"media URL (optional)",[631,1033,1034,1039],{},[646,1035,1036],{},[452,1037,1038],{},"contacts[0].profile.name",[646,1040,1041,1042,706],{},"stored as metadata (",[452,1043,1044],{},"profileName",[448,1046,1047,1048,1051,1052,1054],{},"Meta also posts delivery\u002Fread status updates that contain no ",[452,1049,1050],{},"messages",". Those are acknowledged with ",[452,1053,563],{}," and produce no inbox message.",[884,1056,1058],{"id":1057},"get-verification-challenge","GET — verification challenge",[448,1060,1061,1062,1064,1065,785,1068,1071,1072,1075],{},"When you activate a webhook subscription, Meta sends a ",[452,1063,881],{}," request with ",[452,1066,1067],{},"hub.mode",[452,1069,1070],{},"hub.verify_token",", and ",[452,1073,1074],{},"hub.challenge"," query parameters. The handler runs out-of-band, before the inbound pipeline:",[1077,1078,1079,1088,1109],"ul",{},[501,1080,797,1081,1084,1085,493],{},[452,1082,1083],{},"META_VERIFY_TOKEN"," is unset, it returns ",[452,1086,1087],{},"503",[501,1089,797,1090,1092,1093,1096,1097,1099,1100,1102,1103,1105,1106,493],{},[452,1091,1067],{}," is ",[452,1094,1095],{},"subscribe"," and ",[452,1098,1070],{}," matches ",[452,1101,1083],{}," (constant-time compare), it echoes ",[452,1104,1074],{}," back with ",[452,1107,1108],{},"200",[501,1110,1111,1112,493],{},"Otherwise it returns ",[452,1113,1114],{},"403 Verification failed",[807,1116,1118],{"className":809,"code":1117,"language":811,"meta":812,"style":812},"GET \u002Fwebhooks\u002Fwhatsapp?hub.mode=subscribe&hub.verify_token=\u003Cyour-token>&hub.challenge=12345\n→ 200\n12345\n",[452,1119,1120,1125,1130],{"__ignoreMap":812},[816,1121,1122],{"class":818,"line":819},[816,1123,1124],{},"GET \u002Fwebhooks\u002Fwhatsapp?hub.mode=subscribe&hub.verify_token=\u003Cyour-token>&hub.challenge=12345\n",[816,1126,1127],{"class":818,"line":825},[816,1128,1129],{},"→ 200\n",[816,1131,1132],{"class":818,"line":831},[816,1133,1134],{},"12345\n",[448,1136,1137,1138,1140],{},"Set ",[452,1139,1083],{}," to any string you choose and enter the same value in Meta's webhook configuration as the verify token.",[479,1142,1144],{"id":1143},"post-webhookschannel-generic-shared-secret-envelope","POST \u002Fwebhooks\u002Fchannel (generic shared-secret envelope)",[448,1146,1147],{},"A provider-agnostic endpoint for piping messages from any system that can post JSON with a shared secret. This is the lowest-trust channel — it uses a static shared secret rather than a per-request HMAC, so the pipeline's IP rate limit is your primary abuse control.",[625,1149,1150,1158],{},[628,1151,1152],{},[631,1153,1154,1156],{},[634,1155,636],{},[634,1157,639],{},[641,1159,1160,1168,1176,1193,1201,1210],{},[631,1161,1162,1164],{},[646,1163,648],{},[646,1165,1166],{},[452,1167,508],{},[631,1169,1170,1172],{},[646,1171,657],{},[646,1173,1174],{},[452,1175,916],{},[631,1177,1178,1181],{},[646,1179,1180],{},"Auth header",[646,1182,1183,785,1186,1189,1190],{},[452,1184,1185],{},"X-Webhook-Secret",[465,1187,1188],{},"or"," ",[452,1191,1192],{},"Authorization: Bearer \u003Csecret>",[631,1194,1195,1198],{},[646,1196,1197],{},"Auth scheme",[646,1199,1200],{},"constant-time string equality",[631,1202,1203,1205],{},[646,1204,686],{},[646,1206,1207],{},[452,1208,1209],{},"GENERIC_WEBHOOK_SECRET",[631,1211,1212,1214],{},[646,1213,696],{},[646,1215,1216],{},[452,1217,1218],{},"200 { \"success\": true, \"kind\": \"channel.received\" }",[448,1220,1221],{},"The JSON envelope is forgiving — fields fall back through several aliases:",[625,1223,1224,1236],{},[628,1225,1226],{},[631,1227,1228,1231,1234],{},[634,1229,1230],{},"Envelope field",[634,1232,1233],{},"Aliases \u002F fallback",[634,1235,733],{},[641,1237,1238,1257,1274,1289,1304,1321],{},[631,1239,1240,1245,1254],{},[646,1241,1242],{},[452,1243,1244],{},"from",[646,1246,1247,1250,1251],{},[452,1248,1249],{},"sender",", else literal ",[452,1252,1253],{},"\"webhook\"",[646,1255,1256],{},"sender identifier",[631,1258,1259,1264,1272],{},[646,1260,1261],{},[452,1262,1263],{},"text",[646,1265,1266,785,1269],{},[452,1267,1268],{},"message",[452,1270,1271],{},"content.text",[646,1273,1007],{},[631,1275,1276,1281,1286],{},[646,1277,1278],{},[452,1279,1280],{},"html",[646,1282,1283],{},[452,1284,1285],{},"content.html",[646,1287,1288],{},"HTML body (optional)",[631,1290,1291,1296,1301],{},[646,1292,1293],{},[452,1294,1295],{},"subject",[646,1297,1298],{},[452,1299,1300],{},"content.subject",[646,1302,1303],{},"subject (optional)",[631,1305,1306,1311,1316],{},[646,1307,1308],{},[452,1309,1310],{},"id",[646,1312,1313],{},[452,1314,1315],{},"messageId",[646,1317,1318,1320],{},[452,1319,767],{}," (optional)",[631,1322,1323,1328,1331],{},[646,1324,1325],{},[452,1326,1327],{},"metadata",[646,1329,1330],{},"—",[646,1332,1333],{},"object of string values (optional)",[807,1335,1337],{"className":809,"code":1336,"language":811,"meta":812,"style":812},"POST \u002Fwebhooks\u002Fchannel\nX-Webhook-Secret: \u003Cyour-secret>\nContent-Type: application\u002Fjson\n",[452,1338,1339,1344,1349],{"__ignoreMap":812},[816,1340,1341],{"class":818,"line":819},[816,1342,1343],{},"POST \u002Fwebhooks\u002Fchannel\n",[816,1345,1346],{"class":818,"line":825},[816,1347,1348],{},"X-Webhook-Secret: \u003Cyour-secret>\n",[816,1350,1351],{"class":818,"line":831},[816,1352,1353],{},"Content-Type: application\u002Fjson\n",[807,1355,1359],{"className":1356,"code":1357,"language":1358,"meta":812,"style":812},"language-json shiki shiki-themes github-light github-dark-dimmed","{\n  \"from\": \"user-42\",\n  \"text\": \"Hi, is anyone there?\",\n  \"id\": \"ext-9001\",\n  \"metadata\": { \"source\": \"intercom\" }\n}\n","json",[452,1360,1361,1367,1383,1395,1407,1426],{"__ignoreMap":812},[816,1362,1363],{"class":818,"line":819},[816,1364,1366],{"class":1365},"sYgZi","{\n",[816,1368,1369,1373,1376,1380],{"class":818,"line":825},[816,1370,1372],{"class":1371},"snmFh","  \"from\"",[816,1374,1375],{"class":1365},": ",[816,1377,1379],{"class":1378},"s-HuK","\"user-42\"",[816,1381,1382],{"class":1365},",\n",[816,1384,1385,1388,1390,1393],{"class":818,"line":831},[816,1386,1387],{"class":1371},"  \"text\"",[816,1389,1375],{"class":1365},[816,1391,1392],{"class":1378},"\"Hi, is anyone there?\"",[816,1394,1382],{"class":1365},[816,1396,1397,1400,1402,1405],{"class":818,"line":837},[816,1398,1399],{"class":1371},"  \"id\"",[816,1401,1375],{"class":1365},[816,1403,1404],{"class":1378},"\"ext-9001\"",[816,1406,1382],{"class":1365},[816,1408,1409,1412,1415,1418,1420,1423],{"class":818,"line":844},[816,1410,1411],{"class":1371},"  \"metadata\"",[816,1413,1414],{"class":1365},": { ",[816,1416,1417],{"class":1371},"\"source\"",[816,1419,1375],{"class":1365},[816,1421,1422],{"class":1378},"\"intercom\"",[816,1424,1425],{"class":1365}," }\n",[816,1427,1429],{"class":818,"line":1428},6,[816,1430,1431],{"class":1365},"}\n",[479,1433,1435],{"id":1434},"required-env-secrets-and-fail-closed-behavior","Required env secrets and fail-closed behavior",[448,1437,1438,1439,1442,1443,1446,1447,1449],{},"Every adapter reads its secret through ",[452,1440,1441],{},"apps\u002Fapi\u002Fconvex\u002Flib\u002Fenv.ts",". If the secret is ",[465,1444,1445],{},"unset",", the endpoint rejects with ",[452,1448,1087],{}," — it never accepts an unsigned request \"for now.\"",[625,1451,1452,1468],{},[628,1453,1454],{},[631,1455,1456,1459,1462,1465],{},[634,1457,1458],{},"Endpoint",[634,1460,1461],{},"Required secret",[634,1463,1464],{},"Missing →",[634,1466,1467],{},"Bad\u002Fabsent signature →",[641,1469,1470,1490,1509,1529],{},[631,1471,1472,1477,1481,1485],{},[646,1473,1474],{},[452,1475,1476],{},"POST \u002Fwebhooks\u002Fsms",[646,1478,1479],{},[452,1480,691],{},[646,1482,1483],{},[452,1484,1087],{},[646,1486,1487],{},[452,1488,1489],{},"401",[631,1491,1492,1497,1501,1505],{},[646,1493,1494],{},[452,1495,1496],{},"POST \u002Fwebhooks\u002Fwhatsapp",[646,1498,1499],{},[452,1500,944],{},[646,1502,1503],{},[452,1504,1087],{},[646,1506,1507],{},[452,1508,1489],{},[631,1510,1511,1516,1520,1524],{},[646,1512,1513],{},[452,1514,1515],{},"GET \u002Fwebhooks\u002Fwhatsapp",[646,1517,1518],{},[452,1519,1083],{},[646,1521,1522],{},[452,1523,1087],{},[646,1525,1526],{},[452,1527,1528],{},"403",[631,1530,1531,1536,1540,1544],{},[646,1532,1533],{},[452,1534,1535],{},"POST \u002Fwebhooks\u002Fchannel",[646,1537,1538],{},[452,1539,1209],{},[646,1541,1542],{},[452,1543,1087],{},[646,1545,1546],{},[452,1547,1489],{},[448,1549,1550],{},"Common status codes across the channel pipeline:",[625,1552,1553,1563],{},[628,1554,1555],{},[631,1556,1557,1560],{},[634,1558,1559],{},"Status",[634,1561,1562],{},"Meaning",[641,1564,1565,1578,1587,1596,1605,1617,1628],{},[631,1566,1567,1571],{},[646,1568,1569],{},[452,1570,1108],{},[646,1572,1573,1574,1577],{},"Accepted (or ",[452,1575,1576],{},"ignored: true"," for a no-message payload)",[631,1579,1580,1584],{},[646,1581,1582],{},[452,1583,559],{},[646,1585,1586],{},"Body could not be read or parsed",[631,1588,1589,1593],{},[646,1590,1591],{},[452,1592,1489],{},[646,1594,1595],{},"Missing or invalid signature \u002F shared secret",[631,1597,1598,1602],{},[646,1599,1600],{},[452,1601,1528],{},[646,1603,1604],{},"Meta verification challenge failed (GET only)",[631,1606,1607,1611],{},[646,1608,1609],{},[452,1610,512],{},[646,1612,1613,1614,1616],{},"Method other than ",[452,1615,508],{}," (channel endpoints)",[631,1618,1619,1623],{},[646,1620,1621],{},[452,1622,525],{},[646,1624,1625,1626],{},"Rate limited; honor ",[452,1627,529],{},[631,1629,1630,1634],{},[646,1631,1632],{},[452,1633,1087],{},[646,1635,1636],{},"Endpoint not configured — required secret is unset",[448,1638,1639,1640,1643,1644,1646],{},"Set these with ",[452,1641,1642],{},"convex env set"," (or your deployment's environment configuration). See ",[475,1645,333],{"href":332}," for the full registry.",[479,1648,1650],{"id":1649},"mta-inbound-routes","MTA inbound routes",[448,1652,1653,1654,1657,1658,1661,1662,1665,1666,1669,1670,1672,1673,1675,1676,1678],{},"Two further webhooks live on the same HTTP router but are callbacks from the bundled mail transfer agent and IMAP services, not customer channels. They authenticate with an HMAC-SHA256 signature keyed on ",[452,1655,1656],{},"MTA_WEBHOOK_SECRET",", sent as ",[452,1659,1660],{},"X-MTA-Signature"," over the string ",[452,1663,1664],{},"\u003Ctimestamp>.\u003Cbody>",", with the timestamp in ",[452,1667,1668],{},"X-MTA-Timestamp",". Both reject when ",[452,1671,1656],{}," is unset (",[452,1674,1087],{},") and reject a stale or missing timestamp (",[452,1677,1489],{},").",[884,1680,1682],{"id":1681},"post-webhooksmta-mailbox","POST \u002Fwebhooks\u002Fmta-mailbox",[448,1684,1685,1686,1689,1690,1693,1694,1697,1698,493],{},"Personal-mail (Postbox) inbound delivery from the MTA — distinct from the bounce\u002Fcomplaint callback at ",[452,1687,1688],{},"POST \u002Fwebhooks\u002Fmta",". It accepts the ",[452,1691,1692],{},"inbound.mailbox.received"," event, requires the timestamp to be within ",[465,1695,1696],{},"300 seconds"," of now, and forwards the parsed message into the Postbox delivery pipeline. See ",[475,1699,254],{"href":253},[884,1701,1703],{"id":1702},"post-webhooksmta-verify-credential","POST \u002Fwebhooks\u002Fmta-verify-credential",[448,1705,1706,1707,1710,1711,1714,1715,1718,1719,1722,1723,493],{},"App-password verification for IMAP and SMTP submission. The MTA\u002FIMAP server posts ",[452,1708,1709],{},"{ address, password, scope: \"imap\" | \"smtp\" }","; this lets it authenticate clients without holding the Convex admin key. The timestamp window here is tighter — ",[465,1712,1713],{},"60 seconds",". On a match it returns ",[452,1716,1717],{},"{ ok: true, mailboxId, appPasswordId, userId, organizationId }","; otherwise ",[452,1720,1721],{},"{ ok: false }",". See ",[475,1724,254],{"href":1725},"\u002Fdeveloper\u002Fpostbox-architecture#app-password-auth-flow",[457,1727,1729],{"title":1728,"type":460},"Outbound webhooks are separate",[448,1730,1731,1732,1735,1736,1739,1740,1742,1743,1745],{},"This page is about webhooks Owlat ",[470,1733,1734],{},"receives",". For the webhooks Owlat ",[470,1737,1738],{},"sends"," to your endpoints (delivery events, contact and topic changes), see ",[475,1741,186],{"href":185}," and the ",[475,1744,194],{"href":193}," contract.",[479,1747,1749],{"id":1748},"related","Related",[1751,1752],"link-card",{"description":1753,"title":134,"to":133},"What email, chat, SMS, WhatsApp, and generic channels can do in the product today.",[1751,1755],{"description":1756,"title":190,"to":189},"The other non-authenticated HTTP routes: tracking, unsubscribe, forms, archive, and share links.",[1758,1759,1760],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sYgZi, html code.shiki .sYgZi{--shiki-default:#24292E;--shiki-dark:#ADBAC7}html pre.shiki code .snmFh, html code.shiki .snmFh{--shiki-default:#005CC5;--shiki-dark:#8DDB8C}html pre.shiki code .s-HuK, html code.shiki .s-HuK{--shiki-default:#032F62;--shiki-dark:#96D0FF}",{"title":812,"searchDepth":825,"depth":825,"links":1762},[1763,1764,1765,1769,1770,1771,1775],{"id":481,"depth":825,"text":482},{"id":619,"depth":825,"text":620},{"id":871,"depth":825,"text":872,"children":1766},[1767,1768],{"id":886,"depth":831,"text":887},{"id":1057,"depth":831,"text":1058},{"id":1143,"depth":825,"text":1144},{"id":1434,"depth":825,"text":1435},{"id":1649,"depth":825,"text":1650,"children":1772},[1773,1774],{"id":1681,"depth":831,"text":1682},{"id":1702,"depth":831,"text":1703},{"id":1748,"depth":825,"text":1749},"md",{},{"title":198,"description":199},"2.api\u002F13.inbound-channels","CahJ-DOjoT_P3i0Iwa5jJ0QoCJ2RUohDT0pwzHfGXWI",[1782,1784],{"title":194,"path":193,"stem":1783,"children":-1},"2.api\u002F12.webhook-payloads",{"title":202,"path":201,"stem":1785,"children":-1},"2.api\u002F2.authentication",1782846427038]