Visit Project Liberal Foundation
Project Liberal β€” Backend System Documentation
πŸ—οΈ

System Overview

The Project Liberal content system is an automation pipeline connecting Notion (content storage), Google Chat (user interface), Make.com (orchestration), Zoho Flow (scheduling delays), Metricool (social publishing), Claude API (AI processing), WordPress (media hosting), and Google Sheets (configuration and state).

There are two organizations β€” Project Liberal Action and Project Liberal Foundation β€” each with its own Notion content database, Metricool social accounts, and Google Chat spaces. The infrastructure is mirrored: same automations, same schemas, separate data.

A third organization, Liberal Memes (History is Over), exists in the database layer (Organization Settings, People in Content relations, Scheduling History) but is excluded from the user-facing documentation and chatbot routing.


πŸ—ΊοΈ

Architecture Map

Six Make.com scenarios, three Notion automation sets, one Zoho Flow, and supporting services.

User Entry Points
Google Chat message β†’ Chatbot 1.1
Card button click β†’ Chatbot Link Clicker
Notion Triggers
Type set on content page β†’ New Content Submission
Status changed on content page β†’ Schedule from Status
Scheduled Date changed β†’ Zoho Flow (delay) β†’ Metricool Content Poster
People status β†’ "Search for handles" β†’ Update Person in Content

πŸ”§

Services & Accounts

ServiceRoleNotes
Make.comOrchestration6 scenarios. All webhooks on us1.make.com.
NotionContent storage4 databases (2 content, People, Scheduling History). 5 automations across databases.
Google ChatUser interfaceBot receives messages via event trigger (not webhook). Spaces are isolated.
Google SheetsConfig & state9 sheets: Messages, Space Settings, User Settings, Org Settings, ChatGPT Prompts, Info for Make.com, Thumbnail Links, Database Sync, Time Interpreter.
Google DriveFile storageAll uploaded media (images, video) stored here. Thumbnails extracted from Drive.
Claude APIAI processingAll calls use claude-haiku-4-5-20251001. Used for intent routing, Notion queries, content creation, image description, transcript summarization, people matching.
Zoho FlowScheduling delaySingle flow with two Deluge functions. Holds content until scheduled time, then sends to Make.com.
MetricoolSocial publishingPer-network scheduleAPost calls. Separate Metricool accounts per org.
WordPressMedia hostingThumbnails uploaded to projectliberal.org via WP REST API (/wp-json/wp/v2/media). Used for content card images.
OpenAI (Whisper)TranscriptionVideo audio transcribed via whisper-1. Called from New Content Submission.
CloudConvertVideo processingExtracts audio from video files before Whisper transcription.
SerperWeb searchUsed in Update Person in Content to find social media profiles.
YouTube APIVideo uploadDirect video upload from Google Drive for Video Short content.

⚑

Content Database Automations

Three Notion automations run on both PL Action Content and PL Foundation Content (6 total, mirrored). Each sends a webhook with all existing properties.

Submit New Content
Trigger: Type is set to Video Short, Graphic, or Tweet

Fires webhook to Make.com New Content Submission scenario. Sends all properties. This is the content enrichment pipeline β€” generates Name, Tags, descriptions, thumbnails, transcripts, people tagging.

Schedule Automation
Trigger: Status is edited

Fires webhook to Make.com Schedule from Status scenario. Sends all properties including Name - Description, Canva Link, Caption (public). This handles "Schedule -" status values β€” assigns time slots and updates the page.

Schedule Date From Status
Trigger: Scheduled Date is edited

Fires webhook to Zoho Flow (not Make.com). Sends the page ID and scheduled date. No properties selected in the webhook content β€” Zoho only needs the ID and date to calculate the delay.

ℹ️
Chain reaction: Setting a "Schedule -" status β†’ fires Schedule Automation β†’ Make.com sets a Scheduled Date β†’ fires Schedule Date From Status β†’ Zoho Flow delays β†’ fires Metricool Content Poster. Three automations and three services for one user action.

