TC TeachCopilotAdmin Panel Manual
Back to Home

Admin Panel Operation Manual

This manual documents the current /admin feature set for the TeachCopilot admin panel. It is written for operators who need to manage users, courses, digital products, orders, homepage content, files, email delivery, platform settings, MCP integration, and content modules.

Last verified: Previous verification: Source: live Chrome session against a production deployment's /admin

Every screenshot in this manual is sourced from a real deployed admin panel, not local dev. This revision added System Settings tab-by-tab screenshots (Payments, Email, Analytics, Layout, Integrations), the MCP Keys endpoint setup page, and all seven Site Editor panels (Hero, Courses & Products, Features, Testimonials, Top Bar, Footer, Legal Pages). A handful of screens that require state not present on this instance (Interactive Module editor, Order detail, Admin Profile, sidebar layout close-up) still reference the previous local-dev screenshot set.

Deploy TeachCopilot

TeachCopilot ships as a standard Docker image (ghcr.io/greateyin/single-class), so it runs on any container host: a VPS with docker compose, Render, Railway, Google Cloud Run, or Fly.io. This walkthrough uses Fly.io, tested end-to-end from a brand-new account to a running app.

Before you start: Fly.io requires a payment method on file even to use its free usage allowance — it's an anti-abuse check, not an upfront charge. You will not be billed unless you exceed the free tier.

1. Create a Fly.io account

Go to fly.io/app/sign-up and sign up (GitHub OAuth is fastest if you already have a GitHub account).

Fly.io sign-up page with GitHub, Google, and email options

Right after signing up you'll see a banner asking you to add a payment method — click it and add a card. Once verified, the dashboard unlocks:

Fly.io dashboard showing the payment method required banner
Fly.io dashboard after payment method is verified and account is unlocked

2. Install the CLI and create the app

curl -L https://fly.io/install.sh | sh
flyctl auth login
flyctl apps create my-single-class --org personal

3. Provision Postgres

Use Fly Postgres for the simplest setup (same private network as your app), or bring your own external Postgres (Neon, Supabase, RDS) and skip straight to step 5.

flyctl postgres create \
  --name my-single-class-db \
  --org personal \
  --region nrt \
  --vm-size shared-cpu-1x \
  --volume-size 1 \
  --initial-cluster-size 1 \
  --autostart

This prints a connection string — save it, it's shown only once.

4. Authenticate the private image pull

The image is private on GHCR. Generate a classic GitHub token (not fine-grained) with the read:packages scope at github.com/settings/tokens/new, then:

docker login ghcr.io -u <your-github-username>
# paste the token as the password when prompted

5. Set secrets before the first deploy

flyctl postgres attach my-single-class-db --app my-single-class

flyctl secrets set --app my-single-class \
  AUTH_SECRET="$(openssl rand -base64 32)" \
  AUTH_URL="https://my-single-class.fly.dev" \
  NEXT_PUBLIC_BASE_URL="https://my-single-class.fly.dev" \
  ENABLE_MIGRATIONS=true

