External Integrations Guide
Complete guide for setting up and configuring all external service integrations for the AI Agentification Platform.
Table of Contents​
- YouTube Data API
- Email Service (IONOS SMTP)
- Cloudinary Image Upload
- Newsletter Service
- Supabase Realtime
- Environment Variables Summary
YouTube Data API​
Overview​
The YouTube Data API is used to fetch video metadata (title, duration, thumbnails) for course lessons.
Benefits​
- 10,000 quota units/day (free)
- 1 video fetch = 1 unit (can fetch 10,000 videos/day)
- Automatic metadata extraction
- No manual data entry for video info
Setup Steps​
1. Create Google Cloud Project​
- Go to Google Cloud Console
- Sign in with your Google account
- Click Select a project → NEW PROJECT
- Enter project name:
Agentification Academy - Click CREATE
2. Enable YouTube Data API v3​
- Make sure your project is selected
- Go to APIs & Services → Library
- Search for:
YouTube Data API v3 - Click ENABLE
3. Create API Key​
- Go to APIs & Services → Credentials
- Click + CREATE CREDENTIALS
- Select API key
- Copy the generated API key
4. Restrict the API Key (Recommended)​
- Click the Edit icon next to your API key
- Under API restrictions:
- Select Restrict key
- Check YouTube Data API v3 only
- Under Application restrictions:
- Select HTTP referrers (web sites)
- Add your domains:
http://localhost:*https://your-domain.com/*https://*.vercel.app/*
- Click SAVE
5. Add to Environment Variables​
Local Development (.env):
VITE_YOUTUBE_API_KEY=AIzaSyD_YOUR_ACTUAL_KEY_HERE
Production (Vercel):
- Go to Vercel Dashboard → Settings → Environment Variables
- Add:
- Name:
VITE_YOUTUBE_API_KEY - Value: Your API key
- Environments: Production, Preview, Development
- Name:
- Redeploy
Testing​
# Test the API key
curl "https://www.googleapis.com/youtube/v3/videos?id=dQw4w9WgXcQ&part=snippet,contentDetails&key=YOUR_API_KEY"
Expected: JSON response with video details
Quota Information​
| Limit | Free Tier |
|---|---|
| Daily quota | 10,000 units |
| Cost per video fetch | 1 unit |
| Videos per day | 10,000 |
| Reset time | Midnight Pacific Time |
Troubleshooting​
"API key not valid"
- Verify you copied the entire key
- Check for extra spaces
- Ensure key hasn't been deleted
"Quota exceeded"
- Check usage in Google Cloud Console
- Wait for daily reset
- Request quota increase if needed
"Access Not Configured"
- Ensure YouTube Data API v3 is enabled
- Wait a few minutes after enabling
- Try creating a new API key
Email Service (IONOS SMTP)​
Overview​
Contact form emails are sent via IONOS email hosting using SMTP.
SMTP Settings​
Server: smtp.ionos.co.uk
Port: 587 (TLS)
Authentication: Required
Setup Steps​
1. Get IONOS Email Credentials​
You need:
- Your IONOS email address (e.g.,
noreply@agentification.co.uk) - Your IONOS email password
2. Configure Environment Variables​
Local Development (env.development):
# Email Configuration
EMAIL_ENABLED=true
EMAIL_HOST=smtp.ionos.co.uk
EMAIL_PORT=587
EMAIL_USER=your-email@agentification.co.uk
EMAIL_PASSWORD=your-password
EMAIL_FROM=noreply@agentification.co.uk
EMAIL_TO=info@agentification.co.uk
Production (Vercel):
EMAIL_ENABLED=true
EMAIL_HOST=smtp.ionos.co.uk
EMAIL_PORT=587
EMAIL_USER=your-email@agentification.co.uk
EMAIL_PASSWORD=your-password
EMAIL_FROM=noreply@agentification.co.uk
EMAIL_TO=info@agentification.co.uk
3. Restart Server​
# Stop current server (Ctrl+C)
# Restart
python main.py
4. Test Contact Form​
- Go to
http://localhost:5173/contact - Fill out and submit the form
- Check terminal for email status
- Check inbox at
EMAIL_TOaddress
Email Format​
Subject:
Contact Form: [User's Subject]
Body:
New contact form submission from Agentification website:
Name: John Doe
Email: john@example.com
Subject: Question about courses
Message:
I'd like to know more about your courses...
---
This email was sent from the Agentification contact form.
Reply to: john@example.com
Testing Modes​
Development Mode (No Emails)​
EMAIL_ENABLED=false
- Logs contact submissions to console only
- No emails sent
- Good for testing without sending real emails
Console output:
============================================================
📧 EMAIL SIMULATION (EMAIL_ENABLED=false)
============================================================
From: John Doe <john@example.com>
Subject: Question about courses
Message: I'd like to know more about your courses...
============================================================
Production Mode (Real Emails)​
EMAIL_ENABLED=true
- Connects to IONOS SMTP
- Sends actual emails
- Logs success/failure
Console output:
📧 Connecting to smtp.ionos.co.uk:587...
🔒 Starting TLS...
🔑 Authenticating as noreply@agentification.co.uk...
📤 Sending email to info@agentification.co.uk...
✅ Email sent successfully!
Troubleshooting​
"SMTP Authentication failed"
- Double-check IONOS credentials
- Use full email address as
EMAIL_USER - Verify password has no typos
"Connection timeout"
- Check if port 587 is open
- Try different network
- Contact IONOS support
Emails not arriving
- Verify
EMAIL_ENABLED=true - Check spam folder
- Verify
EMAIL_TOaddress - Check server logs for "✅ Email sent successfully!"
Cloudinary Image Upload​
Overview​
Cloudinary provides image hosting with automatic optimization for blog content and media uploads.
Benefits​
- 25 GB free storage (vs 1 GB on Supabase)
- 25 GB bandwidth/month
- Automatic WebP conversion
- CDN delivery
- Image transformations (resize, crop, filters)
Setup Steps​
1. Create Cloudinary Account​
- Go to cloudinary.com/register
- Sign up for free account
- Verify email address
2. Get Cloud Name​
- After logging in, find your Cloud Name on dashboard
- Example:
dxyz123abcoryour-company-name - Copy this value
3. Create Upload Preset​
- Go to Settings → Upload
- Scroll to Upload presets
- Click Add upload preset
- Configure:
- Preset name:
blog-uploads - Signing Mode: Unsigned (required for client-side)
- Folder:
blog-images - Upload Manipulations:
- Format:
auto(WebP conversion) - Quality:
auto:good
- Format:
- Access Mode:
Public
- Preset name:
- Click Save
4. Add Environment Variables​
Local Development (.env):
VITE_CLOUDINARY_CLOUD_NAME=your-cloud-name
VITE_CLOUDINARY_UPLOAD_PRESET=blog-uploads
Production (Vercel):
- Vercel Dashboard → Settings → Environment Variables
- Add
VITE_CLOUDINARY_CLOUD_NAMEandVITE_CLOUDINARY_UPLOAD_PRESET - Apply to all environments
- Redeploy
Testing​
- Start dev server:
npm run dev - Go to
/admin/content/new - Click image icon in editor
- Upload an image
- Check Cloudinary dashboard → Media Library
Custom Folders​
Organize images by type:
// Featured images
<ImageUpload folder="blog-featured-images" />
// Inline content images
uploadImageToCloudinary(file, {
folder: 'blog-content-images'
})
Image Transformations​
// Resize to max 1200px width
const result = await uploadImageToCloudinary(file, {
folder: 'blog-images',
transformations: {
width: 1200,
quality: 'auto:good',
format: 'auto'
}
});
Usage Limits (Free Tier)​
| Resource | Free Tier |
|---|---|
| Storage | 25 GB |
| Bandwidth | 25 GB/month |
| Transformations | 25,000/month |
| Uploads | Unlimited |
Upgrade to Pro ($99/month):
- 250 GB storage
- 250 GB bandwidth
- Advanced features
Troubleshooting​
"Cloudinary configuration missing"
- Check
.envfile exists - Verify variable names start with
VITE_ - Restart dev server
Upload fails with 401/403
- Verify cloud name matches dashboard
- Check upload preset is Unsigned
- Ensure preset name matches exactly
Images not appearing
- Check browser console
- Verify image URL is accessible
- Check Cloudinary dashboard
Newsletter Service​
Overview​
Newsletter subscription system stores subscriber emails in Supabase with proper RLS policies.
Features​
- Email storage in Supabase
- Privacy-protected (RLS policies)
- Subscription status tracking
- Source attribution
- Reactivation support
Database Setup​
1. Apply Migration​
Run in Supabase SQL Editor:
-- From: supabase/migrations/20251103_newsletter_subscribers.sql
CREATE TABLE newsletter_subscribers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
status TEXT DEFAULT 'active',
source TEXT DEFAULT 'footer',
subscribed_at TIMESTAMPTZ DEFAULT NOW(),
unsubscribed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Enable RLS
ALTER TABLE newsletter_subscribers ENABLE ROW LEVEL SECURITY;
-- Policy: Allow anonymous to subscribe
CREATE POLICY "Anyone can subscribe"
ON newsletter_subscribers FOR INSERT
WITH CHECK (true);
-- Policy: Service role can read all
CREATE POLICY "Service role can read all"
ON newsletter_subscribers FOR SELECT
USING (auth.jwt() ->> 'role' = 'service_role');
2. Verify Table Creation​
SELECT * FROM newsletter_subscribers LIMIT 1;
API Endpoints​
Subscribe to Newsletter​
POST /api/newsletter/subscribe
Content-Type: application/json
{
"email": "user@example.com",
"source": "footer"
}
Response (201 Created):
{
"success": true,
"message": "Successfully subscribed to newsletter!",
"subscriber": {
"email": "user@example.com",
"subscribed_at": "2025-11-03T12:00:00Z"
}
}
Check Subscription Status​
GET /api/newsletter/check?email=user@example.com
Response:
{
"subscribed": true,
"status": "active",
"subscribed_at": "2025-11-03T12:00:00Z"
}
Unsubscribe​
POST /api/newsletter/unsubscribe
Content-Type: application/json
{
"email": "user@example.com"
}
Frontend Integration​
import { subscribeToNewsletter } from '../src/services/newsletter';
// Subscribe
try {
const response = await subscribeToNewsletter({
email: 'user@example.com',
source: 'footer'
});
console.log('Subscribed:', response);
} catch (error) {
console.error('Failed:', error.message);
}
Exporting Subscribers​
-- Export all active subscribers
SELECT email, subscribed_at, source
FROM newsletter_subscribers
WHERE status = 'active'
ORDER BY subscribed_at DESC;
Or use Supabase Table Editor → Export as CSV
Analytics Queries​
-- Total active subscribers
SELECT COUNT(*) as total_subscribers
FROM newsletter_subscribers
WHERE status = 'active';
-- Subscription rate by source
SELECT
source,
COUNT(*) as subscriptions
FROM newsletter_subscribers
WHERE status = 'active'
GROUP BY source;
-- Daily subscription trend
SELECT
DATE(subscribed_at) as date,
COUNT(*) as new_subscribers
FROM newsletter_subscribers
WHERE subscribed_at > NOW() - INTERVAL '30 days'
GROUP BY DATE(subscribed_at)
ORDER BY date DESC;
Supabase Realtime​
Overview​
Supabase Realtime syncs data changes across all connected clients via WebSocket.
How It Works​
- Database Changes → INSERT, UPDATE, DELETE on tracked tables
- Supabase Realtime → Broadcasts via WebSocket
- React Query Cache → Automatically invalidated
- UI Update → All users see latest data
Enabled Tables​
Currently enabled for:
- ✅ nova_tracks - Course/track updates
Setup Steps​
1. Enable Realtime on Tables​
Option A: Supabase Dashboard
- Go to Database → Replication
- Find
nova_trackstable - Toggle Enable Realtime ON
Option B: SQL
ALTER PUBLICATION supabase_realtime ADD TABLE nova_tracks;
2. Verify Realtime​
Check browser console for:
🔄 Setting up realtime sync for tracks...
✅ Realtime sync active for tracks
When data changes:
🔔 Tracks updated in database: UPDATE
3. Configure RLS​
Ensure SELECT policies allow access:
-- Allow everyone to read tracks
CREATE POLICY "Tracks are viewable by everyone"
ON nova_tracks FOR SELECT
USING (true);
Adding More Tables​
To enable realtime for other tables:
- Enable in Supabase Dashboard
- Create sync hook:
export const useEnrollmentsRealtimeSync = () => {
const queryClient = useQueryClient();
useEffect(() => {
const channel = supabase
.channel('enrollments_changes')
.on('postgres_changes', {
event: '*',
schema: 'public',
table: 'course_enrollments'
}, () => {
queryClient.invalidateQueries({ queryKey: ['enrollments'] });
})
.subscribe();
return () => supabase.removeChannel(channel);
}, [queryClient]);
};
- Add to
App.jsx
Performance Notes​
- Free tier: 200 concurrent connections
- Pro plan: 500 concurrent connections
- Only changes sent, not full data
- Scales automatically
Troubleshooting​
Realtime not working?
- Check Supabase Dashboard → API → Realtime enabled
- Verify table replication enabled
- Check browser console for errors
- Verify RLS policies allow SELECT
Changes not appearing?
- Check console for
🔔 Tracks updatedmessages - Verify WebSocket connection in Network tab
- Ensure same Supabase project
Environment Variables Summary​
Complete Environment Variables List​
# ========================================
# SUPABASE
# ========================================
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
# ========================================
# YOUTUBE DATA API
# ========================================
VITE_YOUTUBE_API_KEY=AIzaSyD...
# ========================================
# EMAIL (IONOS SMTP)
# ========================================
EMAIL_ENABLED=true
EMAIL_HOST=smtp.ionos.co.uk
EMAIL_PORT=587
EMAIL_USER=your-email@agentification.co.uk
EMAIL_PASSWORD=your-password
EMAIL_FROM=noreply@agentification.co.uk
EMAIL_TO=info@agentification.co.uk
# ========================================
# CLOUDINARY
# ========================================
VITE_CLOUDINARY_CLOUD_NAME=your-cloud-name
VITE_CLOUDINARY_UPLOAD_PRESET=blog-uploads
# ========================================
# GOOGLE ANALYTICS (Optional)
# ========================================
VITE_GA4_MEASUREMENT_ID=G-XXXXXXXXXX
# ========================================
# FLASK
# ========================================
SECRET_KEY=your-secret-key
JWT_SECRET=your-jwt-secret
NODE_ENV=development
Security Checklist​
- Never commit
.envfiles with real credentials - Use strong, unique secrets for production
- Restrict API keys to specific domains
- Keep Service Role Key server-side only
- Rotate secrets periodically
- Use environment-specific configurations
Vercel Environment Variables​
When deploying to Vercel, add all required variables in: Settings → Environment Variables
Apply to: Production, Preview, Development (as needed)
After adding variables, redeploy the application.
Testing All Integrations​
YouTube API​
curl "https://www.googleapis.com/youtube/v3/videos?id=dQw4w9WgXcQ&part=snippet&key=YOUR_KEY"
Email Service​
- Go to
/contact - Submit form
- Check terminal and inbox
Cloudinary​
- Go to
/admin/content/new - Upload image
- Check Cloudinary dashboard
Newsletter​
- Go to homepage
- Subscribe in footer
- Check Supabase
newsletter_subscriberstable
Realtime​
- Open site in two browser tabs
- Update course in one tab (admin)
- See instant update in other tab
Additional Resources​
Support​
For integration issues:
- Check service status pages
- Review API quotas and limits
- Verify environment variables
- Check browser console and server logs
- Test API endpoints directly
Last Updated: December 2025