Journey 6: Content Engine

Daily AI content generation, Telegram approval, and social media posting

7 AM trigger AI generates Telegram preview Approve/Reject Blotato publishes Engagement tracked
STEP 1Cron → Content Engine

Daily pipeline triggers at 7 AM EST

Every morning the Content Engine wakes up and checks the content calendar and recent post history to avoid repetition. It then decides which content type to generate for today: procedure spotlight, education, seasonal promo, or brand awareness.

▶ Cron schedule + pipeline initialisation
Source files
src/consolidator_content.jsrunDailyContentPipeline() (line 11)
src/consolidator_scheduler.js
What happens
The scheduler registers a cron at 0 12 * * * (12:00 UTC = 7 AM EST). On each tick it calls runDailyContentPipeline(), which queries content_calendar for scheduled posts and content_submissions for recent approvals. It selects the highest-priority content type not posted in the last 48 hours and hands the decision to the generation step.
DB tables
content_calendar, content_pipelines, content_submissions
STEP 2Content Engine → AI

AI generates the content

Karen AI writes a complete social post: caption, hashtags, and an image suggestion. Content is brand-aware — professional yet approachable, rooted in Jamaican context, and positioned for a luxury med spa audience.

▶ AI generation via content-specific prompt
Source files
src/consolidator_content.jspresentDailyContent() (line 132)
What happens
presentDailyContent() builds a content-specific prompt that includes Oshun's brand voice guidelines, the selected content type, and a summary of recent posts to avoid duplication. The AI pipeline returns a structured response containing the caption text, hashtag set, and a plain-language image concept. The draft is immediately written to content_submissions with status draft.
DB tables
content_submissions (status: draft)
STEP 3Content Engine → Telegram

Preview sent to Telegram

The generated content is sent to the Oshun Telegram channel as a preview. The message shows the full caption, hashtags, and image concept alongside Approve and Reject inline buttons for one-tap review.

▶ Telegram preview message + inline keyboard
Source files
src/consolidator_content.jspresentDailyContent() sends via bot
src/telegram_callbacks_content.js → handles button callbacks
What happens
presentDailyContent() formats the draft into a Telegram message and attaches an inline keyboard with content_approve and content_reject callback data. The submission ID is encoded in the callback so the handler knows which draft is being acted on. The submission status is updated to pending_approval at this point.
DB tables
content_submissions (status: pending_approval)
STEP 4Telegram → Content Engine

Approval or rejection

Matthew or Dr. Rockhead taps Approve to send the post live, or Reject to regenerate or skip. No post goes out without human sign-off — the system never publishes autonomously.

▶ Approval callbacks + regeneration logic
Source files
src/telegram_callbacks_content.jscontent_approve / content_reject callbacks
src/consolidator_content.jsautoApproveContent() (line 185)
What happens
On content_approve, the callback calls autoApproveContent(), which updates the submission to approved and queues it for publishing. On content_reject, the submission is marked rejected, rejection feedback is written to content_feedback, and the pipeline can optionally regenerate with a revised brief. The Telegram message is edited to confirm the action taken.
DB tables
content_submissions, content_feedback
STEP 5Content Engine → Blotato → IG/FB/YT

Blotato publishes

Approved content is handed to Blotato, which publishes to Instagram, Facebook, and YouTube. Each platform receives a correctly formatted version of the post — Blotato handles the per-platform differences automatically.

▶ Multi-platform publish via Blotato API
Source files
src/actions/blotato/publish.js
src/actions/blotato/config_accounts.js
src/actions/blotato/media.js
What happens
publish.js reads the approved submission, calls config_accounts.js to resolve the tenant's connected platform accounts, then prepares media via media.js if an image is attached. The Blotato API receives the post payload for each target platform in sequence. Each successful publish response is recorded in content_distributions with the platform, post ID, and timestamp.
Env vars
BLOTATO_API_KEY
DB tables
content_distributions
STEP 6Analytics

Engagement tracked

After publishing, the system tracks likes, comments, shares, and saves. A scoring model evaluates each post's performance relative to baseline. High performers are weighted more heavily when the AI selects future content types and briefs.

▶ Engagement scoring + growth tracking
Source files
src/actions/analytics/scoring.js
src/actions/analytics/growth.js
src/actions/feedback_loop/scoring.js
What happens
Engagement metrics are pulled from Blotato at a configurable interval after publish. analytics/scoring.js computes a normalised engagement score against the tenant's rolling average. analytics/growth.js logs follower deltas to follower_snapshots. feedback_loop/scoring.js writes the final score back to content_feedback, where it influences the content type weighting used in Step 1 of the next day's pipeline.
DB tables
content_feedback, marketing_analytics, follower_snapshots