Complete Step-by-Step Instructions
Every single step broken down with screenshots, code examples, and troubleshooting tips
🟢 Beginner-Friendly
🟡 Intermediate
🔴 Advanced
Estimated Total Time: 10-15 days for complete
implementation
Budget: $69/month recurring + $500-1000 one-time
setup
Time Required: 2-4 hours | Difficulty: 🟡 Intermediate
Navigate to Auth0 Website
💡 Tip: Using "Sign in with Google" is faster and you won't need to verify email
Choose Your Plan
Configure Your Tenant
You'll be asked to set up your "tenant" (your Auth0 instance):
Tenant Domain Name:
career-success-institute
This becomes: career-success-institute.us.auth0.com
Region:
Choose closest to your customers (US, EU, AU, etc.)
Environment Tag:
Select "Development" for now
Important: Tenant name cannot be changed later, so choose carefully!
Complete Account Setup
Verify Success
You should now see the Auth0 Dashboard with:
Navigate to Applications
Configure Application Settings
Name:
Career Success Customer Portal
Application Type:
Select "Single Page Web Applications"
This is for React, Vue, or Bubble.io apps
Configure Application URLs
After creation, click on your new application, then go to the "Settings" tab. Scroll down and configure these URLs:
Allowed Callback URLs:
https://portal.careersuccessinstitute.com/callback,
http://localhost:3000/callback,
https://version-test.bubbleapps.io/callback
These are where Auth0 redirects after successful login
Allowed Logout URLs:
https://portal.careersuccessinstitute.com,
http://localhost:3000,
https://version-test.bubbleapps.io
Where users go after logout
Allowed Web Origins:
https://portal.careersuccessinstitute.com,
http://localhost:3000,
https://version-test.bubbleapps.io
Domains allowed to make authentication requests
Note: localhost:3000 is for testing. Remove it before going live. Add your Bubble.io development URL if using Bubble.
Save Important Credentials
Scroll to the top of the Settings tab and COPY THESE VALUES - you'll need them later:
🔐 SAVE THESE SECURELY:
Domain:
career-success-institute.us.auth0.com
Client ID:
abc123xyz456789
(Long alphanumeric string)
Client Secret:
••••••••••••••••
(Click "Show" button to reveal - treat like a password!)
Action: Copy these to a password manager or secure note. You'll need them for Bubble.io integration.
Scroll Down & Click "Save Changes"
Important: Auth0 won't save settings automatically - you must click the blue "Save Changes" button at the bottom!
Why enable this? Let customers log in with their Google account instead of creating a new password. Increases conversion and reduces friction.
Navigate to Authentication
Enable Google (Recommended)
For Quick Start:
For Production (Your Own Credentials):
https://YOUR-TENANT.auth0.com/login/callback
Configure Permissions
Scroll down in the Google connection settings:
Attributes/Scopes:
Make sure these are checked:
Click "Save" and Test
Auth0 provides a "Try Connection" button - click it to test Google login
Time Required: 8-12 hours | Difficulty: 🟡 Intermediate
Create Bubble Account
Create New App
App Settings:
Career Success Portal
Click "Create a new app"
Familiarize with Bubble Editor
You'll see the visual editor with:
Left Sidebar:
Main Canvas:
💡 Tip: Take Bubble's 10-minute tutorial (Help → Lessons) before continuing
Navigate to Plugins
Auth0
Install Official Auth0 Plugin
Look for:
Auth0 by Bubble
Official Auth0 authentication plugin
FREEConfigure Auth0 Plugin
After installation, the plugin will appear in your Plugins list. Click on it to configure:
Enter Your Auth0 Credentials:
Auth0 Domain:
career-success-institute.us.auth0.com
(Your tenant domain from Step 1.2)
Client ID:
abc123xyz456789...
(From Step 1.2 - the long alphanumeric string)
Client Secret:
••••••••••••
(From Step 1.2 - keep this private!)
After entering credentials, click "Save"
What we're building: Data types to store customer information, orders, and documents
Navigate to Data Tab
Modify User Type
Click on "User" to add these fields:
| Field Name | Field Type | Purpose |
|---|---|---|
| auth0_user_id | text | Link to Auth0 |
| phone | text | Phone number |
| subscription_status | text | active/inactive |
Click "Create a new field" for each one
Create "Order" Data Type
Click "New type" button:
Type name:
Order
Add these fields:
| Field Name | Field Type |
|---|---|
| order_number | text |
| customer | User |
| package_type | text |
| status | text |
| amount | number |
| created_date | date |
| expert_assigned | text |
Create "Document" Data Type
Type name:
Document
| Field Name | Field Type |
|---|---|
| order | Order |
| document_type | text |
| file | file |
| version | number |
| status | text |
| uploaded_date | date |
By now you should have completed:
Next: Continue with the remaining phases in the main integration guide at /integration-guide
The remaining phases (Airtable CRM, Stripe, Emails) follow similar detailed patterns.
Your Auth0:
https://YOUR-TENANT.us.auth0.com
Bubble Editor:
https://bubble.io/app-name
Auth0 Docs:
auth0.com/docsBubble Manual:
manual.bubble.ioCareer Success Institute - Detailed Implementation Guide
This guide covers the most critical first steps. Continue with /integration-guide for remaining phases.
Last Updated: January 2025 | Version 1.0
Time Required: 3-4 hours | Difficulty: 🟢 Beginner
Sign Up for Airtable
Free Plan Includes: Unlimited bases, 1,200 records per base, 2GB attachments
Create New Base
Career Success CRM
Understand the Interface
Top Bar:
Left Sidebar:
How to create:
Customers
| Field Name | Field Type | Configuration |
|---|---|---|
Customer Name |
Single line text | Primary field (already exists) |
Email |
- | |
Phone |
Phone number | - |
Auth0 User ID |
Single line text | For linking to portal |
Status |
Single select | Options: Active, Inactive, Completed |
Sign Up Date |
Date | Include time: Yes |
Orders |
Link to another record | Link to: Orders table (create later) |
Notes |
Long text | Enable rich text formatting |
Tip: Click the dropdown arrow on any field name to change its type, add description, or configure settings
How to create:
Orders
| Field Name | Field Type | Configuration |
|---|---|---|
Order Number |
Single line text | Primary field - format: ORDER-12345 |
Customer |
Link to another record | Link to: Customers table |
Package Type |
Single select | Options: Resume, Cover Letter, LinkedIn, Coaching, Bundle |
Amount Paid |
Currency | Currency: USD ($) |
Status |
Single select | Options: Payment Received, In Progress, First Draft, Revisions, Completed |
Assigned Expert |
Single select | Add your team member names |
Order Date |
Date | Include time: Yes |
Due Date |
Date | - |
Documents |
Link to another record | Link to: Documents table |
Internal Notes |
Long text | For team communication |
Create this table the same way as Orders table:
| Field Name | Field Type | Configuration |
|---|---|---|
Document Name |
Single line text | Primary field - e.g., "John Doe Resume v1" |
Order |
Link to another record | Link to: Orders table |
Document Type |
Single select | Options: Resume, Cover Letter, LinkedIn Profile, Other |
File |
Attachment | Upload PDF, Word, etc. |
Version |
Number | Precision: Integer |
Status |
Single select | Options: Draft, Ready for Review, Approved, Final |
Upload Date |
Created time | Auto-populated |
| Field Name | Field Type |
|---|---|
Name |
Single line text |
Email |
|
Role |
Single select (Admin, Editor, Writer, Support) |
Specialty |
Multiple select (Resume, Cover Letter, LinkedIn, Coaching) |
Active Orders |
Count (from Orders → Assigned Expert) |
What are Views? Different ways to display and filter your data. Perfect for showing editors only their assigned orders, or showing admins all orders.
Create "Active Orders" View
Active Orders
Status → is not → CompletedDue Date → AscendingCreate "Kanban Board" View
Order Pipeline
This gives you a visual workflow like Trello!
Create "Calendar" View
Due Date Calendar
Enable Automations
Automation 1: New Order Notification
Setup:
Automation 2: Due Date Reminder
Invite Team Members
Generate API Key (for Integration)
Career Success Integration
data.records:read and
data.records:write
🔐 SAVE THIS API KEY SECURELY
pat1234567890abcdefghijklmnop...
You'll need this for connecting Bubble.io or Stripe webhooks to Airtable
You now have:
Time Required: 2-3 hours | Difficulty: 🟡 Intermediate
Sign Up for Supabase
Career Success Institute
Create New Project
Project Settings:
career-success-db
⏱️ Project setup takes ~2 minutes
Save Project Credentials
Once project is ready, go to Settings → API:
🔐 SAVE THESE VALUES:
Project URL:
https://abcdefgh.supabase.co
anon public key:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
service_role secret:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
⚠️ Keep this SECRET - never expose in frontend code!
Method: We'll use Supabase's SQL Editor to create tables with proper relationships
Navigate to SQL Editor
Create Customers Table
Copy and paste this SQL code:
-- Create customers table
CREATE TABLE public.customers (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
full_name TEXT NOT NULL,
phone TEXT,
auth0_user_id TEXT UNIQUE,
subscription_status TEXT DEFAULT 'active',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Enable Row Level Security
ALTER TABLE public.customers ENABLE ROW LEVEL SECURITY;
-- Create policy: Users can only see their own data
CREATE POLICY "Users can view own data"
ON public.customers
FOR SELECT
USING (auth.uid()::text = auth0_user_id);
-- Create index for faster lookups
CREATE INDEX idx_customers_email ON public.customers(email);
CREATE INDEX idx_customers_auth0 ON public.customers(auth0_user_id);
Create Orders Table
-- Create orders table
CREATE TABLE public.orders (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
order_number TEXT UNIQUE NOT NULL,
customer_id UUID REFERENCES public.customers(id) ON DELETE CASCADE,
package_type TEXT NOT NULL,
amount_paid DECIMAL(10,2) NOT NULL,
status TEXT DEFAULT 'payment_received',
assigned_expert TEXT,
stripe_payment_id TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
due_date TIMESTAMP WITH TIME ZONE,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Enable RLS
ALTER TABLE public.orders ENABLE ROW LEVEL SECURITY;
-- Policy: Users can see their own orders
CREATE POLICY "Users can view own orders"
ON public.orders
FOR SELECT
USING (
customer_id IN (
SELECT id FROM public.customers
WHERE auth0_user_id = auth.uid()::text
)
);
-- Indexes
CREATE INDEX idx_orders_customer ON public.orders(customer_id);
CREATE INDEX idx_orders_status ON public.orders(status);
CREATE INDEX idx_orders_number ON public.orders(order_number);
Create Documents Table
-- Create documents table
CREATE TABLE public.documents (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
order_id UUID REFERENCES public.orders(id) ON DELETE CASCADE,
document_type TEXT NOT NULL,
file_url TEXT NOT NULL,
file_name TEXT NOT NULL,
version INTEGER DEFAULT 1,
status TEXT DEFAULT 'draft',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Enable RLS
ALTER TABLE public.documents ENABLE ROW LEVEL SECURITY;
-- Policy: Users can see documents for their orders
CREATE POLICY "Users can view own documents"
ON public.documents
FOR SELECT
USING (
order_id IN (
SELECT o.id FROM public.orders o
JOIN public.customers c ON o.customer_id = c.id
WHERE c.auth0_user_id = auth.uid()::text
)
);
-- Indexes
CREATE INDEX idx_documents_order ON public.documents(order_id);
CREATE INDEX idx_documents_status ON public.documents(status);
Verify Tables Created
Create Storage Bucket
customer-documents
application/pdf, application/msword,
application/vnd.openxmlformats-officedocument.wordprocessingml.document
Set Storage Policies
In the bucket settings, add these RLS policies:
-- Allow authenticated users to upload
CREATE POLICY "Authenticated users can upload"
ON storage.objects FOR INSERT
TO authenticated
WITH CHECK (bucket_id = 'customer-documents');
-- Allow users to view their own files
CREATE POLICY "Users can view own files"
ON storage.objects FOR SELECT
TO authenticated
USING (bucket_id = 'customer-documents' AND auth.uid()::text = (storage.foldername(name))[1]);
Time Required: 2-3 hours | Difficulty: 🟡 Intermediate
Create Stripe Account
⏱️ Verification time: Can take 1-2 business days for full approval
Get API Keys
🔐 SAVE THESE KEYS (Test Mode First):
Publishable key (safe to expose):
pk_test_51abc123...
Secret key (NEVER expose!):
sk_test_51xyz789...
Create Products
Product 1: Premium Resume
Product 2: Cover Letter
Product 3: LinkedIn Optimization
What are webhooks? Stripe sends automatic notifications to your server when payments succeed, enabling you to create customer records automatically.
Create Webhook Endpoint
You need a server endpoint to receive webhooks. Options:
Option A: Bubble.io Workflow
Option B: Serverless Function
Configure Webhook in Stripe
https://your-api.com/stripe/webhook
checkout.session.completedpayment_intent.succeededpayment_intent.payment_failedwhsec_)
Sample Webhook Handler Code
Basic Node.js example:
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const { createClient } = require('@supabase/supabase-js');
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_KEY
);
export default async function handler(req, res) {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
req.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle successful payment
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
// Create customer in database
const { data, error } = await supabase
.from('customers')
.insert([
{
email: session.customer_email,
full_name: session.customer_details.name,
}
])
.select()
.single();
if (!error) {
// Create order record
await supabase.from('orders').insert([
{
customer_id: data.id,
order_number: `ORDER-${Date.now()}`,
package_type: session.metadata.package_type,
amount_paid: session.amount_total / 100,
stripe_payment_id: session.payment_intent,
status: 'payment_received'
}
]);
// Send confirmation email (Phase 6)
// await sendConfirmationEmail(session.customer_email);
}
}
res.json({ received: true });
}
Time Required: 2-3 hours | Difficulty: 🟡 Intermediate
Create SendGrid Account
Free Plan: 100 emails/day forever (perfect for starting)
Verify Domain (Recommended)
careersuccessinstitute.com
DNS Records to Add:
CNAME Record 1:
em1234.careersuccessinstitute.com →
u1234567.wl.sendgrid.net
CNAME Record 2:
s1._domainkey.careersuccessinstitute.com →
s1.domainkey.u1234567.wl.sendgrid.net
CNAME Record 3:
s2._domainkey.careersuccessinstitute.com →
s2.domainkey.u1234567.wl.sendgrid.net
Add these to your domain registrar (GoDaddy, Namecheap, etc.)
Verification takes 24-48 hours
Create API Key
Career Success Portal
🔐 SAVE THIS API KEY NOW:
SG.abc123xyz789...
You won't be able to see it again!
Navigate to Dynamic Templates
Template 1: Payment Confirmation
Payment Confirmation
/email-templates page
{{"{{"}}{{"orderNumber"}}}} syntax
Key Variables to Add:
{{"{{"}}{{"customerName"}}}} - Customer's
name
{{"{{"}}{{"orderNumber"}}}} - Order ID
{{"{{"}}{{"packageName"}}}} - What they
purchased
{{"{{"}}{{"amount"}}}} - Price paid{{"{{"}}{{"orderDate"}}}} - Purchase date
Create Remaining Templates
Repeat for:
Copy Template IDs
For each template, copy the Template ID (looks like:
d-abc123xyz789)
You'll need these when sending emails via API
Add this to your Stripe webhook handler (from Phase 5):
const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
async function sendConfirmationEmail(customerData) {
const msg = {
to: customerData.email,
from: '[email protected]', // Your verified sender
templateId: 'd-abc123xyz789', // Your template ID
dynamic_template_data: {
customerName: customerData.name,
orderNumber: customerData.orderNumber,
packageName: customerData.package,
amount: customerData.amount,
orderDate: new Date().toLocaleDateString()
}
};
try {
await sgMail.send(msg);
console.log('Email sent successfully');
} catch (error) {
console.error('Email error:', error);
}
}
// Call in your webhook:
await sendConfirmationEmail({
email: session.customer_email,
name: session.customer_details.name,
orderNumber: 'ORDER-12345',
package: 'Premium Resume',
amount: '$299'
});
Test it! Use SendGrid's "Send Test Email" feature in template editor
Time Required: 1-2 days | Difficulty: 🟢 Beginner
Complete Testing Flow:
Before Going Live:
Make sure ALL testing is complete and working perfectly!
Quick fixes for frequent problems
❌ Stripe Webhook Not Working
Symptoms: Payment succeeds but no database record created
Solutions:
stripe listen --forward-to localhost:3000/webhook
❌ Auth0 Login Fails
Symptoms: Redirect loop or "invalid callback" error
Solutions:
❌ Emails Not Sending
Symptoms: No emails received after purchase
Solutions:
❌ Database Connection Failed
Symptoms: Can't read/write to Supabase
Solutions:
You've completed the detailed implementation guide!
Your complete system is now set up with:
Auth0 authentication • Bubble.io customer portal • Airtable CRM
Supabase database • Stripe payments • SendGrid emails
Career Success Institute - Complete Detailed Implementation Guide
All 7 phases covered with step-by-step instructions
Last Updated: January 2025 | Version 1.0