⚑

People in Content Automations

Two automations on the People in Content database.

Look for Social Media Handles
Trigger: Status is set to "Search for handles"

Sends webhook to Make.com Update Person in Content scenario with Name, Liberal Memes DB relation, and Bio. The scenario searches the web for social profiles and writes URLs back to Notion.

Update Handles
Trigger: Any property edited

Runs Notion formulas to extract handles from URL properties. Strips domains (handles both twitter.com and x.com) and formats as (@handle) for: Twitter, Threads, Bluesky, Facebook, Instagram, TikTok. The formula uses nested replaceAll with regex to strip the domain and trailing path.

// Twitter handle extraction formula (representative)
if(
  empty( Trigger page . Twitter URL ),
  "",
  " (@" +
  replaceAll(
    replaceAll(
      Trigger page . Twitter URL,
      "https?://(www\\.)?(twitter\\.com|x\\.com)/",
      ""
    ),
    "/.*",
    ""
  ) +
  ")"
)

πŸ€–

Chatbot 1.1

Trigger: Google Chat event

The main scenario. Receives all Google Chat messages and routes them through the AI pipeline. This is the largest scenario with ~80 modules across 9+ workflow routes.

Top-Level Routes

RouteTrigger ConditionWhat It Does
Route 0Message is a replyFetches original message via Google Chat API. Logs to Sheets history.
Route 1Message contains URLRegex parses URL. Sub-routes: bystander (log only), webpage (fetch + HTML-to-text + Claude), image URL (upload to Drive + Claude describe + WordPress thumbnail).
Route 2Message has attachmentSub-routes for non-Drive attachments (fetch via Chat API) vs Drive attachments (fetch metadata). Both: upload to Drive β†’ Claude describe β†’ WordPress thumbnail β†’ log.
Route 3Default (all messages)Main processing pipeline. Logs message, loads space config, loads permissions.

Route 3 Sub-Routes (Workflow Engine)

Sub-RouteWorkflowKey Modules
3.0New user / new spaceProvisions records in Sheets. Sends welcome message. Lists Notion users for initial sync.
3.1.0PermissionsClaude Haiku parses request β†’ Sheets lookup/update β†’ Chat response. Manages all 6 permission types except Admin.
3.1.1Button ManagerClaude Haiku processes request β†’ Sheets config read/write β†’ Chat response.
3.1.2EditingClaude Haiku generates edit JSON β†’ Sheets lookup β†’ Notion update β†’ Chat response with card.
3.1.3SearchLoads field data from Sheets β†’ Claude Haiku builds Notion API query β†’ Notion search β†’ Chat card(s).
3.1.4Database SyncFetches Notion DB schema via API β†’ iterates fields β†’ syncs to Sheets. No Claude call.
3.1.5UploadClaude Haiku generates upload JSON β†’ Sheets/Notion creates page β†’ Chat response with card.
3.1.6Creation (3 sub-routes)Each creation type has its own Claude Haiku call with dedicated prompt. Tweet (6a), Graphic (6b), Video Short (6c). Each β†’ Chat response β†’ history log.
3.1.7Notion API executionExecutes the API call built by sub-routes 2/3/5. Deeply nested router handles card rendering: creates Chat message, then updates with cardsV2. Handles search results (multiple cards via feeder), edit confirmations, and upload confirmations.
3.1.8FallbackError/unknown intent response. Chat message + history log.

Intent Router β†’ Sub-Route Mapping

The Intent Router (Claude Haiku, module 269) outputs a workflow value. The Make.com router's filter conditions map these to sub-routes:

"notion"            β†’ 3.1.3 (search), 3.1.2 (edit), 3.1.5 (upload) β€” determined by Notion workflow prompt
"tweet creation"    β†’ 3.1.6a
"graphic creation"  β†’ 3.1.6b
"video short creation" β†’ 3.1.6c
"permissions"       β†’ 3.1.0
"button manager"    β†’ 3.1.1
"database sync"     β†’ 3.1.4
"notion user sync"  β†’ 3.0 (re-uses provisioning route)
"timezone manager"  β†’ 3.1.0 (handled within permissions route)
null + error_message β†’ 3.1.8 (fallback β€” error sent as Chat message)

