AI Marketing Bot - Existing Infrastructure Summary
1. API Routes Structure (api/src/routes/)
posts.ts
Purpose: Post generation, regeneration, publishing, and retrieval
Key Endpoints:
POST /generate_post— Create posts for a campaign or customerPOST /bot/generate_post— Bot-initiated generation (text/plain body)POST /post/init— Create parent post record (returns post_id)POST /post/:id/platform— Save platform-specific contentPOST /regenerate_post— Regenerate content for specific platformPOST /regenerate_post_title— Regenerate post title onlyPOST /edit_post— AI-powered content editing with instructionsPOST /auto_generate— Async post generation (fire-and-forget)POST /process_email_template— Extract structure from HTML emailPOST /publish_post— Publish to Ghost CMSGET /post/:id— Full post details with all platform contentPATCH /post/:id/status— Update post workflow statusPATCH /post/:id/schedule— Schedule post for publishingPOST /post/:id/schedule-multiple— Batch schedule multiple posts
Database Tables Used:
- customer_posts (id, title, prompt, status, platforms, scheduled_at, created_at)
- customer_platform_post (id, customer_post_id, platform, content, images, metadata)
- integration_log (post_id, integration, created_at, result)
clients.ts
Purpose: Client context, analytics, and asset management
Key Endpoints:
GET /client/:id/context— Brand voice + industries + description + analyticsGET /client/:id/posts— List posts with status/platform filteringPOST /client/:id/media— Upload or save content assetGET /client/:id/analytics— Aggregate engagement metricsGET /client/:id/scheduled-posts— List scheduled posts for a clientGET /user/customers— List all customers for authenticated userGET /customers/search— Search customers by namePOST /client/:id/crawl— Trigger Cloudflare browser crawlPOST /client/:id/smtp— Configure SMTP for email sendingPUT /client/:id/smtp— Update SMTP settingsDELETE /client/:id/smtp— Remove SMTP configurationGET /client/:id/email-lists— List email subscriber listsPOST /client/:id/email-lists— Create new email listPUT /client/:id/email-lists/:list_id— Update email listDELETE /client/:id/email-lists/:list_id— Delete email listGET /client/:id/email-lists/:list_id/subscribers— List subscribersPOST /client/:id/email-lists/:list_id/subscribers— Add subscriberPUT /client/:id/email-lists/:list_id/subscribers/:subscriber_id— Update subscriberDELETE /client/:id/email-lists/:list_id/subscribers/:subscriber_id— Remove subscriberPOST /client/:id/email-segments/sync— Sync behavioral segmentsGET /client/:id/email-metrics— Email performance metrics
Database Tables Used:
- customer_customer (id, name, description, logo_url, industries, active)
- client_brand_voice (customer_id, rule_type, rule_text, source)
- post_analytics (platform_post_id, metric_type, metric_value, recorded_at)
- client_content_asset (customer_id, asset_type, url, content, metadata)
- smtp_config (customer_id, host, port, username, encrypted_password)
- email_list (customer_id, name, description, subscriber_count)
- email_list_subscriber (list_id, email, name, tags, metadata)
- email_segment (customer_id, name, segment_type, rules, subscriber_count)
telegram.ts
Purpose: Telegram pairing, brand voice setup, and approval workflow
Key Endpoints:
POST /telegram/pair— Pair Telegram chat_id to user accountGET /telegram/status— Get current Telegram connection statusPOST /telegram/unpair— Unpair Telegram accountPOST /client/:id/brand-voice— Add brand voice rulePOST /post/approve— Approve/reject/request edits on a postGET /posts/pending-reminders— List posts in review >24h (for cron)
Database Tables Used:
- telegram_user_mapping (telegram_chat_id, user_id, paired_at, is_active, last_interaction_at)
- client_brand_voice (customer_id, rule_type, rule_text, source)
- post_feedback (post_id, feedback_type, feedback_text, created_at)
- customer_posts (status: new → pending_review → approved → revision_requested → scheduled → sent)
integrations.ts
Purpose: OAuth flows and social platform credential management
Key Endpoints:
POST /integrations/outstand/auth-url— Get Outstand OAuth URLGET /integrations/outstand/callback— OAuth callbackGET /integrations/outstand/pending— Check pending OAuth sessionPOST /integrations/outstand/finalize— Finalize connection with page selectionGET /integrations/blogger/callback— Blogger OAuth callbackGET /client/:id/integrations— List connected platformsGET /client/:id/integrations/accounts— List Outstand accountsPOST /client/:id/integrations/analytics/sync— Sync post analyticsDELETE /client/:id/integrations/:integration_id— Remove integrationPUT /client/:id/integrations/twitter— Update Twitter credentialsDELETE /client/:id/integrations/twitter— Remove TwitterPUT /client/:id/integrations/linkedin— Update LinkedIn credentialsDELETE /client/:id/integrations/linkedin— Remove LinkedInPUT /client/:id/integrations/facebook— Update Facebook credentialsDELETE /client/:id/integrations/facebook— Remove FacebookPUT /client/:id/integrations/mailchimp— Update Mailchimp credentialsDELETE /client/:id/integrations/mailchimp— Remove MailchimpGET /client/:id/integrations/platforms— Get supported platforms list
Database Tables Used:
- customer_integration (customer_id, integration, access_token, metadata, created_at)
- post_analytics (platform_post_id, metric_type, metric_value, recorded_at)
tracking.ts
Purpose: Email open/click tracking (public endpoints, no auth)
Key Endpoints:
GET /track/open?t=<token>— Record email open, return 1×1 GIFGET /track/click?t=<token>&u=<url>— Record click, redirect to URL
Database Tables Used:
- email_tracking_event (token, event_type, metadata, created_at)
- email_send_log (id, resend_email_id, customer_id, recipient_email)
webhooks.ts
Purpose: Inbound webhooks from third-party services (public, signature-verified)
Key Endpoints:
POST /webhooks/resend— Resend delivery/bounce/complaint/unsubscribe events
Supported Events:
- email.delivered →
delivered - email.bounced →
bounced(with hard/soft classification) - email.complained →
complained - email.unsubscribed →
unsubscribed
Database Tables Used:
- email_send_log (resend_email_id lookup)
- email_tracking_event (provider events recorded here)
admin.ts
Purpose: Admin-only management endpoints
Key Endpoints:
GET /admin/customers— List all customers with stats (briefing history, pending posts, telegram pairing)POST /admin/customers/:id/trigger-briefing— Manually trigger morning briefing for a customer
Database Tables Used:
- customer_customer
- telegram_user_mapping
- customer_posts
- morning_briefing_log
2. Main API Entry Point (api/src/index.ts)
Key Features:
- Fastify-based server with TypeBox type provider
- CORS enabled (origin: '*')
- Auth via:
x-api-keyheader = API_KEY (worker role)- Authorization Bearer JWT = WORKER_API_JWT_SECRET (worker role)
- Supabase JWT token validation
- Route registration:
- postRoutes
- clientRoutes
- contentRoutes
- Health check:
GET /endpoint
Auth Hook Validation:
- Checks headers on all routes except '/'
- Returns 401 if no valid auth found
3. Bot Configuration (bot/data/config.json)
Key Settings:
{
"database": { "mode": "managed" },
"agents": {
"defaults": {
"provider": "openrouter",
"model": "anthropic/claude-haiku-4.5",
"max_tool_iterations": 25,
"max_tokens": 4096,
"temperature": 0.7,
"memory": {
"enabled": true,
"embedding_provider": "gemini",
"embedding_model": "text-embedding-004"
},
"subagents": {
"maxConcurrent": 8,
"maxSpawnDepth": 3,
"maxChildrenPerAgent": 10
}
},
"list": {
"liaison-agent": {
"displayName": "Marketing Liaison",
"identity": { "name": "Aimee", "emoji": "💬" },
"default": true,
"skills": ["marketing-liaison"],
"tools": { "allow": ["*"], "deny": [] }
},
"creator-agent": {
"displayName": "Content Creator",
"identity": { "name": "Creator", "emoji": "✍️" },
"provider": "openrouter",
"model": "anthropic/claude-haiku-4.5",
"max_tokens": 8192,
"tools": { "allow": ["*"], "deny": ["exec", "shell"] }
},
"critic-agent": {
"displayName": "Brand Critic",
"identity": { "name": "Critic", "emoji": "🔍" },
"skills": ["brand-review"],
"max_tool_iterations": 15,
"tools": { "allow": ["memory_search", "memory_get", "evaluate_loop"], "deny": ["*"] }
}
}
},
"channels": {
"telegram": {
"enabled": true,
"token": "${TELEGRAM_BOT_TOKEN}",
"dm_policy": "pairing",
"group_policy": "disabled",
"require_mention": false,
"stream_mode": "partial",
"history_limit": 50,
"media_max_bytes": 20971520
}
},
"bindings": [
{
"agentId": "liaison-agent",
"match": { "channel": "telegram" }
}
]
}
Telegram Configuration:
- DM policy: "pairing" (must be paired to use)
- Group policy: "disabled" (no group messages)
- Media limit: 20 MB
- Stream mode: partial (streaming enabled)
- History limit: last 50 messages
4. Agent Skills
marketing-liaison/SKILL.md
Role: Client-facing marketing assistant (Aimee)
Responsibilities:
- Greet and understand client needs
- Delegate creation to creator-agent
- Request review from critic-agent
- Present drafts to clients in friendly Telegram format
- Handle client feedback
- Confirm publishing
Key Rules:
- Never generate content directly (always delegate)
- Never skip the critic (always review before showing client)
- Format for Telegram (no tables, use emojis, short messages)
- Track approvals
- Proactive reminders for pending posts
Tools Used:
- delegate (to creator-agent and critic-agent)
- memory_search
- fetch-client-context
- list-pending-posts
- message (for Telegram notifications)
content-creation/SKILL.md
Role: Multi-platform content generator
Responsibilities:
- Generate platform-optimized content
- Always fetch client context first
- Research URLs if needed
- Add relevant images
- Return structured JSON
Platform Guidelines Included:
- Instagram: Max 2,200 chars, 5-10 hashtags, emojis, carousel format
- Twitter: Max 280 chars, 1-2 hashtags, hook in first 100 chars
- LinkedIn: 1,300-2,000 chars, professional but not stuffy, 3 hashtags max
- Email: 40-50 char subject, 150-300 word body, clear CTA
- Blog: 800-1,500 words, SEO-optimized, 2-3 images
Tools Used:
- fetch-client-context (ALWAYS called first)
- scrape-url
- search-images
- memory_search
- web_search
brand-review/SKILL.md
Role: Brand voice guardian for content review
Responsibilities:
- Review drafts created by creator-agent
- Evaluate tone, terminology, audience fit
- Return clear verdict (pass/fail/conditional pass)
- Provide specific feedback
Evaluation Criteria:
- Tone check (formal, casual, playful, authoritative)
- Terminology check (correct industry terms, avoid prohibited terms)
- Audience fit (relevant to target, right complexity level)
- Brand alignment (reinforces values, aligned CTA)
Output Format Includes:
- verdict: pass | fail | conditional_pass
- overall_score (out of 10)
- platform_reviews with individual scores
- suggested_fixes for conditional passes
Tools Used:
- memory_search
- memory_get
- evaluate_loop
5. Bot Tools (api/src/bot/tools.ts)
The bot uses LangChain tools that wrap existing service functions directly. No MCP serialization, no seeding scripts. Tools are defined in api/src/bot/tools.ts and include: fetch-client-context, list-posts, generate-content, approve-post, search-images, scrape-url, publish-content, pair-telegram, add-brand-voice, and more.
See api/src/bot/tools.ts for the full list.
6. Database Schema
Existing Tables (Phase 2 Complete)
telegram_client_mapping
- id (BIGSERIAL PK)
- telegram_chat_id (TEXT UNIQUE)
- customer_id (BIGINT FK → customer_customer)
- paired_at (TIMESTAMPTZ)
- is_active (BOOLEAN)
- pairing_code (TEXT)
- last_interaction_at (TIMESTAMPTZ)
- Indexes: chat_id, customer_id
client_brand_voice
- id (BIGSERIAL PK)
- customer_id (BIGINT FK)
- rule_type (TEXT: tone, terminology, audience, style)
- rule_text (TEXT)
- source (TEXT: manual, analytics, ai_learned)
- created_at, updated_at (TIMESTAMPTZ)
- Indexes: customer_id, customer_id+type
client_content_asset
- id (BIGSERIAL PK)
- customer_id (BIGINT FK)
- asset_type (TEXT: image, document, scraped_page, menu, video)
- url (TEXT)
- content (TEXT)
- metadata (JSONB)
- created_at (TIMESTAMPTZ)
- Indexes: customer_id, customer_id+type
post_analytics
- id (BIGSERIAL PK)
- platform_post_id (BIGINT FK → customer_platform_post)
- metric_type (TEXT: open_rate, click_rate, likes, shares, comments, impressions, reach)
- metric_value (NUMERIC)
- recorded_at (TIMESTAMPTZ)
- Indexes: platform_post_id, recorded_at, platform_post_id+type
integration_log
- id (BIGSERIAL PK)
- post_id (INTEGER FK → customer_posts)
- integration (TEXT)
- created_at (TIMESTAMPTZ)
- result (JSONB)
- Unique: (post_id, integration)
- Indexes: post_id, integration, created_at
customer_posts (Modified)
- Added: status (TEXT: new, pending_review, approved, revision_requested, scheduled, sent)
- Added: active (BOOLEAN default true)
- Index: status
marketing_calendar (For Phase 4)
- id (BIGSERIAL PK)
- event_name (TEXT)
- event_date (DATE)
- event_type (TEXT: holiday, industry_event, custom, awareness_day)
- industries (TEXT[])
- description (TEXT)
- is_global (BOOLEAN)
- created_at (TIMESTAMPTZ)
- Indexes: event_date, industries (GIN)
post_feedback Table Status
NOT YET CREATED - Referenced in phase-3-conversation-flow.md but no migration exists. Should be created to store client feedback on posts with structure:
CREATE TABLE post_feedback (
id BIGSERIAL PRIMARY KEY,
post_id BIGINT NOT NULL REFERENCES customer_posts(id),
feedback_text TEXT NOT NULL,
feedback_type TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
7. Environment Configuration (api/src/config/env.ts)
{
API_KEY: string (shared secret for internal workers/scheduler),
LLM_PROVIDER: string (default: 'ollama'),
NODE_ENV: string (default: 'development'),
PORT: number (default: 8000),
HOST: string (default: '0.0.0.0'),
// Supabase
SUPABASE_URL: string,
SUPABASE_KEY: string,
SUPABASE_SERVICE_ROLE_KEY: string (bypasses RLS),
// Google Gemini
GOOGLE_API_KEY: string,
// Ollama
OLLAMA_BASE_URL: string (default: http://localhost:11434),
OLLAMA_MODEL: string (default: llama2),
// SerpAPI
SERPAPI_API_KEY: string,
// Unsplash
UNSPLASH_ACCESS_KEY: string,
// Ghost CMS
GHOST_API_URL: string,
GHOST_ADMIN_API_KEY: string,
// Bot
BOT_ENABLED: boolean (enables Telegraf adapter + cron jobs),
TELEGRAM_BOT_TOKEN: string,
CRON_TIMEZONE: string (default: UTC),
OPENROUTER_API_KEY: string (multi-model LLM strategy),
// Email (Resend)
RESEND_API_KEY: string,
RESEND_FROM_EMAIL: string,
RESEND_FROM_NAME: string,
RESEND_WEBHOOK_SECRET: string,
// Encryption
INTEGRATION_ENCRYPTION_KEY: string (AES-256),
// Error Tracking
SENTRY_DSN: string,
// Cloudflare
CF_API_TOKEN: string,
CF_ACCOUNT_ID: string
}
11. Front-end Architecture (front-end/)
Technology Stack
- Framework: Vue 3.5 with Composition API
- Build Tool: Vite 6.2
- Router: Vue Router 4.5 (file-based via unplugin-vue-router)
- State Management: Pinia 3.0
- CSS Framework: Bulma (via @cssninja/bulma) with CSS variables
- Type Checking: TypeScript 5.8 + vue-tsc
- Testing: Vitest (unit) + Playwright (E2E)
- Deployment: Cloudflare Pages via Wrangler
Development Setup
cd front-end
pnpm install
pnpm dev # Vite dev server on http://localhost:3547
pnpm build # Production build (sitemap + bulma + vite)
pnpm test # Run type check + unit tests in parallel
pnpm test:e2e # Run Playwright E2E tests
pnpm deploy # Build + deploy to Cloudflare Pages
File-based Routing
Pages in src/pages/ are automatically converted to routes:
index.vue→/auth.vue+auth/index.vue→/authauth/signup.vue→/auth/signupapp.vue+app/index.vue→/appapp/clients.vue→/app/clientsapp/client/[id].vue→/app/client/:idapp/client/[id]/settings.vue→/app/client/:id/settings
Full Route List (20 pages):
/- Landing page/auth- Login/auth/signup- Sign up/auth/profile- User profile/app- Dashboard home/app/clients- Client list/app/posts- Posts list/app/edit- Post editor/app/settings- User settings/app/admin- Admin panel/app/client/:id- Client overview/app/client/:id/integrations- OAuth integrations/app/client/:id/settings- Client settings (brand voice, Telegram pairing)/app/client/campaigns/:id- Campaign detail/app/campaign/new- Create campaign/app/campaign/:id- Campaign editor[...all]- 404 catch-all
Pinia Stores (Auto-imported)
Located in src/stores/:
- campaigns.ts - Campaign state management
- clients.ts - Client/customer state
- notes.ts - Notes functionality
- user-session.ts - User auth session (Supabase)
Composables (Auto-imported)
Located in src/composables/:
- fetch.ts - API fetch wrapper (auto-attaches Supabase auth token)
- supabase.ts - Supabase client factory
- user-token.ts - User auth token management
- notyf.ts - Toast notifications
- darkmode.ts - Dark mode toggle
- dropdown.ts - Dropdown state management
- screen-size.ts - Responsive breakpoint helpers
- theme-colors.ts - Theme color management
- useBreadcrumbs.ts - Breadcrumb navigation
- useCrawlWatcher.ts - URL crawl watching
- field-context.ts - Form field context
- image-error.ts - Image error handling
- vuero-context.ts - Vuero theme context
Utilities
Located in src/utils/:
- platforms/htmlWrappers.ts - Platform-specific HTML wrappers for content rendering
- platforms/icons.ts - Platform icon mappings
- apex-formatters.ts - ApexCharts data formatters
- plugins/ - Vue plugins
Component Library
Base components in src/components/base/:
- VCard / VCardAdvanced - Card containers (replaces Bulma
.box) - VButton - Button component
- VAvatar - Avatar display
- VCheckbox - Checkbox input
- VAccordion - Accordion/collapsible
- VBreadcrumb - Breadcrumb navigation
- And 15+ more...
CRITICAL CONVENTION: Never use <div class="box">. Always use <VCard radius="smooth"> instead. VCard uses CSS variables that correctly theme in both light and dark mode, while Bulma's .box does not.
Auto-imports Configuration
Via unplugin-auto-import in vite.config.ts:
- All Vue APIs (
ref,computed,onMounted, etc.) - All VueUse composables (
useLocalStorage,useEventListener, etc.) - All Vue Router composables (
useRoute,useRouter, etc.) - All files in
src/composables/,src/stores/,src/utils/
No explicit imports needed for these in .vue files.
Path Alias
/@src/ maps to src/ in all imports (configured in vite.config.ts).
Styling System
- Bulma CSS framework via
@cssninja/bulma-css-vars - CSS variable theming configured in
bulma-css-vars.config.cjs - Dark mode support via CSS variable switching
- PurgeCSS removes unused styles in production
- Custom SCSS in
src/scss/main.scss
API Integration
- All API calls go through
src/composables/fetch.ts - Supabase auth token automatically attached via
src/composables/user-token.ts - API base URL from env:
VITE_API_BASE_URL - Supabase client from
src/composables/supabase.ts
Authentication Flow
- User logs in via
/auth(Supabase Auth) - Session stored in Pinia
user-session.tsstore - Auth token retrieved via
user-token.tscomposable - Token attached to all API requests via
fetch.ts - Protected routes redirect to
/authif not authenticated
OAuth Integration Pages
/app/client/:id/integrations- Connect social accounts- Supported platforms: Twitter, LinkedIn, Facebook, Mailchimp
- OAuth flow: Dashboard → API → Platform → Callback → Dashboard
- Credentials stored encrypted in
customer_integrationtable
8. Bot Module (api/src/bot/)
The Aimee bot runs in-process inside the API (enabled via BOT_ENABLED=true). No separate containers or seeding scripts are required.
bot/index.ts— Starts/stops channel adapters when the API bootsbot/channels/telegram.ts— Telegraf adapter; routes incoming messages to the agentbot/agent.ts— LangChain tool-calling agent; calls service functions directlybot/tools.ts— LangChain tool wrappers around existing service functionsbot/cron.ts— node-cron jobs (morning briefing 9 AM, reminders 10 AM, weekly summary Mon 9 AM)bot/memory.ts— Loads/saves conversation turns tochat_historytable
9. Phase 3 Requirements (Coming)
Missing Components for Phase 3
post_feedback Table - Not yet created
- Referenced in phase-3-conversation-flow.md
- Should store: post_id, feedback_text, feedback_type, created_at
New API Endpoints Needed:
POST /telegram/pair— Telegram client pairing with customer_idPOST /client/:id/brand-voice— Add brand voice rulesPOST /post/approve— Handle approval/rejection/edit actionsGET /posts/pending-reminders— For approval reminder cron
Telegram Integration Points:
- DM pairing flow (/start command)
- Brand voice setup conversation
- Welcome sequence with example post
- Content approval workflow with buttons
- Media upload handling (images, documents)
- Daily reminder cron (24h timeout)
- Post-publish notifications
- Weekly summary cron
State Machine Implementation:
- Post status flow: new → pending_review → approved → scheduled → sent
- Revision path: any status → revision_requested → back to new
- Max 2 revision loops before presenting anyway
10. Current Status vs. Phase 4 Needs
| Feature | Status | Notes |
|---|---|---|
| 4-Agent Team (liaison, creator, critic, briefing) | ✅ Complete | All agents seeded and active |
| MCP Server with 28 Tools | ✅ Complete | Registered via scripts/register-mcp-server.sh |
| Telegram Pairing | ✅ Complete | Via front-end dashboard Settings → Messaging |
| Brand Voice Management | ✅ Complete | add-brand-voice MCP tool + /client/:id/brand-voice endpoint |
| Post Approval Workflow | ✅ Complete | approve-post MCP tool handles approve/reject/request_edit |
| Post Feedback Table | ✅ Complete | Migrated in 20260322142226_remote_schema.sql |
| Telegram pairing, brand-voice, approve, pending-reminders Endpoints | ✅ Complete | All implemented in api/src/routes/telegram.ts |
| Cron Jobs (morning briefing, approval reminders, weekly summary) | ✅ Complete | Implemented via node-cron in api/src/bot/cron.ts (runs in-process when BOT_ENABLED=true) |
| Publishing Adapters (Twitter, LinkedIn, Facebook, Mailchimp, Ghost) | ✅ Complete | All implemented in api/src/integrations/ |
| OAuth Flows | ✅ Complete | Implemented in api/src/routes/integrations.ts |
| Email Tracking | ✅ Complete | Open/click tracking in api/src/routes/tracking.ts, Resend webhooks in webhooks.ts |
| Marketing Calendar Integration | 🚧 Phase 4 | Table exists, integration pending |
| Website Monitoring | 🚧 Phase 4 | Cloudflare Browser Rendering configured, cron job pending |
| Analytics-Driven Optimization | 🚧 Phase 4 | Analytics collection complete, memory integration pending |
Quick Reference
Four Agents:
- liaison-agent (Aimee) — Client interface, orchestrator, Telegram entry point
- creator-agent — Content generation, platform-specific formatting
- critic-agent — Brand voice validation, no modification
- briefing-agent — Morning briefing cron job, daily status snapshots
Agent Delegation Flow:
Client (Telegram)
↓
liaison-agent (fetch-client-context)
↓
creator-agent (generate content)
↓
critic-agent (review)
↓ (if pass)
liaison-agent → Client (approval buttons)
↓ (if approved)
publish endpoints
MCP Server:
- Mounted at
POST /mcp - 28 tools exposed to agents
- Tools call service layer (
api/src/services/) - All tools require Telegram auth (except admin tools)
- Registered via
scripts/register-mcp-server.sh
Main Database Relations:
- customer_customer (1) ← → (M) customer_posts
- customer_posts (1) ← → (M) customer_platform_post
- customer_posts (1) ← → (M) integration_log
- customer_customer (1) ← → (M) client_brand_voice
- customer_customer (1) ← → (M) client_content_asset
- customer_customer (1) ← → (M) telegram_client_mapping
- customer_platform_post (1) ← → (M) post_analytics
Telegram Configuration:
- Bot token from
TELEGRAM_BOT_TOKENenv var - DM policy: pairing (client must be paired to message bot)
- Pairing: via front-end dashboard Settings → Messaging Connections
- Channels binding: liaison-agent handles all Telegram messages
- Cron jobs: morning briefing (9 AM), approval reminders (10 AM), weekly summary (Mon 9 AM)