Skip to main content

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​

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​

  1. Go to Google Cloud Console
  2. Sign in with your Google account
  3. Click Select a project → NEW PROJECT
  4. Enter project name: Agentification Academy
  5. Click CREATE

2. Enable YouTube Data API v3​

  1. Make sure your project is selected
  2. Go to APIs & Services → Library
  3. Search for: YouTube Data API v3
  4. Click ENABLE

3. Create API Key​

  1. Go to APIs & Services → Credentials
  2. Click + CREATE CREDENTIALS
  3. Select API key
  4. Copy the generated API key
  1. Click the Edit icon next to your API key
  2. Under API restrictions:
    • Select Restrict key
    • Check YouTube Data API v3 only
  3. Under Application restrictions:
    • Select HTTP referrers (web sites)
    • Add your domains:
      http://localhost:*
      https://your-domain.com/*
      https://*.vercel.app/*
  4. Click SAVE

5. Add to Environment Variables​

Local Development (.env):

VITE_YOUTUBE_API_KEY=AIzaSyD_YOUR_ACTUAL_KEY_HERE

Production (Vercel):

  1. Go to Vercel Dashboard → Settings → Environment Variables
  2. Add:
    • Name: VITE_YOUTUBE_API_KEY
    • Value: Your API key
    • Environments: Production, Preview, Development
  3. 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​

LimitFree Tier
Daily quota10,000 units
Cost per video fetch1 unit
Videos per day10,000
Reset timeMidnight 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​

  1. Go to http://localhost:5173/contact
  2. Fill out and submit the form
  3. Check terminal for email status
  4. Check inbox at EMAIL_TO address

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_TO address
  • 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​

  1. Go to cloudinary.com/register
  2. Sign up for free account
  3. Verify email address

2. Get Cloud Name​

  1. After logging in, find your Cloud Name on dashboard
  2. Example: dxyz123abc or your-company-name
  3. Copy this value

3. Create Upload Preset​

  1. Go to Settings → Upload
  2. Scroll to Upload presets
  3. Click Add upload preset
  4. Configure:
    • Preset name: blog-uploads
    • Signing Mode: Unsigned (required for client-side)
    • Folder: blog-images
    • Upload Manipulations:
      • Format: auto (WebP conversion)
      • Quality: auto:good
    • Access Mode: Public
  5. Click Save

4. Add Environment Variables​

Local Development (.env):

VITE_CLOUDINARY_CLOUD_NAME=your-cloud-name
VITE_CLOUDINARY_UPLOAD_PRESET=blog-uploads

Production (Vercel):

  1. Vercel Dashboard → Settings → Environment Variables
  2. Add VITE_CLOUDINARY_CLOUD_NAME and VITE_CLOUDINARY_UPLOAD_PRESET
  3. Apply to all environments
  4. Redeploy

Testing​

  1. Start dev server: npm run dev
  2. Go to /admin/content/new
  3. Click image icon in editor
  4. Upload an image
  5. 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)​

ResourceFree Tier
Storage25 GB
Bandwidth25 GB/month
Transformations25,000/month
UploadsUnlimited

Upgrade to Pro ($99/month):

  • 250 GB storage
  • 250 GB bandwidth
  • Advanced features

Troubleshooting​

"Cloudinary configuration missing"

  • Check .env file 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​

  1. Database Changes → INSERT, UPDATE, DELETE on tracked tables
  2. Supabase Realtime → Broadcasts via WebSocket
  3. React Query Cache → Automatically invalidated
  4. 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

  1. Go to Database → Replication
  2. Find nova_tracks table
  3. 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:

  1. Enable in Supabase Dashboard
  2. 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]);
};
  1. 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?

  1. Check Supabase Dashboard → API → Realtime enabled
  2. Verify table replication enabled
  3. Check browser console for errors
  4. Verify RLS policies allow SELECT

Changes not appearing?

  1. Check console for 🔔 Tracks updated messages
  2. Verify WebSocket connection in Network tab
  3. 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 .env files 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​

  1. Go to /contact
  2. Submit form
  3. Check terminal and inbox

Cloudinary​

  1. Go to /admin/content/new
  2. Upload image
  3. Check Cloudinary dashboard

Newsletter​

  1. Go to homepage
  2. Subscribe in footer
  3. Check Supabase newsletter_subscribers table

Realtime​

  1. Open site in two browser tabs
  2. Update course in one tab (admin)
  3. See instant update in other tab

Additional Resources​

Support​

For integration issues:

  1. Check service status pages
  2. Review API quotas and limits
  3. Verify environment variables
  4. Check browser console and server logs
  5. Test API endpoints directly

Last Updated: December 2025