πŸ–±οΈ

Chatbot Link Clicker

Trigger: Webhook (card button click)

Handles button clicks on Google Chat content cards. Receives: Notion page ID, action value, user secret code, database ID, message ID.

Flow

StepModuleDetails
1WebhookReceives click payload from Google Chat card button.
2Google SheetsLooks up user permissions by secret user code.
3Router (5 routes)Routes by action value.

Routes

RouteActionWhat It Does
0approvecontentUpdates Notion page Status β†’ Approved. Updates card in-place via cardsV2 updateMask.
1rejectcontentUpdates Notion page Status β†’ Rejected. Updates card in-place.
2scheduleNUpdates Notion page with calculated schedule time (current time + N minutes). Updates card in-place.
3suggestcontentFetches Notion page, sends it to the org's default Chat space. Space determined by database ID β†’ space ID conditional mapping.
4FallbackSends error message to Chat.
ℹ️
Card update mechanism: Every action route uses google-chat:makeApiCall with URL /v1/{messageid}?updateMask=cardsV2 to update the existing card rather than posting a new message. The messageid is URL-decoded from the webhook payload.

πŸ“₯

New Content Submission

Trigger: Webhook (Notion "Submit New Content" automation)

Processes new content when Type is set. Generates Name, Tags, descriptions, thumbnails, transcripts, and people tags. This is the content enrichment pipeline.

Flow

StepModuleDetails
1WebhookReceives full page data from Notion.
2–3Google Sheets (Γ—2)Loads space config and field mappings.
4Set VariableStages shared data for routing.
5Router (3 routes)Routes by content type + file presence.

Route 0 β€” File-Based Content (Graphics & Video)

Downloads file from File (public) β†’ uploads to Google Drive β†’ gets file metadata/thumbnail.

Sub-RouteTypeProcessing
0aGraphicClaude Haiku describes image β†’ downloads thumbnail β†’ uploads to WordPress (/wp-json/wp/v2/media) β†’ updates Notion page (Name, Tags, Internal Notes, Public Thumbnail, etc.).
0bVideo ShortCloudConvert extracts audio β†’ Whisper transcribes β†’ Claude Haiku summarizes transcript β†’ updates Notion page (Name, Tags, Internal Notes, Transcript, etc.).

Route 1 β€” Tweets (Text Only)

Claude Haiku processes text content β†’ generates Name, Tags, AI Summary β†’ updates Notion page via API.

Route 2 β€” People Tagging (All Types)

Runs for all content after the type-specific route:

  • Fetches the content page from Notion.
  • Searches the People in Content database for matches.
  • Aggregates results into text.
  • Claude Haiku determines if any people match the content.
  • If match found: updates existing People record with content relation. If new person: creates a new People in Content record, then updates the content page.

πŸ“…

Schedule from Status

Trigger: Webhook (Notion "Schedule Automation")

Handles status changes that trigger scheduling. Receives when any status is changed on a content page. Processes "Schedule -" status values into actual Scheduled Date values.

Flow

StepModuleDetails
1WebhookReceives full page data.
2Google SheetsPulls scheduling config β€” time slot rules, timezone offsets, next available slots.
3Router (2 routes)Routes based on status type.

Routes

RouteConditionWhat It Does
0Direct schedule (simple status change)Updates Notion page directly (e.g. Status β†’ Approved with no scheduling needed).
1"Schedule -" status detectedUpdates Google Sheets scheduling queue row β†’ looks up next available time slot from Sheets β†’ updates Notion page with Scheduled Date + Status β†’ Approved.
ℹ️
Time Interpreter sheet: The scheduling logic uses the Time Interpreter sheet to convert schedule status labels (e.g. "Schedule - Today 10:00 AM") into actual datetime values adjusted for the user's timezone.

πŸ“‘