Why this order matters: Fly creates two machines by default for high availability, even with min_machines_running = 0 — that setting only controls scale-to-zero-when-idle, not how many machines get created on first deploy. Each is a fully separate process. Leaving DATABASE_URL unset and relying on the in-app setup wizard instead means only the one machine that handled your form submission learns about it — the second machine still has nothing configured and throws a raw 500 the moment a request round-robins to it. Setting secrets upfront pushes the same configuration to every machine identically. (The wizard itself works fine — it's the right flow for a single-container VPS deployment with no replicas to desync.) Missing AUTH_SECRET/AUTH_URL also silently breaks sign-in, redirecting to the container's internal address instead of your real domain — found by re-running this exact guide end-to-end as a QA pass.

6. Write fly.toml and deploy

app = "my-single-class"
primary_region = "nrt"

[build]

[http_service]
  internal_port = 3000
  force_https = true
  auto_stop_machines = "stop"
  auto_start_machines = true
  min_machines_running = 0

[[vm]]
  size = "shared-cpu-1x"
  memory = "512mb"
flyctl deploy --image ghcr.io/greateyin/single-class:latest \
  --app my-single-class \
  -c fly.toml \
  --local-only \
  --now

--local-only pulls the private image through your own authenticated Docker session (step 4) instead of Fly's remote builder, then pushes it into Fly's registry. Use v1.4.2 or later — earlier tags shipped with a broken license public key baked in, so no license key would ever activate. :latest always points to the newest tag, so it's unaffected.

7. First boot

With DATABASE_URL and ENABLE_MIGRATIONS=true already set from step 5, the app runs migrations on first boot and every machine agrees on the same database — visiting your app's URL goes straight to admin account creation:

Create admin account form with brand name, email, and password fields

Sign in with the credentials you just created, and the instance is fully live — confirmed reliable across repeated requests, unlike the unconfigured-secrets path described above. First boot also seeds one example course and one example digital product automatically, so there's real content to click through immediately instead of an empty dashboard (set SEED_DEMO_CONTENT=false before first boot to skip it):

Working TeachCopilot admin dashboard after first-boot setup completes on Fly.io

Running without a license: a fresh install runs on a 30-day grace period with full functionality except: 1 course (capped at 2 sections × 2 lectures each), 1 digital product, online payment providers (Stripe/PayPal/Lemon Squeezy — Bank Transfer stays available), and MCP key management. Activating a license at /admin/settings/license lifts every limit immediately, no redeploy required.

Custom domain

flyctl certs add courses.example.com --app my-single-class

Update AUTH_URL and NEXT_PUBLIC_BASE_URL and redeploy.


Test Access

The screenshots in this revision were captured against the live production instance. If you are following along on your own deployment, sign in with your own admin account. The URLs below are illustrative.

ItemValue
Production URLhttps://your-domain.example
Admin sign-in URLhttps://your-domain.example/auth/signin
Admin dashboardhttps://your-domain.example/admin/dashboard
Local dev URL (alternative)http://localhost:4000

If running locally instead:

pnpm dev
# or
PORT=4000 npx next dev --port 4000

Scope & Coverage

Graphify identifies the admin surface across these major communities:

AreaAdmin Features Covered
Admin Management Actionsusers, orders, course/product admin workflows
Content & Stats Administrationdashboard, site editor, analytics-style summaries
System Settings & Databaseplatform settings, license setup, storage/email/AI/payment settings
Storage & Media Managementfile browser, upload cleanup, storage provider status
Payment & Checkout Processingorders, manual fulfillment, refunds, transaction status
MCP Integration & Infrastructureadmin MCP tooling and audit infrastructure

GitNexus confirmed admin-related files under src/app/admin, covering:

ModuleMain Routes
Dashboard/admin/dashboard
Users/admin/users, /admin/users/[userId]
Courses/admin/courses, /admin/courses/[id]/*, /admin/lessons/[id]
Products/admin/products, /admin/products/[id]/*
YouTube Shares/admin/youtube-shares, /admin/youtube-shares/[id]
Interactive Modules/admin/interactive-modules, /admin/interactive-modules/new, /admin/interactive-modules/[id]
Orders/admin/orders, /admin/orders/[id]
Sales/admin/sales
Profile/admin/profile
Site Editor/admin/site-editor
Storage/admin/storage
Email Logs/admin/email-logs
Settings/admin/settings, /admin/settings/license, /admin/settings/mcp, /admin/setup/license

Admin Layout

The admin panel uses a persistent left sidebar and a main work area. The sidebar is the primary navigation for all admin modules.

Sidebar Navigation

Sidebar ItemPurpose
DashboardBusiness overview and shortcuts
UsersStudent/admin account management
CoursesCourse creation, curriculum, sales page, pricing, settings
Digital ProductsDownloadable products, files, pricing, settings
YouTube SharesPublic SEO share pages for YouTube content
Interactive ModulesReusable SVG/GSAP learning modules
OrdersTransactions, fulfillment, refunds, invoice actions
SalesRevenue and sales reporting
ProfileCurrent admin account profile
Site EditorHomepage/landing content
StorageFile browser and storage cleanup
Email LogsTransactional email delivery logs
System SettingsBrand, email, analytics, layout, integrations, payment, license settings
MCP KeysAPI keys for AI clients (Claude Desktop, Cursor) connecting via MCP

1. Dashboard

Path/admin/dashboard

TeachCopilot admin dashboard showing revenue cards and workspace shortcuts

The dashboard is the first admin overview page.

Main Uses

AreaWhat To Check
Revenue cardsTotal revenue, course sales, product sales, active learners (last 7 days)
Workspace shortcutsOne-click links into Courses, Products, Orders, Users

Recommended Operator Flow

  1. Sign in as admin.
  2. Open /admin/dashboard.
  3. Confirm revenue and order totals look reasonable.
  4. Use the sidebar or the Workspace shortcuts for deeper management.

2. Users

Path/admin/users

Users list showing stat cards and a demo student account
Cropped to a demo account only. The live table also shows every real admin/student email.

The Users section manages registered accounts.

List Page

FeatureDescription
Stat cardsTotal Users, Administrators, Students
User tableName, email, role, last login, joined date, total spend
Add UserCreates an account directly (email, optional name, role); sends a welcome email with password-setup instructions

User Detail

Path/admin/users/[userId]

User detail page showing spend, courses, products, and password reset panel

Use this page to inspect and manage one account.

PanelPurpose
Role selectorPromote/demote between Student and Admin
Stat cardsTotal Spend, Courses enrolled, Products owned, Last Login, Joined date
Enrolled CoursesList of course enrollments for this user
Reset PasswordAdmin-set new password, bypassing the email flow

Operational Notes

  • Do not demote the last known admin unless another admin account exists.
  • Disabling/demoting a user blocks admin access but does not delete order records.
  • Email identity changes should be handled carefully because orders and ownership records depend on user identity.

3. Courses

Path/admin/courses

Courses list with stat cards and a published course card

Courses are the primary learning product type. A course contains curriculum modules, lessons, landing-page copy, pricing, social proof, reviews, comments, and publication settings.

Course List

FeatureDescription
Stat cardsTotal Courses, Published, Drafts
Create New CourseType a title, click Create Course. Redirects straight into the editor
Course cardsTitle, draft/published badge, price, category

Free-tier limit: without an activated license key, the admin panel shows only 1 course. Additional courses are hidden and a yellow banner indicates how many are locked. The Create Course form is disabled when at the limit. Activating a valid license key immediately removes this restriction. No data migration needed. See License Settings.

maintenance_expired is not restricted. A license whose maintenance window has lapsed still grants full content access.

Course Editor Tabs

TabPathPurpose
Intended Learners/admin/courses/[id]/intended-learnersLearning outcomes, prerequisites, target learners. Currently a "Coming Soon" placeholder, not yet implemented
Curriculum/admin/courses/[id]/curriculumModules, lessons, lesson content, drip schedule
Landing Page/admin/courses/[id]/landing-pageSales copy, media, SEO, OG settings
Pricing/admin/courses/[id]/pricingPrice, promo pricing, coupons
Upsell/admin/courses/[id]/upsellPost-purchase one-time offer
Social Proof/admin/courses/[id]/social-proofTestimonials and trust content
Reviews/admin/courses/[id]/reviewsStudent review moderation
Comments/admin/courses/[id]/commentsLesson Q&A/comment moderation
Settings/admin/courses/[id]/settingsPublish status, access rules, downloads, deletion

3.1 Intended Learners

Path/admin/courses/[id]/intended-learners

Intended Learners tab showing a Coming Soon placeholder notice

This tab is reserved for defining who the course is for, but is not built yet. It currently shows a "Coming Soon" notice. Use the Landing Page's course description and features list to communicate audience fit in the meantime.

3.2 Curriculum

Path/admin/courses/[id]/curriculum

Curriculum editor showing four sections and twelve published lectures

The curriculum editor manages course structure.

ObjectDescription
Section (module)A course section that groups lessons
Lecture (lesson)A page inside a section
Draft/Published badgeControls whether a lesson is visible to students
Drag handleReorder sections and lessons
DripOptional release timing per section

Free-tier limit: without an activated license, only 1 section is visible in the curriculum editor. Additional sections are hidden. The Add New Section form is disabled when at the limit. Activate a license to see and manage all sections.

Curriculum Workflow

  1. Type a section name into Add New Section and click Add Section.
  2. Under each section, type a lecture title into Add a new lecture... and click Add Lecture.
  3. Click into a lecture to fill its markdown body, video/embed URL, and attachments.
  4. Publish lessons when ready. A course cannot be published while it has zero published lessons.
  5. Drag to reorder sections/lessons to match the intended learning path.

3.3 Landing Page

Path/admin/courses/[id]/landing-page

Course landing page editor showing hero video and course details fields

This page controls the content shown on the public course page (/course/[courseId]), which also hosts the inline checkout panel. There is no separate enrollment page; /enroll/[courseId] is only a legacy permanent redirect to it.

SectionPurpose
Hero Background Video / Sales Video (VSL)Optional YouTube/Vimeo embeds for the enrollment page
Course ImageCover image (750x422 recommended)
Course DetailsTitle, Public SEO Slug, Subtitle, Course Description (markdown, AI-assisted Expand/Simplify/Fix Grammar/translate), Sales Story
Basic InformationLanguage, Skill Level, Category, Primary Topic
SEO SettingsSEO title/description/keywords, with an AI Generate button
OG / SocialOpen Graph image and share description
Everything Included FeaturesChecklist-style feature/value rows shown on the sales page

Recommended Landing Page Flow

  1. Fill Course Title, Subtitle, and a markdown Description (200+ words recommended for SEO).
  2. Set Language, Skill Level, Category, and Primary Topic.
  3. Add a cover image and OG image.
  4. Fill SEO Title/Description/Keywords. Use AI Generate for a first draft, then review.
  5. Add 3 to 5 "Everything Included" feature rows with a label and value each.
  6. Save, then use Preview to check the public page.

3.4 Pricing

Path/admin/courses/[id]/pricing

Course pricing panel with price field and quick select buttons

Pricing is stored internally in cents but edited as a dollar amount.

Field/FeatureDescription
Price (USD)Main course price
Original Price (crossed-out)Leave blank to auto-calculate as 3x the sale price
Quick SelectOne-click common price points (Free, $19.99 to $297)
Promotional PricingTime-limited or permanent promotion (scroll below Quick Select)
CouponsCourse-scoped discount codes

Rules

  • Paid course prices should be at least 1 cent; use 0 only for intentionally free courses.
  • Confirm promo end date and permanent-promo flags before publishing.
  • Coupon usage is tracked and should be reviewed if usage limits matter.

3.5 Upsell

Path/admin/courses/[id]/upsell

Post-purchase upsell configuration panel

Configures a one-time offer (OTO) shown immediately after this course is purchased, accepted via one-click charge against the buyer's saved card.

OptionBehavior
No upsellCheckout ends normally
Another courseOffers a different published course as the OTO
A digital productOffers a digital product as the OTO

Only published courses/products appear in the Upsell target dropdown. If you see "No published courses available," publish the target first.

3.6 Social Proof, Reviews, Comments

Social Proof/admin/courses/[id]/social-proof

Social proof panel for adding course testimonials

Add named testimonials shown on the landing page via Add Testimonial, then Save Changes.

Reviews/admin/courses/[id]/reviews

Reviews panel showing average rating and star distribution

Shows average rating, a 1 to 5 star distribution, and the full review list with reply/hide/delete moderation actions.

Comments/admin/courses/[id]/comments

Lesson comments inbox with a lesson filter sidebar

A course-level inbox for lesson Q&A. Filter the thread list by lesson using the left panel; "Include resolved" reveals closed threads.

3.7 Settings

Path/admin/courses/[id]/settings

Course settings panel with published toggle and access duration

Controls course-level publication and behavior.

SettingDescription
PublishedMakes the course public. See the publish guard below
Allow Video DownloadLets enrolled students download lesson videos
Money Back GuaranteeFree-text trust badge shown on the sales page; blank hides it
Access Duration (Months)Limits enrollment access; blank means lifetime

Publish guard: checking Published and saving while no lesson is published yet shows a confirmation dialog:

Publish confirmation dialog warning that no lessons are published yet

"This course may not be ready to publish: None of this course's lessons are published. Students will see no content."

You can proceed anyway (useful for reserving the public slug ahead of content), but in practice publish at least one lesson first so paying students see something immediately.

4. Digital Products

Path/admin/products

Digital products list with a draft product card

Digital products are downloadable items such as manuals, templates, PDFs, or code bundles.

Free-tier limit: without an activated license key, the admin panel shows only 1 digital product. Additional products are hidden and a yellow banner shows how many are locked. The Create Product form is disabled when at the limit. Activate a license key to access all products. See License Settings.

Product Editor Tabs

TabPathPurpose
Landing Page/admin/products/[id]/landing-pageProduct copy, SEO, OG, public sales content
Product Files/admin/products/[id]/filesUpload downloadable files delivered after purchase
Pricing/admin/products/[id]/pricingPrice, promo pricing
Upsell/admin/products/[id]/upsellProduct-level post-purchase OTO
Social Proof/admin/products/[id]/social-proofTestimonials and proof
Settings/admin/products/[id]/settingsPublish status and Lemon Squeezy Variant ID

4.1 Product Landing Page

Path/admin/products/[id]/landing-page

Product landing page editor with title and short description fields
SectionPurpose
Title / Public Link / Short DescriptionHeadline and one-line promise; the public link is permanent and UUID-based
Cover Image / Hero VideoProduct cover (16:9) and optional YouTube hero background
Sales StoryLonger markdown pitch: hook, story, offer, shown between hero and order box
What's Included (Feature Bullets)Label/value rows, e.g. "Full admin manual (PDF + Markdown)" / "$49"
Guarantee BadgeFree-text trust badge on the sales page

4.2 Product Files

Path/admin/products/[id]/files

Product files panel with an empty downloadable files list

Upload the files delivered to buyers via Add File; drag to reorder if multiple files are attached.

Storage rule: product file upload should go through the platform upload module. Do not manually insert file rows into the database. The upload layer is responsible for provider selection, file metadata, public/private URL behavior, and cleanup consistency.

4.3 Product Pricing

Path/admin/products/[id]/pricing

Product pricing panel showing price and quick select buttons

Same pattern as course pricing: dollar-denominated Price (USD), an auto-calculated 3x "Original Price," and Quick Select buttons.

4.4 Product Upsell

Path/admin/products/[id]/upsell

Product upsell panel with a course selected as the offer target

Same OTO pattern as course upsell, but the offer can point at another product or a course. Selecting "A course" reveals an Upsell target dropdown and an optional OTO price in cents override. Again, only published targets are selectable.

4.5 Product Social Proof & Settings

Social Proof/admin/products/[id]/social-proof

Product social proof panel for adding testimonials

Same testimonial editor pattern as course Social Proof.

Settings/admin/products/[id]/settings

Product settings panel with visibility toggle and Lemon Squeezy variant ID
SettingDescription
PublishedMakes the product visible on the store
Lemon Squeezy Variant IDRequired only if selling this product through Lemon Squeezy checkout
Delete ProductDestructive action in the Danger Zone; irreversible

5. YouTube Shares

Path/admin/youtube-shares

YouTube shares list with a create new share form

YouTube Shares create public SEO pages for YouTube videos.

FeatureDescription
Stat cardsTotal Shares, Published, Drafts
Create New ShareTitle + YouTube URL, one click to create
SlugPublic URL segment
Markdown contentOptional article content below the video

Use Cases

  • Turn a YouTube video into an indexed article-style landing page.
  • Add summary, links, and calls to action below the embedded video.
  • Use a stable slug for sharing.

6. Interactive Modules

Path/admin/interactive-modules

Interactive modules list with embedding syntax help

Interactive modules are reusable SVG/GSAP learning widgets that can be embedded inside any lesson or product markdown body.

Embed Syntax

<interactive id="module-slug" mode="explore" />
ModeBehavior
exploreFree exploration
guidedStep-by-step walkthrough
examLocked or assessment-style interaction

Click Create Module to build a new one with AI assistance.

7. Orders

Path/admin/orders

Orders list with stat cards and an empty orders table

Orders represent course enrollments, digital product purchases, free claims, manual orders, and bank-transfer/pending flows.

Order List

Column/FeatureDescription
Stat cardsTotal Orders, Completed (this page), Refunded (this page), Revenue (this page)
TabsAll Orders / Courses / Digital Products
Manual OrderAdmin-created order, e.g. for an offline sale
Table columnsDate, Customer, Product/Course, Amount, Status, Expires, Actions

Order Detail

Path/admin/orders/[id]

ActionPurpose
Confirm pending paymentCompletes bank-transfer/manual pending order
Fix enrollmentRepairs missing enrollment/ownership when fulfillment failed
Send invoiceRe-sends order invoice/confirmation
RefundInitiates or records refund flow where supported

Operational Notes

  • Refund and destructive actions should be used only after confirming provider state.
  • Pending bank-transfer orders do not represent successful payment until confirmed.
  • Enrollment/ownership repair should be used for fulfillment gaps, not as a normal purchase path.

8. Sales

Path/admin/sales

Sales analytics page with revenue card and sales overview chart

Sales reporting summarizes revenue and sales performance.

AreaPurpose
Total Revenue cardLifetime revenue, with a period selector (Monthly, etc.)
Total Orders / Refunds ProcessedQuick counters
Sales OverviewChart of sales over the selected period

9. Profile

Path/admin/profile

The Profile page edits the currently signed-in admin account (name, avatar).

10. Site Editor

Path/admin/site-editor

Site editor overview with homepage sections list and live preview

The Site Editor controls homepage and site-level marketing content with a live split preview: edit on the left, see the rendered homepage on the right. Click Save to publish; Reset discards unsaved edits.

Section (left sidebar)Purpose
HeroHomepage headline, subheadline, rating badge text, hero image/video, CTA button + link
Courses & ProductsInformational only. This section auto-lists published courses/products; manage them from their own admin sections
FeaturesHomepage feature grid (icon, title, description per row)
TestimonialsHomepage social proof
Top BarAnnouncement bar text and colors
FooterBrand name, copyright text, colors
Legal PagesFavicon, Terms of Service, Privacy Policy, Support page content

10.1 Courses & Products

Site editor Courses and Products panel with manage shortcuts

This panel has no editable fields. It explains that the homepage grid automatically shows all published courses and digital products, with Manage Courses / Manage Products shortcuts.

10.2 Features

Site editor Features panel with title, icon, and description fields

Each feature row has a Title, an Icon picker, and a Description. Reorder or delete rows as needed; section background color is also configurable here.

10.3 Testimonials

Site editor Testimonials panel with an add testimonial button

Homepage-level testimonials (distinct from per-course/per-product testimonials). Click Add Testimonial to add a quote, name, and optional photo.

10.4 Top Bar

Site editor Top Bar panel with notification text and color pickers

A Show top bar toggle, free-text Notification text, and background/text color pickers for the announcement strip shown above the header.

10.5 Footer

Site editor Footer panel with brand name and copyright text

Site-wide brand name (used in page titles, llms.txt, and course pages), copyright text, and footer background/text colors.

10.6 Legal Pages

Site editor Legal Pages panel with favicon upload and terms of service editor

Upload a favicon and edit the markdown body of the Terms of Service page (and, further down this panel, Privacy Policy and Support content) directly in the admin panel.

Publishing Notes

  • Changes appear live in the preview immediately; nothing is public until you click Save.
  • Use the desktop/tablet/mobile preview toggle at the top of the editor to sanity-check responsive layout before saving.
  • Keep homepage copy in English if the product is configured as English-first.

11. Storage

Path/admin/storage

Storage file browser listing uploaded files with type and size columns

Storage manages every file uploaded through the application, regardless of which storage provider is active (shown top-right, e.g. "Cloudflare R2").

FeatureDescription
File Browser tabSearch by filename; filter by Type (system/course/digital_product/...) and Storage (DB vs Blob)
File tableName, type, size, storage backend, created date; preview/download/delete actions
Cleanup tabIdentify and remove orphaned files

Storage provider rule: when uploading through the admin UI or MCP, use the existing upload service. The service reads current storage settings and handles provider-specific writes, database file records, and cleanup behavior. Never insert files rows directly.

12. Email Logs

Path/admin/email-logs

Email logs page with status and template filters

Email Logs show transactional email delivery events.

Column/FeatureDescription
Search recipientFilter the table by "To" address
All Statuses / All TemplatesDropdown filters
Table columnsTime, To, Subject, Template, Provider, Status

Use Cases

  • Confirm purchase confirmation emails were sent.
  • Debug password reset, magic-link, or welcome-email delivery.
  • Verify Resend/SendGrid configuration changes through recent delivery logs.

If the active email provider has no API key configured, sends fail silently at the provider level and nothing appears here. Check System Settings → Email first if the log is unexpectedly empty.

13. System Settings

Path/admin/settings

System settings General tab with app name and brand fields

System Settings centralize platform configuration across six tabs: General, Payments, Email, Analytics, Layout, and Integrations.

13.1 General

Shown above. App Name, Brand Display Name, Brand Tagline, Service/Support Email, and support-hours timezone.

13.2 Payments

Payments tab with Stripe, PayPal, and Lemon Squeezy toggles

Per-provider enable toggles and credentials for Stripe, PayPal, and Lemon Squeezy, plus bank-transfer configuration further down the tab. Unlike Email, these toggles are independent — enabling Stripe does not disable PayPal, so a checkout page can legitimately offer more than one payment method at once.

13.2.1 Setting Up Stripe (Test Mode)

  1. Sign in to the Stripe Dashboard, switch to Test mode, and go to Developers → API keys. Copy the Publishable key (pk_test_...) and Secret key (sk_test_...).
  2. In Admin → Settings → Payments: toggle Enable Stripe on, paste both keys, and save.
  3. Required, not optional — create a webhook: Developers → Webhooks → Add destination, listen for checkout.session.completed, point it at the URL shown under Webhook Secret in this tab (https://<your-domain>/api/webhooks/stripe). Copy the signing secret (whsec_...) it gives you back into that field and save again.

Why the webhook step isn't skippable: Stripe Checkout Sessions fulfill entirely through the webhook — checkout.session.completed is what creates the transaction record and grants course/product access. Found by testing the full purchase flow end-to-end: without a configured webhook, Stripe's own checkout page shows a completely convincing "Payment Confirmed — You're in" success screen, and the card is genuinely charged, but the platform never learns the purchase happened — no order appears in Admin → Orders, no account is created, and the buyer has no access to anything. There is no error anywhere in the UI to indicate this. Always place a real test purchase after setup and confirm it shows up in Orders before considering Stripe configured.

13.2.2 Setting Up PayPal (Sandbox)

  1. Sign in to the PayPal Developer Dashboard and create (or open) a REST API app under Sandbox.
  2. Copy the Client ID and Secret.
  3. In Admin → Settings → Payments: toggle Enable PayPal on, paste both values, and save. Webhook ID is optional — leave it blank unless you specifically want PayPal's own delivery logs as a secondary audit trail.
PayPal section of the Payments tab with Client ID filled in, Client Secret marked Configured, Webhook ID left blank, and Mode set to Sandbox

Unlike Stripe, the webhook is not required for fulfillment. PayPal's checkout uses a different architecture: when the buyer approves payment, the browser calls a capture action directly, which synchronously captures the order and grants access in the same request — there's no waiting on an asynchronous event. Confirmed by testing with the Webhook ID field left empty and no webhook subscription pointing at this deployment at all: purchases still fulfilled correctly every time. A webhook subscription (Testing Tools → Webhook Simulator, or a real one under Apps & Credentials) is only useful as a secondary safety net for refund/dispute events, not for the initial purchase.

13.2.3 Coupons at Checkout

A coupon is created against exactly one course or one digital product (Pricing tab → Coupon Management/Coupons → enter a code, percentage or fixed amount, optional usage limit and expiry). On the public checkout page, the buyer expands I have a coupon, enters the code, and clicks Apply to preview the discounted total before paying — the discount is calculated the same way regardless of which payment provider is used.

Verified end-to-end (2026-08-22): tested all four combinations of course/digital-product × with/without coupon through Stripe test mode, each as a genuinely signed-out guest checkout. Every purchase correctly: charged the discounted amount when a coupon was applied, appeared in Admin → Orders as Completed, auto-created a new student account with the exact purchase amount as Total Spend, and granted real course enrollment / product ownership (confirmed via each account's own "Enrolled Courses" / "Digital Products" panel, not just the order list).

PayPal verified end-to-end (2026-08-22): tested a course purchase with no coupon and a digital-product purchase with a coupon applied, both through PayPal Sandbox as a genuinely signed-out guest checkout with a real Sandbox test buyer account. Note that PayPal's own checkout popup requires a real user click to open (browsers block script-triggered clicks on it as a popup) and, after approval, a second explicit confirmation on PayPal's own review page — logging in alone does not complete the purchase. Both test purchases correctly charged the discounted amount when the coupon was applied, appeared in Admin → Orders as Completed, and granted real access: course enrollment and digital-product ownership were both confirmed on the same buyer account's "Enrolled Courses" / "Digital Products" panel, and the order-confirmation email was confirmed sent in Admin → Email Logs.

User detail page for the PayPal sandbox buyer account showing Total Spend of $241.99, 1 enrolled course, and 1 owned digital product

13.2.4 Refunds

Open any Completed order (Admin → Orders → an order row) and scroll to the red Danger Zone card, which only appears while the order is Completed. Refund Order asks for a single confirmation, then issues a full refund — there is no partial-amount or reason field. Once refunded, the button disappears, so the same order can't be refunded twice.

  1. Open the order and confirm it's still Completed, then click Refund Order in the Danger Zone card.
Order detail page for a Completed PayPal course order with the Danger Zone card and Refund Order button
  1. Confirm the "Refund this order?" dialog — this is the only chance to back out, since the action is irreversible.
Confirmation dialog asking are you sure you want to refund this order, this action cannot be undone
  1. The button briefly shows Processing Refund... while the server calls the real payment provider's refund API, then a toast confirms success and the order's Status flips to Refunded.
Order refunded successfully toast with Status now showing Refunded for a PayPal course order

Verified end-to-end (2026-08-22), full 2×2 coverage: refunded one order from every course/digital-product × Stripe/PayPal combination, each previously purchased as a genuinely signed-out guest checkout.

ProviderItemCouponAmountProvider API confirmsAccess after refund
PayPalDigital productYes (10%)$44.99REFUNDEDOwnership row removed
PayPalCourseNo$197.00REFUNDEDEnrollment removed
StripeCourseYes (20%)$157.60refunded: trueEnrollment removed
StripeDigital productNo$49.99refunded: trueOwnership row removed

Every row was confirmed as a genuine provider-side refund by querying Stripe's and PayPal's own REST APIs directly after clicking Refund Order in the admin UI — not just trusting the local status flip. Every buyer's account was then checked individually: Total Spend dropped by exactly the refunded amount, and the corresponding "Enrolled Courses" / "Digital Products" panel entry was gone, confirming real access revocation rather than a cosmetic status change.

Order refunded successfully toast with Status now showing Refunded for a Stripe digital product order

Known limitation: refunding a coupon-discounted order does not return the coupon's usage slot — usageCount stays exactly where it was before the refund, confirmed on both coupon-discounted refunds above. If a coupon has a usage limit, a refunded order still permanently counts against it; the only workaround today is to manually raise the limit or issue a fresh code.

Orders list showing 2 Refunded orders and the Revenue total correctly reduced by both refunded amounts

13.3 Email

Email tab showing the Active Email Provider dropdown set to Resend, with an Active badge on the Resend card and its configured API key

Three providers can be configured simultaneously — Resend, SendGrid, and SMTP — each keeps its own credentials, and the Active Email Provider dropdown at the top picks which one actually sends. Switching providers later is instant: no redeploy, just re-save with a different selection. Sender Name applies across all three. Whichever provider is currently active shows an Active badge on its card, as shown above.

13.3.1 Setting Up Resend

  1. Sign in at resend.com/api-keys and click Create API key. "Full access" is fine for a single instance; name it something you'll recognize later (e.g. the app's domain).
  2. Pick a From Email domain. Either use Resend's shared sandbox address (onboarding@resend.dev — works immediately, but only reliably delivers to your own account's verified email) or, for real customer-facing sending, verify your own domain first under Domains (add the DNS records Resend gives you, wait for the "Verified" badge).
  3. Back in Admin → Settings → Email: set Active Email Provider to Resend, fill in Sender Name, paste the re_... key into API Key, and set From Email to an address at your verified domain (or the sandbox address).
  4. Click Save Changes.
  5. Optional — delivery tracking: create a webhook in Resend Dashboard → Webhooks pointing to https://<your-domain>/api/webhooks/resend, then paste its signing secret (whsec_...) into Webhook Signing Secret. Without this, emails still send — Email Logs just won't show delivered/bounced status updates after the fact.

13.3.2 Testing the Configuration

SMTP provider fields and the Send Test Email box at the bottom of the Email tab

Every provider card ends with a Send Test Email field. Enter any address, click send, and check three places to confirm it actually worked end-to-end rather than just being accepted by the form:

  1. The in-app toast confirms the request was submitted ("Test email sent") — this only means the API call succeeded, not that the message was delivered.
  2. Admin → Email Logs records the attempt with recipient, subject, provider, and status (sent means Resend/SendGrid/SMTP accepted it).
  3. The provider's own dashboard is the actual source of truth for delivery — e.g. Resend Dashboard → Emails shows each message's real status (Delivered, Bounced, etc.), which is the only way to catch a misconfigured From-domain that the app-side "sent" status won't reveal.

Verified working end-to-end on 2026-08-21: Resend configured with a verified courses.most.tw sender, test email sent to an external Gmail address, confirmed Delivered in both Email Logs and the Resend dashboard.

13.4 Analytics

Analytics tab with Google Analytics and Facebook Pixel fields

Third-party tracking IDs: Google Analytics (GA4), Facebook Pixel ID plus Meta CAPI Access Token and Meta Test Event Code, and Microsoft Clarity ID.

13.5 Layout

Layout tab with top bar text and hero headline fields

Edits the same homepage copy fields also reachable from Site Editor → Hero/Top Bar/Footer (Top Bar Text, Hero Headline, Hero Subheadline, Hero CTA Text/Link, Footer Branding). The two surfaces are kept in sync.

13.6 Integrations

Integrations tab with Google OAuth client ID and secret fields

Google OAuth (Client ID/Secret, redirect URI for "Sign in with Google"), plus file storage provider and AI provider configuration further down the tab.

14. License Settings

Path/admin/settings/license

License settings page showing active status, masked key, and instance ID

License settings show product license state and license-related actions.

PanelPurpose
License StatusActive/inactive badge, masked License Key, Activated date, Instance ID
ActionsRe-validate, Replace Key, Deactivate

License Status Reference

StatusMeaningAdmin AccessStudent AccessMCPContent Limits
maintenance_activeValid key, within maintenance windowFullFullYesNone
maintenance_expiredValid key, maintenance window lapsed. Runtime continuesFullFullYesNone
cache_graceLicense server unreachable; using cached state (7 days max)Full + bannerFullYesNone
dev_bypassNODE_ENV !== productionFullFullYesNone
inactiveNo key configured, within 30-day graceFullFullNo1 course, 1 section, 1 product
grace_expiredNo key configured, grace period elapsed (30+ days)LockedLockedNoN/A
disabledKey explicitly revoked by vendorLockedLockedNoN/A
refundedOrder refundedLockedLockedNoN/A

Key principle: maintenance_expired is not restricted. The platform runs on a lifetime license. Maintenance expiry only stops access to new updates, official support, and minting new Docker registry pull tokens. It does not disable the admin panel or student access.

Freemium Content Limits

When the license status is inactive, grace_expired, disabled, or refunded:

  • Courses list (/admin/courses): shows at most 1 course. A yellow banner appears showing how many courses are hidden.
  • Curriculum editor (/admin/courses/[id]/curriculum): shows at most 1 section. The Add New Section form is disabled.
  • Digital products list (/admin/products): shows at most 1 product. The Create Product form is disabled.

These restrictions are UI-layer only. All content remains in the database. Activating a valid license key immediately removes all restrictions; no migration needed.

14.1 Activating a License Key

  1. Go to Admin → Settings → License (/admin/settings/license)
  2. Paste the SC2-... key into the License Key field
  3. Click Activate

The key is validated offline. No internet connection is required during activation.

Grace-period banner still showing right after activation? The License Settings page itself updates immediately — check there first. The yellow "30-day grace period" banner on other admin pages can take a few minutes to catch up on deployments running multiple app instances (e.g. Fly.io's default 2-machine setup); it's a display lag only, not a sign activation failed, and it clears on its own. Single-container deployments (VPS/docker-compose) aren't affected.

14.2 Purchasing and Activating a Lemon Squeezy License

If you bought Teach Copilot through a Lemon Squeezy checkout instead of receiving a hand-issued SC2-... key, the key looks like a UUID (e.g. 41A637CD-4320-4216-B274-9045A444F7E5) and validates directly against Lemon Squeezy's own servers — no extra setup on your side.

Admin dashboard showing the yellow 'No license key — 30-day grace period' banner before activation
Before activation: a fresh install runs on a 30-day grace period with freemium limits.
  1. After checkout, open the confirmation email or Lemon Squeezy's My Orders page — your license key is listed under the order.
Lemon Squeezy order confirmation page showing a paid order and the issued license key (test-mode example)
Order confirmation showing the issued license key. (Screenshot from a Test-mode sandbox purchase.)
  1. Go to Admin → Settings → License (/admin/settings/license) and click Activate Key.
License settings page showing Inactive status and the Activate Key button before a Lemon Squeezy key is entered
  1. Paste the full key (with dashes) into the License Key field and click Apply. This step requires an internet connection — the key is checked against Lemon Squeezy's servers, not verified offline like SC2 keys.
License settings page showing Active status, masked license key, activation date, and maintenance date after successful Lemon Squeezy activation
After activation: status flips to Active, maintenance date is shown, and gated nav items like MCP Keys appear immediately.

15. MCP Keys

Path/admin/settings/mcp

MCP Keys page showing endpoint setup and client configuration snippet

Issue and revoke API keys for AI clients (Claude Desktop, Cursor, and any other MCP-compatible client) to connect to this instance's MCP server.

PanelPurpose
Endpoint & client setupShows the resolved, actually-reachable MCP server URL (/api/mcp), auth format, scopes explainer, and copy-paste mcp.json / curl snippets
Key tableIssue new keys with scoped permissions; the plaintext key is shown only once at creation. Only a hash is stored

The server speaks JSON-RPC 2.0 over the Streamable HTTP transport at a single flat URL. Every request must include Authorization: Bearer <key>; requests without a valid, active key are rejected. Each key only authorizes the tool categories checked at creation (or later via that key's row). A read-only key cannot call write or destructive tools even if the client requests them.

16. Recommended Admin QA Checklist

Use this checklist after major deployments.

AreaCheck
AuthAdmin sign-in works
DashboardDashboard loads without server errors
UsersUser list and detail page load; Add User sends a welcome email
CoursesCreate draft course, edit landing page, pricing, curriculum, settings
ProductsCreate draft product, upload file through UI, edit pricing/settings
OrdersOrder list and detail load; avoid real refund test unless intentional
Site EditorHomepage fields save and public homepage reflects changes
StorageUpload and cleanup tools use configured provider
EmailTest email sends and appears in Email Logs
SettingsPayment/storage/analytics test connections behave as expected
MCP KeysA freshly issued key can call tools/list against /api/mcp
Public pagesPublished course/product URLs load and use UUID paths

17. Screenshot Inventory

Screenshots in this manual are sourced from a full tour of a live production deployment's /admin, numbered in capture order and stored alongside this page under assets/images/.

ScreenshotPage
04-dashboard.jpgDashboard
05-courses-list.jpgCourses list
06-course-landing-page.jpgCourse landing page
07-course-curriculum.jpgCourse curriculum
08-course-intended-learners.jpgCourse intended learners (Coming Soon)
09-course-pricing.jpgCourse pricing
10-course-upsell.jpgCourse upsell
11-course-social-proof.jpgCourse social proof
12-course-reviews.jpgCourse reviews
13-course-comments.jpgCourse comments
14-course-settings.jpgCourse settings
15-course-publish-warning.jpgCourse publish confirmation dialog
16-products-list.jpgProducts list
17-product-landing-page.jpgProduct landing page
18-product-files.jpgProduct files
19-product-pricing.jpgProduct pricing
20-product-upsell.jpgProduct upsell
21-product-upsell-course-target.jpgProduct upsell targeting a course
22-product-social-proof.jpgProduct social proof
23-product-settings.jpgProduct settings
24-orders-list.jpgOrders list
25-users-list.pngUsers list (cropped, privacy-safe)
26-user-detail.jpgUser detail
27-sales-analytics.jpgSales report
28-settings-general.jpgSystem settings, General
29-settings-payments.jpgSystem settings, Payments
30-settings-email.jpgSystem settings, Email — active provider selector
30b-settings-email-test.jpgSystem settings, Email — SMTP fields and Send Test Email
31-settings-analytics.jpgSystem settings, Analytics
32-settings-layout.jpgSystem settings, Layout
33-settings-integrations.jpgSystem settings, Integrations
34-settings-license.jpgLicense settings
35-interactive-modules.jpgInteractive modules list
36-youtube-shares.jpgYouTube shares
37-storage.jpgStorage
38-email-logs.jpgEmail logs
39-mcp-keys.jpgMCP Keys
40-site-editor-overview.jpgSite editor overview
41-site-editor-courses-products.jpgSite editor, Courses & Products
42-site-editor-features.jpgSite editor, Features
43-site-editor-testimonials.jpgSite editor, Testimonials
44-site-editor-topbar.jpgSite editor, Top Bar
45-site-editor-footer.jpgSite editor, Footer
46-site-editor-legal-pages.jpgSite editor, Legal Pages