I Built a Creator Command Center with Coral β Here's the Exact Route π΄ββ οΈ
I Built a Creator Command Center with Coral β Here's the Exact Route π΄ββ οΈ
Query your YouTube, Twitter & Discord as SQL. No ETL. No warehouse. Just Coral.
I'm a developer and content creator, and I was tired of the same daily ritual β open YouTube, check views, switch to Twitter, check engagement, hop to Discord, see what the community is asking. Three tabs. Three APIs. Zero unified picture.
So for the Pirates of the Coral-bean Hackathon by WeMakeDevs, I built something I actually needed: a Creator Command Center β a personal agent that queries all three platforms as SQL using Coral, and spits out a live dashboard.
This is the exact route I took. Follow it and you'll have your own running in under a day.
πΊοΈ What We're Building
A Python agent that:
Connects YouTube and Twitter as SQL sources via Coral
Runs cross-platform queries like
SELECT ... FROM youtube.videos JOIN twitter.tweetsGenerates a live HTML dashboard from the results
Runs inside Docker β works on Windows, Mac, Linux
Live demo: creator-command-center-seven.vercel.app
Source code: github.com/Soumya9107/creator-command-center
π οΈ Prerequisites
Docker Desktop installed
A YouTube channel (even with 1 video)
A Twitter/X account
A Discord account
About 2β3 hours of focused time
Step 1 β Project Setup
Create your project structure:
mkdir creator-command-center
cd creator-command-center
mkdir sources, queries, agent
Create a Dockerfile:
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y \
curl python3 python3-pip \
&& rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://withcoral.com/install.sh | sh
RUN pip3 install groq --break-system-packages
WORKDIR /app
COPY . .
ENV PATH="/root/.local/bin:$PATH"
CMD ["bash"]
Create docker-compose.yml:
services:
coral-agent:
build: .
env_file:
- .env
volumes:
- .:/app
- coral-config:/root/.config/coral
stdin_open: true
tty: true
volumes:
coral-config:
Build and enter the container:
docker-compose build
docker-compose run coral-agent bash
Verify Coral is working:
coral --version
# coral 0.4.1+43d8309
Step 2 β Get Your API Keys
You need four things:
| Key | Where to get it |
|---|---|
YOUTUBE_API_KEY |
console.cloud.google.com β Enable YouTube Data API v3 β Credentials β API Key |
YOUTUBE_CHANNEL_ID |
Your YouTube channel URL β copy the UCxxxxxxx part |
TWITTER_BEARER_TOKEN |
developer.twitter.com β App β Keys & Tokens β Bearer Token |
TWITTER_USER_ID |
Go to tweeterid.com β enter your username β get numeric ID |
DISCORD_BOT_TOKEN |
discord.com/developers/applications β New App β Bot β Reset Token |
GROQ_API_KEY |
console.groq.com β API Keys β Create (free!) |
Create .env in your project root:
YOUTUBE_API_KEY=your_key_here
YOUTUBE_CHANNEL_ID=UCxxxxxxxxxxxxxxx
TWITTER_BEARER_TOKEN=your_bearer_token_here
TWITTER_USER_ID=your_numeric_id_here
DISCORD_BOT_TOKEN=your_token_here
DISCORD_GUILD_ID=your_server_id
DISCORD_CHANNEL_ID=your_channel_id
GROQ_API_KEY=your_key_here
β οΈ Important: Add
.envto.gitignoreright away β never commit API keys!
Step 3 β Write Custom Source Specs
This is where Coral shines. You define any API as a YAML file and Coral exposes it as SQL.
Create sources/youtube.yaml:
name: youtube
version: 0.1.0
dsl_version: 3
backend: http
base_url: https://www.googleapis.com/youtube/v3
inputs:
YOUTUBE_API_KEY:
kind: secret
hint: YouTube Data API v3 key
YOUTUBE_CHANNEL_ID:
kind: variable
hint: Your YouTube Channel ID
tables:
- name: videos
description: Videos from your YouTube channel
request:
method: GET
path: /search
query:
- name: part
from: literal
value: snippet
- name: type
from: literal
value: video
- name: channelId
from: template
template: "{{input.YOUTUBE_CHANNEL_ID}}"
- name: maxResults
from: literal
value: "50"
- name: key
from: template
template: "{{input.YOUTUBE_API_KEY}}"
response:
rows_path:
- items
columns:
- name: id__videoId
type: Utf8
- name: snippet__title
type: Utf8
- name: snippet__publishedAt
type: Utf8
- name: snippet__channelTitle
type: Utf8
Create sources/twitter.yaml:
name: twitter
version: 0.1.0
dsl_version: 3
backend: http
base_url: https://api.twitter.com/2
inputs:
TWITTER_BEARER_TOKEN:
kind: secret
hint: Twitter API v2 Bearer Token from developer.twitter.com
TWITTER_USER_ID:
kind: variable
hint: Your numeric Twitter User ID
auth:
type: HeaderAuth
headers:
- name: Authorization
from: template
template: "Bearer {{input.TWITTER_BEARER_TOKEN}}"
tables:
- name: tweets
description: Your recent tweets with engagement metrics
request:
method: GET
path: "/users/{{input.TWITTER_USER_ID}}/tweets"
query:
- name: max_results
from: literal
value: "100"
- name: tweet.fields
from: literal
value: "public_metrics,created_at,text"
response:
rows_path:
- data
columns:
- name: id
type: Utf8
- name: text
type: Utf8
- name: created_at
type: Utf8
- name: mentions
description: Recent mentions of your account
request:
method: GET
path: "/users/{{input.TWITTER_USER_ID}}/mentions"
query:
- name: max_results
from: literal
value: "100"
- name: tweet.fields
from: literal
value: "public_metrics,created_at,text"
response:
rows_path:
- data
columns:
- name: id
type: Utf8
- name: text
type: Utf8
- name: created_at
type: Utf8
β οΈ Twitter API Note: Twitter's free tier has very limited read credits. If you hit a
CreditsDepletederror, use the mock data fallback below β it still demonstrates the cross-source SQL perfectly.
Twitter mock data fallback β create sources/twitter_mock.json:
[
{"id": "1", "text": "Just posted a new video! Check it out", "created_at": "2026-05-28T10:00:00Z", "likes": 42, "retweets": 12},
{"id": "2", "text": "Working on something exciting for my audience", "created_at": "2026-05-27T10:00:00Z", "likes": 38, "retweets": 8},
{"id": "3", "text": "What content do you want to see next?", "created_at": "2026-05-26T10:00:00Z", "likes": 55, "retweets": 15},
{"id": "4", "text": "Behind the scenes of my latest project", "created_at": "2026-05-25T10:00:00Z", "likes": 61, "retweets": 20},
{"id": "5", "text": "Thanks for 1000 followers! More content coming soon", "created_at": "2026-05-24T10:00:00Z", "likes": 89, "retweets": 30}
]
And sources/twitter_local.yaml:
name: twitter_local
version: 0.1.0
dsl_version: 3
backend: file
tables:
- name: tweets
description: Twitter tweets (local mock)
format: json
source:
location: file:///app/sources/
glob: "twitter_mock.json"
columns:
- name: id
type: Utf8
- name: text
type: Utf8
- name: created_at
type: Utf8
- name: likes
type: Int64
- name: retweets
type: Int64
Create sources/discord.yaml:
name: discord
version: 0.1.0
dsl_version: 3
backend: http
base_url: https://discord.com/api/v10
inputs:
DISCORD_BOT_TOKEN:
kind: secret
hint: Discord Bot Token
DISCORD_GUILD_ID:
kind: variable
hint: Your Discord Server ID
DISCORD_CHANNEL_ID:
kind: variable
hint: Your Discord Channel ID
auth:
type: HeaderAuth
headers:
- name: Authorization
from: template
template: "Bot {{input.DISCORD_BOT_TOKEN}}"
tables:
- name: messages
description: Messages from your Discord channel
request:
method: GET
path: "/channels/{{input.DISCORD_CHANNEL_ID}}/messages"
query:
- name: limit
from: literal
value: "100"
response:
rows_path:
- $
columns:
- name: id
type: Utf8
- name: content
type: Utf8
- name: timestamp
type: Utf8
Lint to validate:
coral source lint /app/sources/youtube.yaml
# Manifest is valid β
Step 4 β Connect the Sources
Load your environment and add sources:
# Fix Windows line endings first
sed -i 's/\r//' /app/.env
# Load env vars
set -a && source /app/.env && set +a
# Add sources
coral source add --file /app/sources/youtube.yaml
coral source add --file /app/sources/twitter_local.yaml # use twitter.yaml if you have API credits
coral source add --file /app/sources/discord.yaml
# Fix variable storage (important!)
printf 'YOUTUBE_CHANNEL_ID=%s\n' "$YOUTUBE_CHANNEL_ID" \
> /root/.config/coral/workspaces/default/sources/youtube/variables.env
printf 'DISCORD_GUILD_ID=%s\nDISCORD_CHANNEL_ID=%s\n' \
"\(DISCORD_GUILD_ID" "\)DISCORD_CHANNEL_ID" \
> /root/.config/coral/workspaces/default/sources/discord/variables.env
Verify it works:
coral source list
# discord 0.1.0 imported file (plaintext)
# twitter_local 0.1.0 imported file (plaintext)
# youtube 0.1.0 imported file (plaintext)
Step 5 β Write the SQL Queries
Create queries/weekly_summary.sql:
SELECT
'YouTube' AS platform,
title AS content,
likes AS engagement
FROM youtube_local.videos
UNION ALL
SELECT
'Twitter' AS platform,
text AS content,
likes AS engagement
FROM twitter_local.tweets
ORDER BY engagement DESC
LIMIT 10;
Test it:
coral sql < /app/queries/weekly_summary.sql
You'll see results from both platforms ranked by engagement β that cross-source JOIN is the whole magic of Coral.
Step 6 β Build the Agent & Dashboard
Create agent/main.py:
import subprocess
def run_coral_query(sql):
result = subprocess.run(
["coral", "sql", sql],
capture_output=True, text=True
)
return result.stdout if result.returncode == 0 else ""
def parse_table(output):
rows = []
lines = output.strip().split('\n')
data_lines = [l for l in lines if l.startswith('|') and '---' not in l]
if len(data_lines) < 2:
return [], []
headers = [h.strip() for h in data_lines[0].split('|')[1:-1]]
for line in data_lines[1:]:
values = [v.strip() for v in line.split('|')[1:-1]]
if values:
rows.append(values)
return headers, rows
queries = [
{
"question": "Most viewed videos",
"sql": "SELECT title, views, likes FROM youtube_local.videos ORDER BY views DESC LIMIT 5"
},
{
"question": "Top tweets by likes",
"sql": "SELECT text, likes, retweets FROM twitter_local.tweets ORDER BY likes DESC LIMIT 5"
},
{
"question": "Cross-platform engagement",
"sql": """
SELECT 'YouTube' AS platform, title AS content, likes AS engagement
FROM youtube_local.videos
UNION ALL
SELECT 'Twitter' AS platform, text AS content, likes AS engagement
FROM twitter_local.tweets
ORDER BY engagement DESC LIMIT 10
"""
}
]
if __name__ == "__main__":
print("\nπ΄ββ οΈ Creator Command Center\n" + "="*40)
for q in queries:
print(f"\nπ€ {q['question']}")
print(run_coral_query(q['sql']))
Run it:
python3 /app/agent/main.py
Step 7 β Deploy
Push to GitHub:
# Add a .gitignore first!
echo ".env" > .gitignore
git init
git add .
git commit -m "Initial commit - Creator Command Center"
git remote add origin https://github.com/YOUR_USERNAME/creator-command-center.git
git push -u origin main
Deploy to Vercel:
Go to vercel.com β Sign in with GitHub
Click Add New Project β Import your repo
Framework: Other β Click Deploy
Your live URL:
https://creator-command-center.vercel.app
π‘ What I Learned
1. Coral's DSL is opinionated β in a good way. Every field has a reason. The from: literal vs from: template distinction forces you to be explicit about what's static and what's dynamic. It caught bugs I didn't know I had.
2. Docker + Windows = line ending hell. The \r carriage return issue cost me an hour. Always run sed -i 's/\r//' .env before loading env vars in a Linux container on Windows.
3. API free tiers are fragile. Twitter's free tier ran out of credits mid-build. Lesson: always have a mock data fallback. Real engineering means handling real constraints.
4. Cross-source SQL is genuinely magical. The moment UNION ALL across YouTube and Twitter returned results in one table β that's when it clicked. This is what Coral is actually for.
π Bounty Tip
Submit your source specs as PRs to the Coral GitHub repo. Each accepted custom source spec earns \(100 cash. YouTube, Twitter, and Discord are three separate PRs β potentially \)300 in bounties just from the YAML files you already wrote!
π Resources
Built by Soumyajit Maity for the Piratesπ΄ββ οΈ of the Coral-bean Hackathon Β· WeMakeDevs 2026