Metricool Content Poster

Trigger: Webhook (Zoho Flow "Send_Webhook")

The final stage of the publishing pipeline. Receives a Notion page ID and Scheduled Date from Zoho Flow, fetches the full content, then publishes to all configured social networks. This is the most deeply nested scenario β€” ~50+ modules across multiple routers.

Flow

StepModuleDetails
1WebhookReceives Data_ID (Notion page ID) and Scheduled_Date_Start.
2Notion: Get PageFetches full page data.
3RouterTop-level split by org/content type.

Per-Network Publishing

The router fans out into deeply nested sub-routes. Each content type Γ— organization combination has its own set of metricool:scheduleAPost calls because per-network field requirements differ (image URLs, video files, caption formats, alt text, hashtags).

After all Metricool calls complete, each branch:

  • Creates a Scheduling History record in Notion (notion:createDataSourceItem).
  • Updates the content page via Notion API (/v1/pages/{id}) β€” sets Last Used date, updates status.
  • Sends confirmation to Google Chat via google-chat:makeApiCall β€” posts to the space ID looked up from Google Sheets.

YouTube Upload Branch

For Video Short content, dedicated branches fetch the video file from Google Drive (google-drive:getAFile) and upload directly to YouTube (youtube:uploadVideo). This runs alongside the Metricool posts, not instead of them.


πŸ‘€

Update Person in Content

Trigger: Webhook (Notion "Look for Social Media Handles")

Searches the web for a person's social media profiles and writes the URLs back to Notion.

Flow

StepModuleDetails
1WebhookReceives person data (Name, Bio, relation IDs).
2Serper (Γ—2)Two web searches β€” likely "[name] social media" and "[name] twitter/X".
3Claude HaikuProcesses search results, extracts social URLs from results.
4Notion SearchFinds the person's page.
5RouterUpdates existing record or creates new one with extracted URLs.

⏱️

Zoho Flow β€” Scheduling Delay System

Trigger: Webhook (Notion "Schedule Date From Status" automation)

Receives a Notion page ID when a Scheduled Date is set. Calculates the delay until the scheduled time, waits, then fires a webhook to the Metricool Content Poster scenario.

Flow Steps

Webhook receives page data
↓
Decision: Scheduled Date exists?
↓ Yes
Minutes_till_Scheduled() β†’ calculates delay
↓
Delay Until Scheduled (waits N minutes)
↓
Send_Webhook() β†’ fires Make.com Metricool Content Poster

Deluge Functions

Minutes_till_Scheduled(Current_Time, Scheduled_Time)

Parses both timestamps, calculates difference in minutes. Enforces a minimum of 4 minutes β€” if the calculated delay is less than 4, it's set to 4. This prevents instant-fire and gives users a cancellation window.

string Minutes_till_Scheduled(string Current_Time, string Scheduled_Time)
{
  cleanScheduled = Scheduled_Time.subString(0,19).replaceAll("T"," ");
  cleanCurrent = Current_Time.subString(0,19).replaceAll("T"," ");
  scheduledDT = cleanScheduled.toDateTime("yyyy-MM-dd HH:mm:ss");
  currentDT = cleanCurrent.toDateTime("yyyy-MM-dd HH:mm:ss");
  diffMs = scheduledDT.toLong() - currentDT.toLong();
  diffMinutes = (diffMs / 60000).toLong();
  if(diffMinutes < 4)
  {
    diffMinutes = 4;
  }
  return diffMinutes.toString() + " minutes";
}
Send_Webhook(NotionID, ScheduledDateStart)

Posts the Notion page ID and scheduled date to the Make.com Metricool Content Poster webhook.

string Send_Webhook(string NotionID, string ScheduledDateStart)
{
  payload = Map();
  payload.put("Data_ID", NotionID);
  payload.put("Scheduled_Date_Start", ScheduledDateStart);
  response = invokeurl
  [
    url: "https://hook.us1.make.com/i2bxdatn59m1b9beovm77p33elk90tcb"
    type: POST
    body: payload.toString()
    headers: {"Content-Type":"application/json"}
  ];
  return response;
}

🧠

Intent Router Prompt

Module 269 in Chatbot 1.1 claude-haiku-4-5-20251001

Determines which workflow handles each message. Outputs a JSON object with workflow, error_message_to_user, set_organization, thumbnail, and button state.

Key Logic

PriorityRule
1Organization check β€” runs first. Auto-detects org from space name. Locks org in dedicated spaces. Allows switching in DMs. Blocks all processing if org is null.
2Bystander detection β€” if message appears directed at humans rather than the bot (shared content, room-directed questions), logs it and responds neutrally.
3Workflow continuation β€” if previous Claude response was tagged with a workflow prefix and user's reply is a short follow-up, re-routes to same workflow without re-evaluating intent.
4Spelling/grammar check β€” if user uploads a graphic or video, extracts visible text and checks for errors. Blocks upload until user approves or fixes.
5Workflow routing β€” matches intent to one of 9 workflows based on keywords and context.
6Permission inline responses β€” "what are my permissions?" and "what's my timezone?" answered directly in error_message_to_user without routing to a workflow.
7Button state extraction β€” always parses Space Button Settings JSON and populates all 8 button fields regardless of routing outcome.

Workflow Values

"notion" Β· "tweet creation" Β· "video short creation" Β· "graphic creation" Β· "permissions" Β· "button manager" Β· "database sync" Β· "notion user sync" Β· "timezone manager" Β· null (with error message)

Routing Disambiguation

Graphic attached + no help request β†’ notion (upload). Graphic attached + help request β†’ graphic creation. "sync" + "user/users" β†’ notion user sync. "sync" without "user" β†’ database sync. Unclear content type β†’ defaults to tweet creation.

Output Schema

{
  "workflow": string | null,
  "error_message_to_user": string | null,
  "set_organization": string | null,
  "Public_Thumbnail": string | null,
  "Button1_Name": string | null,
  "Button1_Action": string | null,
  "Button2_Name": string | null,
  "Button2_Action": string | null,
  "Button3_Name": string | null,
  "Button3_Action": string | null,
  "Button4_Name": string | null,
  "Button4_Action": string | null
}

🧠

Notion Workflow Prompt

Module 293 in Chatbot 1.1 claude-haiku-4-5-20251001

Handles search, edit, and upload actions against the Notion content database. Outputs a complete Notion API request body ready to execute.

Key Logic

AreaBehavior
Permission enforcementChecks Search/Editing/Uploading/Scheduling permissions. Blocks the entire action if required permission is missing. Scheduling permission only strips the date field (doesn't block the rest).
Action detectionDetermines search vs edit vs upload from the most recent message. Extracts Notion page IDs from URLs (reformats to 8-4-4-4-12 dash pattern).
ConfirmationEdits require explicit confirmation. Uploads and searches proceed immediately.
Search filtersDefault status filter: Approved. "And" logic = each keyword in its own OR block inside AND. "Or" logic = all keywords in single OR block. Scheduled date searches: is_not_empty + ascending sort + limit 100.
Auto-statusSetting Scheduled Date on edit β†’ Status auto-set to Approved. Upload with Scheduled Date β†’ Approved. Upload without β†’ Needs Review.
Body constructionOutputs complete body_json string with Notion API property formats. Search: filter + page_size. Edit: properties object. Upload: parent + properties.

Output Schema

{
  "action": "search" | "edit" | "upload" | null,
  "API_URL": string | null,
  "Method": "POST" | "PATCH" | null,
  "Scheduled Date": string | null,
  "Public_Thumbnail": string | null,
  "body_json": string,
  "question_for_user": string | null
}

🧠

Tweet Creation Prompt

Module 450 in Chatbot 1.1 (Route 3.1.6a) claude-haiku-4-5-20251001

Writes tweets in Project Liberal's voice. Identifies task type (new/rewrite/alternatives/refine), content category, style, and tone.

Key Rules

Voice: Confident classical liberal. Uses "liberal" not progressive/centrist. Opponents labeled "illiberal"/"MAGA"/"authoritarian" not "Republican"/"GOP". No progressive buzzwords. No doom without confidence.

Length: Default under 100 chars. Max 200 unless style requires more. Median ~78 chars for punchy styles.

Formatting: Double line breaks for dramatic pause. ALL CAPS sparingly (single words). 0–2 emoji max. 0–1 hashtag max.

8 tweet styles: punchy_declarative, quote_attribution, contrast_hypocrisy, you_cant_be, question_led, to_be_is_to_be, historical_parallel, sarcastic_mockery.

Output Schema

{
  "reasoning": string,
  "identified_task": "new" | "rewrite" | "alternatives" | "refine" | "unclear",
  "identified_style": string,
  "identified_tone": string,
  "identified_category": string,
  "tweet_output": string | null,
  "message_to_user": string
}

🧠

Graphic Creation Prompt

Module 453 in Chatbot 1.1 (Route 3.1.6b) claude-haiku-4-5-20251001

Generates graphic concepts: visual format, exact text, image-text relationship, branding notes. Identifies task type (new_concept/write_text/refine/alternatives/help_with_attached).

Key Rules

Every graphic must make a specific argument β€” not just express a mood. Must frame from classical liberal principles.

Primary text target: under 120 characters. Uses specific numbers/data over vague claims.

Meme template rules: Drake = preference contrasts, Distracted Boyfriend = misplaced priorities, Wojak = in-group dynamics, NPC = mindless talking points, etc. Format must match argument structure.

Each concept delivers 5 components: graphic concept, primary text, supporting text, branding note (@projectliberal), why it works.

Output Schema

{
  "reasoning": string,
  "identified_task": string,
  "identified_style": string,
  "identified_tone": string,
  "identified_category": string,
  "graphic_output": string | null,
  "message_to_user": string
}

🧠

Video Short Creation Prompt

Module 456 in Chatbot 1.1 (Route 3.1.6c) claude-haiku-4-5-20251001

Writes spoken-word scripts. 3–5 beat structure: hook β†’ core fact β†’ context/evidence β†’ escalation/stakes β†’ closing punch. Identifies structure, tone, and target length.

Key Rules

Hook rules: First sentence must be bold declarative, provocative question, counterintuitive framing, or direct challenge. No preamble ever.

Closing rules: Warning, mic-drop, rhetorical question, principle affirmation, or callback. Never a CTA or summary.

8 script structures: breaking_news_analysis, hook_first_escalation, contrast_reversal, data_led_argument, rapid_fire_rhetorical, direct_address_response, storytelling_human_interest, philosophical_essay.

3 length brackets: Short (150–250 words), Standard (250–400), Long (400–600).

Visual directions: inline bracketed (e.g. [ON SCREEN: $330 billion / year]) only where they add value.

Output Schema

{
  "reasoning": string,
  "identified_task": string,
  "identified_structure": string,
  "identified_tone": string,
  "identified_category": string,
  "target_length": "short" | "standard" | "long",
  "script_output": string | null,
  "message_to_user": string
}

🧠

Permissions Prompt

Module 469 in Chatbot 1.1 (Route 3.1.0) claude-haiku-4-5-20251001

Manages user permissions. View is open to all. Modify requires Admin. Admin Permission itself cannot be changed via chatbot. All changes require confirmation. Outputs current permission values for the target user.

Managed Permissions

Search Β· Scheduling Β· Editing Β· Uploading Β· Creation (not yet implemented in routing)

Admin β€” read-only via this prompt. Cannot be modified.

Output Schema

{
  "User ID": string | null,
  "Admin Permission": boolean,
  "Search Permission": boolean,
  "Scheduling Permission": boolean,
  "Editing Permission": boolean,
  "Uploading Permission": boolean,
  "Creation Permission": boolean,
  "Success Message to User": string | null,
  "Questions to User": string | null
}

🧠

Button Manager Prompt

Module 418 in Chatbot 1.1 (Route 3.1.1) claude-haiku-4-5-20251001

Manages the 4-button configuration and available actions list per space. Stores config as minified JSON in Google Sheets.

Key Rules

Buttons must have both name + action, or neither. Actions must exist in available_actions before assignment. Can't remove an action assigned to a button. At least one action must remain. Schedule actions require minutes value. Outputs validated JSON string.

Output Schema

{
  "reasoning": string,
  "button_settings": "{\"buttons\":[...],\"available_actions\":[...]}",
  "error_message_to_user": string | null,
  "success_message_to_user": string | null
}

πŸ“Š

Google Sheets β€” Schema Reference

9 sheets in the ChatBot workbook. Sheets serve as the configuration layer, state store, and history log for the chatbot system.

Messages

Chat history log. One row per message (user or bot). Used to load last 10 messages as context for each chatbot execution.

Columns: Date/Time, Message, Space ID, Message ID, Thread ID, Attachment ID, User ID

Space Settings

Per-space configuration. One row per Google Chat space. Stores org assignment, Notion database ID, button configuration, and blocked flag.

Columns: Date, Space ID, Name, Organization, Notion ID, Notes, Space Instructions, Organization Short Name, Blocked, temp2, temp3, Button Settings (JSON)

User Settings

Per-user permission and config data. Users have separate rows per org β€” one for Action, one for Foundation. Permissions are org-scoped.

Columns: Date Added, User Name, Organization, User Google ID, Secret User Code, Notion Database ID, Admin Permission, Search Permission, Scheduling Permission, Editing Permission, Uploading Permission, Creation Permission, Email, User Timezone, Notion User ID, Chatbot DM Space

Organization Settings

Org-level configuration. One row per org (Action, Foundation, Liberal Memes).

Columns: Organization Name, Notion ID, Organization Short Name, Default Chat Space, Content to Socials Alert (boolean), New Content Alert (boolean)

ChatGPT Prompts

Legacy/older prompt versions for tweet and video short creation. Contains the full Tags list and Script Training Data corpus used by the v1 creation workflows. Note: The v1.2 prompts (currently active) are injected directly in Make.com module configs, not read from this sheet.

Columns: Pathway, Prompt/Result, Tags, Video Short Scripts, Defunct

Info for Make.com

Single-row reference data fed to Make.com scenarios. Contains the full Tags list and Script Training Data corpus.

Columns: Line Item, Tags, Script Training Data

Thumbnail Links

Fallback thumbnail URLs used when content doesn't have its own thumbnail.

Columns: Phoenix (default PL logo), Twitter (Twitter/X icon), Video file Unavailable (video placeholder)

Database Sync

Field mapping table used by the Database Sync workflow. Maps Notion property names to types, sync flags, Claude permissions, and per-database property IDs.

Columns: Property Name, Types, Sync (boolean), Claude Permissions, pl_foundation_id, liberal_memes_id, pl_action_id

Time Interpreter

Scheduling time calculation sheet. Converts schedule status labels into actual datetime values. Used by the Schedule from Status scenario to determine target times relative to today's date and the user's timezone.

Columns: Time from Notion, Fields, Today's Date, Time, Date Output (+ additional calculation columns)


πŸ—ƒοΈ

Content Databases

Two mirrored databases: PL Action Content (29 properties, ~2,100 rows) and PL Foundation Content (28 properties β€” same minus Created time).

Full Property List

PropertyTypeSource
Name - DescriptionTitleSystem (New Content Submission)
TypeSelectUser (triggers automation)
StatusSelectUser / System
Caption (public)Rich textUser
Description (public)Rich textUser
Video Title (public)Rich textUser
TranscriptRich textSystem (Whisper via New Content Submission)
TagsMulti-selectSystem (New Content Submission)
Full Tags listFormulaAuto
File (public)FilesUser
Canva LinkURLUser
Public ThumbnailURLSystem (WordPress upload)
Internal NotesRich textUser + System
Twitter Image Alt TextRich textUser
Instagram HashtagsRich textUser
Scheduled DateDateUser / System (Schedule from Status)
Submission DateDateSystem
Last UsedDateSystem (Metricool Content Poster)
Created ByTextSystem
Created timeCreated timeAuto (Action only)
Evergreen RatingTextManual
TempRich textUser
TEMPORARY - Person In ContentTextLegacy
Unique IDTextSystem
Google File IDTextSystem
Google Drive View LinkFormulaAuto
Google Drive Download LinkFormulaAuto
People in ContentRelationUser / System
Scheduling HistoryRelationSystem

πŸ—ƒοΈ

People in Content Database

23 properties. Stores individuals referenced in content with per-platform social URLs and auto-extracted handles.

PropertyTypeNotes
NameTitlePerson's name.
BioRich textBrief bio.
Identifying infoRich textOrgs, titles, other identifying context.
StatusSelect"Search for handles" triggers lookup automation.
WikipediaURLWikipedia link if available.
Twitter URL / HandleURL / FormulaHandle extracted via regex formula.
Threads URL / HandleURL / FormulaSame pattern.
Bluesky URL / HandleURL / FormulaSame pattern.
Facebook URL / HandleURL / FormulaSame pattern.
Instagram URL / HandleURL / FormulaSame pattern.
TikTok URL / HandleURL / FormulaSame pattern.
YouTube URL / HandleURL / FormulaSame pattern.
PL Action ContentRelationContent referencing this person.
PL Foundation ContentRelationContent referencing this person.

πŸ—ƒοΈ

Scheduling History Database

10 properties. One record per social media publication event, created by the Metricool Content Poster scenario.

PropertyTypeNotes
NameTitleAuto-generated: date + org + content name.
OrganizationSelectWhich org published this.
TypeSelectTweet, Graphic, or Video Short.
Scheduled DateDateWhen published.
TimestampTextExact timestamp.
24 Twitter PerformanceTextMetrics after 24 hours.
1 Week Twitter PerformanceTextMetrics after 1 week.
PL Action ContentRelationBack-link to source content.
PL Foundation ContentRelationBack-link to source content.

πŸ“‘

Networks & Metricool Configuration

Connected Networks by Org

NetworkAction AccountFoundation Account
Twitter / X@ProjectLiberal@ProjectLiberalF
FacebookProject Liberal (Page)Project Liberal Foundation (Page)
Instagram@projectliberal (Pro)@liberalfoundation (Pro)
Threads@projectliberal@liberalfoundation
Blueskyprojectliberal.orgliberalfoundation.bsky.social
TikTok@projectliberal (Business)@liberalfoundation (Business)
YouTubeProject LiberalProject Liberal Foundation
LinkedInProject Liberal Actionβ€”
PinterestProjectLiberal (Personal)β€”
Twitch@projectliberalβ€”
Web/Blogprojectliberal.org + RSSβ€”

Per-Network Publishing Notes

Each metricool:scheduleAPost call is configured per-network because field requirements differ: Instagram requires an image URL, YouTube requires video file + title, Twitter has character limits + alt text, TikTok only accepts video, etc. This is why the Metricool Content Poster has so many modules β€” each content type Γ— network is a separate call.


🌐

WordPress Media Integration

Thumbnails are uploaded to projectliberal.org via the WordPress REST API. This provides stable public URLs for content card images in Google Chat.

How It Works

When a graphic is processed by New Content Submission or the Chatbot:

  • Image is uploaded to Google Drive β†’ thumbnail link extracted from Drive metadata.
  • Thumbnail downloaded from Drive.
  • Thumbnail uploaded to WordPress via POST /wp-json/wp/v2/media.
  • WordPress returns a public URL (e.g. https://projectliberal.org/wp-content/uploads/...).
  • URL stored in the Notion page's Public Thumbnail property.

Fallback thumbnails (when no image exists) are stored in the Thumbnail Links sheet: a PL logo, a Twitter icon, and a video placeholder.