ShowcaseAIProduct & Technical Docs
← Back to app Testing Guide

Complete Product & Technical Reference

ShowcaseAI Documentation

Everything about the application in one place — architecture, technology, every feature, the credit and payment systems, the full API, the data models, and how it all fits together. Written to be read start-to-finish or used as a lookup reference.

Express 5 + MongoDB backend React 19 + Vite frontend 3 AI providers Credit-based billing Stripe payments

Overview #

ShowcaseAI is a self-hosted SaaS platform for creating AI-generated marketing media — videos, images, voiceovers, avatars, and ad copy — sold on the CodeCanyon / Envato marketplace.

Users generate content by describing what they want or uploading a product photo. Behind the scenes the platform orchestrates three external AI services to produce talking-avatar videos, product showcase clips, images, cloned voices, and written ad copy. Usage is metered with a credit balance that users top up via Stripe.

2
Applications (backend + frontend)
26
Database models
70+
API endpoints
3
AI providers (Kie · Speechify · NewPortAI)

Core capabilities

  • Video generation — talking-avatar videos (lip-synced), product showcase, product-avatar, fashion/try-on, and AI ad videos.
  • Image generation — text-to-image, product shoots, avatar imagery, and avatar+product compositing.
  • Voice — text-to-speech in many voices, plus custom voice cloning.
  • AI copywriting — ad scripts, marketing/sales copy, and a multi-variant Ad Copy Studio.
  • Brand Kits — reusable brand identity applied across generations.
  • Ad Localizer — recreate a talking-avatar video in many languages.
  • Community — social feed with posts, likes, and comments.
  • Billing — credit ledger + Stripe credit-pack purchases.
  • Admin — users, analytics, preset prompts, avatar templates, community moderation, credit packs, and a Settings screen where all provider keys are entered (no file editing needed).
🚀

New here? Read Requirements, then follow Installation & Running. The interactive npm run setup command creates your configuration, your admin account, and the starter data for you.

Architecture #

Two independent applications in one package (the backend/ and frontend/ folders), communicating over HTTP.

App Role Tech Port
backend/ Backend — business logic, database, AI orchestration, auth, credits, payments Node.js + Express 5 + Mongoose 5000
frontend/ Frontend — the browser UI users interact with React 19 + Vite 7 4001

How a request flows

  1. The user clicks something in the browser (frontend, port 4001).
  2. The frontend's API.request() helper sends an HTTP call to the backend at VITE_API_URL (e.g. http://localhost:5000/api/…), attaching the JWT token from the auth_token cookie in the Authorization: Bearer <token> header.
  3. The backend authenticates the token (checkAuth middleware → req.user; /admin/* routes additionally pass checkAdmin), runs the controller, talks to MongoDB and/or an AI provider, and returns JSON.
  4. For slow jobs (video/image), the backend returns immediately with a "processing" record; the provider later calls back (webhook) or is polled, and the record flips to "completed."
🔑

Token-based auth: there are no server-side session objects. Every request carries a JSON Web Token (JWT) that the backend verifies, together with a per-user token version stored on the user record so that logout, password changes and admin actions can revoke sessions instantly. Both apps must run at the same time (or the API can serve the built frontend itself — see Production Deployment).

Technology Stack #

Backend backend/

Concern Library
Runtime / framework Node.js 20.19+ (22 LTS recommended; an .nvmrc for Node 22 is included), ES modules, Express 5
Database MongoDB via Mongoose 8
Auth jsonwebtoken (JWT), bcryptjs (password hashing)
File uploads express-fileupload → AWS S3 (@aws-sdk/client-s3, lib-storage)
Payments stripe
AI / HTTP axios, form-data (Kie's OpenAI-compatible Gemini endpoint is called over plain HTTP)
Security helmet (security headers), express-rate-limit, cors
Email Mandrill (@mailchimp/mailchimp_transactional), nodemailer
Media music-metadata (audio duration)
Dev node --watch (built in)

Frontend frontend/

Concern Library
Framework / build React 19, Vite 7 (dev host 0.0.0.0, port 4001)
Routing React Router 7 (data router, createBrowserRouter)
State Zustand 5 with persist → sessionStorage (<APP_NAME>-storage). Only the user profile is persisted; the login token lives only in the auth_token cookie.
UI Ant Design 5 (+ @ant-design/icons), Bootstrap 5 + react-bootstrap
Forms react-hook-form
Media wavesurfer.js (waveforms), react-h5-audio-player
Charts, drag and drop, content react-apexcharts, @dnd-kit, react-markdown, react-infinite-scroll-component, re-resizable
Misc js-cookie, nanoid, dayjs, dompurify, @hotjar/browser (optional, disabled when VITE_HOTJAR_ID is empty)

Project Structure #

The download contains two applications plus a quick-start README. Paths below are relative to the folder you unzipped.

README.md                          # Quick start
documentation.html      # This documentation (open in a browser)
testing-guide.html      # Step-by-step testing guide
backend/                           # Backend (Express + MongoDB)
├── server.js                # Entry — starts the HTTP server
├── package.json             # npm scripts (dev, start, setup, check…)
├── .nvmrc                   # Node.js 22 (nvm use)
├── .env.example             # Template for .env
├── .env                     # YOUR configuration — not in the package; created by npm run setup. Never share it
├── src/
│   ├── app.js               # Express app, security middleware, /api/health, Stripe webhook mount
│   ├── routes.js            # API route registry (all /api/* except /api/health and the Stripe webhook, which are in app.js)
│   ├── controllers/         # Request handlers (+ webhook/ subfolder)
│   ├── models/              # Mongoose schemas (26 models)
│   ├── helpers/             # CreditHelper, SettingsHelper, LlmHelper, UploadHelper, Middleware…
│   ├── lib/                 # AI providers: kieLib, tts, newportai, render, stripe
│   └── emailTemplates/      # resetPassword.html, userCreated.html
├── public/logo.png          # Logo shown in emails (served at <BACKEND_URL>/logo.png)
├── tmp/                     # Temporary upload files (cleaned automatically)
├── scripts/                 # setup, check, security-smoke (+ internal helpers)
├── seed/                    # Starter data loaded by npm run setup
└── deploy/
    ├── ecosystem.config.cjs # PM2 process file
    └── nginx.conf.example   # nginx reverse-proxy example

frontend/                          # Frontend (React 19 + Vite 7)
├── .nvmrc                   # Node.js 22 (nvm use)
├── .env.example             # Template for the VITE_* settings
├── .env                     # Your VITE_* settings — not in the package; created by npm run setup (backend)
├── vite.config.js           # Dev server (port 4001)
├── public/assets/images/    # favicon, logo and other static images
└── src/
    ├── routes/              # index / userRoutes / adminRoutes / authRoutes
    ├── pages/               # user/, admin/, user/create/, user/tool/, auth/
    ├── components/          # common/, avatars/, AiAds/, Popups/, profile/
    ├── store.js             # Zustand auth/user state
    └── utils/               # API.js (fetch wrapper), Helper.js

Backend npm scripts (run inside backend/)

Command What it does
npm run setup Interactive installer: creates backend/.env and frontend/.env from the .env.example files (MongoDB URL, generated secrets, API URL), connects to MongoDB, creates the admin and loads the starter data. See Installation.
npm run dev Starts the API and restarts it automatically when code changes (Node's built-in --watch). For development.
npm start Starts the API with plain Node.js. For production (usually through PM2).
npm run check Installation health check (configuration, database, admin, starter data, settings).
npm run smoke Runs the automated security checks (scripts/security-smoke.js, which you can also start with node scripts/security-smoke.js) against a running API. See Security.

Requirements & Technical Specifications #

What you need before installing. Items marked optional can be added later — the app starts without them and simply hides or disables the related feature.

💳

Third-party services & costs — not included with your purchase. ShowcaseAI connects to these third-party services:

  • Kie.ai — AI video and image models, ad copy and translation (required, paid).
  • AWS S3 — storing uploads and generated media (required, paid).
  • Speechify — voice library, text-to-speech and voice cloning (paid).
  • NewPortAI — lip-sync for talking videos and the Ad Localizer (paid).
  • Stripe — selling credit packs (optional, Stripe fees apply).
  • SMTP or Mandrill — welcome and password-reset emails (optional).
  • Pixabay, Unsplash, Pexels — stock image/video search (optional, free keys).

Accounts, API keys and usage fees for these services are not included with the purchase. You sign up with each provider yourself and pay them directly, according to their pricing. The AI features do not work until the required keys are entered in Admin → Settings. Prices and availability of these services are set by the providers and can change.

Requirement Needed for Notes
A server or VPS that can run Node.js Hosting the API (and the built frontend) Any Linux, Windows or macOS server or VPS. Shared PHP hosting is not enough — it cannot run a Node.js process.
Node.js 20.19+ (22 LTS recommended) and npm 10+ Running both applications Check with node -v and npm -v. Download from nodejs.org. The .nvmrc files in backend/ and frontend/ select Node 22 (with nvm on macOS/Linux, run nvm use).
MongoDB 6.0+ All application data Self-hosted or a MongoDB Atlas cluster. On Atlas, add your server's IP address under Network Access (IP allow-list), otherwise the connection is refused.
A reverse proxy with HTTPS (production) Serving the app and the API on your domains An nginx example (backend/deploy/nginx.conf.example) and a PM2 process file (backend/deploy/ecosystem.config.cjs) are included. See Production Deployment.
AWS S3 bucket Storing uploaded and generated media Files must be publicly readable (bucket policy, or ACLs) because the AI providers download them. See Admin → Settings → Storage.
Kie.ai API key All image and video generation, and all AI text (scripts, ad copy, translation) via Kie's Gemini endpoint From your kie.ai account → API keys.
Speechify API key Text-to-speech and voice cloning From the Speechify API dashboard.
NewPortAI API key Lip-sync talking avatars and the Ad Localizer From newportai.com.
Stripe account (optional) Selling credit packs Secret key + webhook signing secret.
SMTP server or Mandrill (optional) Password-reset and welcome emails Without it, emails are skipped and a warning is logged.
Pixabay / Unsplash / Pexels keys (optional) Stock media search Each one is independent.
A public https URL for the API Receiving generation results from Kie by webhook Set as BACKEND_URL. For local testing use a tunnel such as ngrok.

Technical specifications

Part Technology
Backend Node.js 20.19+ (22 LTS recommended), Express 5, Mongoose 8
Frontend React 19 + Vite 7, built into static files (npm run build); runs in modern browsers (current Chrome, Edge, Firefox, Safari)
Database MongoDB 6.0+ (self-hosted or MongoDB Atlas)
Storage AWS S3 bucket
Production server Any Linux/Windows/macOS server or VPS with a reverse proxy and HTTPS; PM2 and nginx examples in backend/deploy/
🔌

Why a public URL? Kie.ai sends finished images and videos back to <BACKEND_URL>/api/kie/webhook. On your own computer http://localhost:5000 is not reachable from the internet, so generations stay "Processing" (credits are returned automatically after RESERVATION_TTL_MIN, default 120 minutes). For local testing run a tunnel, for example ngrok http 5000, and set BACKEND_URL to the https://… address it prints (in Admin → Settings → Site & Branding, or in backend/.env).

Installation & Running #

Install the backend first, then the frontend. The whole process takes about ten minutes. Run the commands in a terminal (on Windows: PowerShell or Command Prompt).

🔐

Secrets – set once, never change. Run npm run setup before you use the app or save any key in Admin → Settings.

  • SETTINGS_ENCRYPTION_KEY encrypts every provider key you save in Admin → Settings (AES-256-GCM; the encryption key is derived from it, and JWT_SECRET is used if it is not set). If it changes, every saved key becomes unreadable and the related features stop working. The saved values stay in the database, so putting the original value back (and restarting) makes them readable again; otherwise you must enter all keys again.
  • Changing JWT_SECRET logs every user out.
  • Keep a backup of backend/.env. If the file is lost while the database still holds data, restore the same values — a newly generated key cannot decrypt the old data.
  • --rotate-secrets (an option of npm run setup) replaces only JWT_SECRET; it never touches SETTINGS_ENCRYPTION_KEY.

1 · Install the backend packages

$ cd backend
$ npm install

2 · Run the interactive installer

$ npm run setup

The installer does everything needed for a working first start:

  1. Checks your Node.js version.
  2. Creates backend/.env from backend/.env.example. The package contains no .env file — no database URL or secret is included. If backend/.env already exists, setup keeps it and only fills in the missing values.
  3. Asks for the MongoDB connection string. Press Enter for a local MongoDB (mongodb://127.0.0.1:27017/showcaseai) or paste your own, for example an Atlas mongodb+srv://… URL (see how to get one). It also asks for the FRONTEND_URL (default http://localhost:4001) and the BACKEND_URL (default http://localhost:5000).
  4. Generates a random JWT_SECRET and a random SETTINGS_ENCRYPTION_KEY and writes them into .env.
  5. Creates frontend/.env from frontend/.env.example, with VITE_API_URL=<your BACKEND_URL>/api/, if it does not exist yet.
  6. Connects to MongoDB.
  7. Creates the admin account. It asks for the admin email (default admin@showcaseai.app), name and password. There is no default password: leave it empty and setup generates a strong random one and prints it once at the end — save it. Public demo passwords such as User@123# are refused.
  8. Loads the starter data and one demo user — the demo user only when NODE_ENV is not production (see Admin Account & Starter Data). Content such as prompts and avatar templates is added by you afterwards (see Add your own content).
  9. Prints the logins and the next steps.
♻️

Run it once. If setup stops halfway (for example MongoDB is not running), fix the problem and run it again. Once it has finished, running npm run setup again stops with "already set up" and changes nothing — see Admin Account & Starter Data for how to change things afterwards, or how to start over with --reset.

Non-interactive mode (servers, scripts): npm run setup -- --yes asks no questions and uses the values already in .env, or the defaults. There is no default database URL in this mode: mongoDBURL must already be set in backend/.env, or you pass --mongo <url>. Without --admin-password it generates a random admin password and prints it once (save it); without --admin-email the admin is admin@showcaseai.app. Pass your own on a live server. Options:

Option Effect
--yes Do not ask questions; use .env values or defaults. Needs mongoDBURL in .env or --mongo.
--no-demo Create the admin and load starter data only — no demo user. (With NODE_ENV=production in backend/.env the demo user is never created.)
--mongo <url> MongoDB connection string. Required with --yes unless mongoDBURL is already in .env.
--admin-email, --admin-password, --admin-name The admin account created by the first install (instead of answering the questions). Environment alternatives: ADMIN_EMAIL, ADMIN_PASSWORD, ADMIN_NAME. With --yes and no password given, setup generates a random admin password and prints it once — save it.
--frontend-url, --backend-url Public URLs of the frontend and of the API (default http://localhost:4001 and http://localhost:5000). Editable later in Admin → Settings → Site & Branding.
--rotate-secrets Generate a new JWT_SECRET during an install. It never changes SETTINGS_ENCRYPTION_KEY.
--reset Deletes ALL data in the database (users, media records, payments, settings and saved keys) and installs again. Shows what will be deleted and asks you to type the database name to confirm. Non-interactive: --reset --confirm=<database name>.

Example:

$ npm run setup -- --yes --no-demo --mongo "mongodb://127.0.0.1:27017/showcaseai" \
    --admin-email you@example.com --admin-password "choose-a-strong-password" --admin-name "Your Name"

3 · Start the backend

$ npm run dev        # development (restarts on code changes)
$ npm start          # production

The API listens on http://localhost:5000. Check it is alive by opening http://localhost:5000/api/health in a browser — it answers {"status":"success","message":"ok"}.

4 · Install and start the frontend

Open a second terminal (leave the backend running):

$ cd frontend
$ npm install
$ npm run dev             # http://localhost:4001

frontend/.env was created by npm run setup, with VITE_API_URL=http://localhost:5000/api/ (your Backend URL + /api/), so it points at the backend. If the file is missing, the frontend still uses http://localhost:5000/api/; to change it, copy .env.example to .env and edit it. Open http://localhost:4001 in a browser.

5 · Log in and enter your provider keys

  1. Log in with the admin account you created in step 2 — the email you entered (default admin@showcaseai.app) and the password you entered, or the generated one setup printed.
  2. The AI features need two things. (a) Your own, paid provider keys entered in Admin → Settings: Kie.ai, AWS S3, Speechify and NewPortAI (see Third-party services & costs). (b) A public https BACKEND_URL (Admin → Settings → Site & Branding), because Kie.ai sends finished results to <BACKEND_URL>/api/kie/webhook. On localhost, videos, avatars and images stay "Processing". For local testing run a tunnel such as ngrok http 5000 and set BACKEND_URL to the https address it shows — see Why a public URL?.
  3. Open Admin → Settings.
  4. Enter your Kie.ai, Speechify and NewPortAI keys, your S3 storage details and (optionally) Stripe, email and stock-media keys. Click Save.
  5. Click each Test button to confirm the key works. See Admin → Settings for every field.
  6. Add your own content — prompts, avatar templates, community posts: Add your own content (manual).

6 · Verify the installation

$ cd backend
$ npm run check

npm run check verifies that .env is valid, the database connection works, an admin exists and the starter data is loaded. It lists which settings are configured (it never prints their values) and warns if BACKEND_URL is still localhost.

🗄️

The database is yours. ShowcaseAI does not include a hosted database, and the package contains no MongoDB URL, password or secret. Use a local MongoDB server or your own MongoDB Atlas cluster and enter its connection string during setup.

Admin Account & Starter Data #

There is one way to install: npm run setup. It creates the admin account and a small set of starter data for you — there is nothing to import by hand. After that, you change everything from the admin panel and add your own content. Visitors can register themselves on the sign-up page (/register), and admins can also create users in Admin → Users.

What npm run setup does

$ cd backend
$ npm install
$ npm run setup
  1. Creates backend/.env from backend/.env.example (the package contains no .env file) and generates the two secrets.
  2. Asks for your MongoDB connection string and site URLs (press Enter to accept the defaults), and creates frontend/.env pointing at your Backend URL.
  3. Creates the admin account with the email, name and password you type. Leave the password empty to get a generated one, printed once at the end — save it. There is no default admin password.
  4. Loads the starter data (Creator Studio models, community categories, credit packs, dropdown lists) and one demo user (not when NODE_ENV=production).
  5. Prints the login details.
✅

Setup runs once. Running npm run setup on an installed database stops with "already set up" and changes nothing, so it can never overwrite your work. To start completely fresh, use npm run setup -- --reset (see below).

After setup — change whatever you want

To change… Go to
Your admin password Profile
Your admin email or name MongoDB Compass. Admin accounts are not listed in Admin → Users (see Changing the admin login).
Customer accounts: name, password, credits and active/inactive status Admin → Users (a user's email cannot be edited)
API keys, site URLs, email, AI text price Admin → Settings
Credit packs and prices Admin → Credit Packs
Community categories Admin → Community Categories
Preset prompts Admin → Prompts
Library avatars Admin → Product / Video Avatar Templates
Creator Studio model prices The aimodelcatalogs collection in your database (any MongoDB tool)

Changing the admin login

There is exactly one admin account. Change it like this:

To change… How
The admin password Log in → Profile → Change password. Forgot it? Use Forgot password on the login page (needs email set up in Admin → Settings → Email).
The admin email or name Edit the admin in the database with MongoDB Compass (steps below).
Everything back to a clean install npm run setup -- --reset — deletes all data after you confirm.

Edit the admin with MongoDB Compass

  1. Install MongoDB Compass (free) from https://www.mongodb.com/products/tools/compass.
  2. Click Add new connection, paste the mongoDBURL from backend/.env and connect.
  3. Open your database (e.g. showcaseai) → the users collection.
  4. Filter with { "role": "admin" }, click the pencil icon on the document, change email (lower-case) and/or name, then Update. Log in with the new email.

Locked out and email is not set up?

  1. In a terminal inside backend, create a password hash (replace the password):
    $ node -e "console.log(require('bcryptjs').hashSync('MyNewPassword123', 10))"
  2. In Compass, edit the admin document and replace the value of password with the printed text (it starts with $2), then Update.
  3. Log in with the new password and change it again from Profile if you like.
⚠️

Only edit what is described here. Do not change role, _id or other fields of the admin document, and never create a second admin.

Starting over (reset)

$ npm run setup -- --reset

Setup lists what will be deleted — every user account, video and image record, payment record, credit, setting and saved API key — and asks you to type the database name to confirm. Nothing is deleted unless you confirm. Then it installs everything fresh, exactly like the first time. Files already uploaded to S3 are not removed. Make a backup first if you might need the data (e.g. mongodump).

Logins after setup

Email Password Role Credits
The email you entered in setup (default admin@showcaseai.app) The password you entered, or the generated one setup printed Admin — (admins don't use credits)
user@showcaseai.app User@123# User (demo) 500

Passwords are stored as bcrypt hashes. The demo user is for local testing only. Its password is public, so delete it in Admin → Users before you go live. It is never created when NODE_ENV=production is set in backend/.env (or with --no-demo).

Starter data (what setup creates)

Collection Contents
aimodelcatalogs 11 Creator Studio models. Video: seedance1-lite, seedance1-pro, seedance1-pro-fast, kling21-statndard, veo3-fast, grok-imagine-15. Image: z-image, nano-banana, seedream45, flux2-pro, gpt-image15. Each has example credit prices (pricingMatrix per resolution/duration, or costPerSecond) — adjust them to your margins.
sitedatas Ethnicity and profession dropdown lists used by the admin avatar-template form.
communitycategories 4 community categories (you can edit, reorder, add and delete them).
creditpacks 3 packs: Starter (100 credits, $9), Pro (500 credits, $39), Business (2000 credits, $129).
users, credits The admin, plus one demo user and that user's 500 credits (skip the demo user with npm run setup -- --no-demo; it is never created when NODE_ENV=production).
🧑‍🎨

What setup does NOT create: prompts, avatar templates, community posts, brand kits, voices and generated images or videos. A fresh installation starts without them — you add the ones you want from the admin panel (see Add your own content (manual)). Uploading avatar templates requires S3 storage to be configured.

Add your own content (manual) #

A fresh installation contains only the admin, one demo user, the AI model catalog, community categories, credit packs and dropdown lists. Everything below is added by you, from the admin panel, in a few minutes. Log in as the admin first.

Prompts Admin → Prompts

Preset prompts appear in the prompt picker of the studios so users can start from a ready-made idea. Until you add some, that picker is empty (users can still write their own prompt).

  1. Open Admin → Prompts in the sidebar.
  2. Use the filter at the top to choose the prompt type (Fashion Shoot Video, Product Shoot Video, Product Shoot Image, Product Avatar Image or Image To Avatar). The list shows only that type.
  3. Click Add Prompt.
  4. Choose the Prompt Type, type a Title and write the full Prompt text (all three are required).
  5. Optional: under Reference Image click the box and choose a PNG, JPG or WEBP file (under 5 MB). Uploading needs S3 storage (Admin → Settings → Storage).
  6. Click Submit. To change a prompt later use the edit (pencil) icon; Update Prompt saves it. The trash icon asks for confirmation and deletes it.

Avatar templates Admin → Product Avatar · Video Avatar

The ready-made avatars users pick in Avatar Studio. Product Avatar templates are used by the product/fashion avatar tools; Video Avatar templates by the talking-avatar tools. Both pages work the same way and both need S3 to be configured.

  1. Open Admin → Product Avatar or Admin → Video Avatar.
  2. Click Add Product Avatar Template / Add Video Avatar Template.
  3. Fill in the details first: Full Name, Age Range, Ethnicity, Profession (the dropdown lists come from the starter data) and Gender, then Voice Type and Voice Name. Product Avatar templates also have an Avatar Type (Fashion Avatar or Product Avatar).
  4. Click Save Details. The upload boxes (image, video, matted video, thumbnail) become active only after the details have been saved once.
  5. Upload the files, then use the status switch in the list to publish or hide the template. Edit or delete it from the same list.

Community categories and posts Admin → Community Categories · Community

  1. Admin → Community Categories lists the 4 starter categories. Click + Add Category, enter a Name (required), an optional description and an optional Icon SVG (paste SVG code), then save. Drag rows to change their order; use the pencil or trash icons to edit or delete.
  2. Admin → Community opens the community feed. Click + Start Discussion, enter a Title, optional Description, pick a Category (required), add optional hashtags and up to 3 JPEG/PNG images, then post.
  3. As admin you can also pin a post to the top of the feed or remove any post. Posts by your users appear here automatically.

Credit packs Admin → Credit Packs

Setup creates three example packs (Starter, Pro, Business). Adjust them to your own prices.

  1. Open Admin → Credit Packs.
  2. Click Add Credit Pack (or edit an existing pack).
  3. Enter the Name, an optional description, the number of Credits and the Price; leave Currency as usd or change it; set a Sort Order; tick Mark as "Most Popular" to highlight a pack.
  4. Click Create (or Update). Active packs appear on the users' Buy Credits page; real payments need the Stripe keys in Settings.

Provider keys Admin → Settings

  1. Open Admin → Settings. The settings are split into groups (Site & Branding, AI Providers, Storage (AWS S3), Email / Notifications, Payments (Stripe), Credit Prices, Limits & Security, Stock Media); the links at the top jump to a group.
  2. Paste the keys you have (see Keys & Credentials for where to get each one). Leave a field empty to keep its current value.
  3. Click Save Settings at the bottom, then press the Test button next to each provider. Test checks the saved value, so save first.
🔐

Secrets – set once, never change. Save provider keys only after npm run setup has run. If SETTINGS_ENCRYPTION_KEY (in backend/.env) ever changes, all keys saved here become unreadable and show as not configured. Restore the original value and restart to get them back; otherwise you would have to re-enter every key. Details.

Other things you may want to add

  • Users and credits: Admin → Users (add users, give credits).
  • Model prices: the example credit prices of the Creator Studio models live in the aimodelcatalogs collection (see Starter data).
  • Brand kits, voices and generations are created by users in their own studios and need no admin setup.

Configuration (.env) #

Server settings live in backend/.env (backend) and frontend/.env (frontend). The package contains only the .env.example templates; npm run setup creates both .env files from them. Never commit or share them.

🔐

Secrets – set once, never change. JWT_SECRET and SETTINGS_ENCRYPTION_KEY are empty in backend/.env.example; npm run setup generates them once. Changing SETTINGS_ENCRYPTION_KEY makes every key saved in Admin → Settings unreadable until the original value is restored. Changing JWT_SECRET logs everyone out. Back up backend/.env — if it is lost, restore the same values; a new key cannot decrypt old data. Details.

🔑

Provider keys belong in Admin → Settings. Keys for Kie, Speechify, NewPortAI, Stripe, S3, email and stock media are best entered in the admin panel (stored encrypted, applied without a restart). The same names in .env only act as a fallback. See Admin → Settings, and Keys & Credentials for where to get every value.

Backend — backend/.env

This file is not in the package. npm run setup creates it from backend/.env.example and fills in mongoDBURL, JWT_SECRET and SETTINGS_ENCRYPTION_KEY for you (once). It only holds what the server needs before it can start; everything else — site URLs, API keys, email, payments, storage, credit prices, session length, rate limits and upload limits — is managed in Admin → Settings.

Variable Default Purpose
mongoDBURL required empty until setup MongoDB connection string including the database name, e.g. mongodb://127.0.0.1:27017/showcaseai or an Atlas mongodb+srv://… URL. See how to get it.
JWT_SECRET required empty until setup Signs login tokens. At least 32 characters (the API refuses to start in production with a shorter value). Generated by npm run setup. Set once — changing it logs everyone out.
SETTINGS_ENCRYPTION_KEY required empty until setup Encrypts the keys saved in Admin → Settings. Generated by npm run setup. Set once, never change — if it changes, every saved key becomes unreadable and must be entered again.
PORT 5000 Port the API listens on.
NODE_ENV development Set to production on your live server (enables stricter checks).
Advanced (optional) — commented out in .env.example; leave them unless you need them.
TRUST_PROXY empty Empty = trust nginx on the same server. Set to 1 behind a cloud load balancer.
SERVE_FRONTEND_DIST empty e.g. ../frontend/dist — serve the built frontend from the API on one port.
SALT_ROUNDS 10 bcrypt cost for passwords (10–12).
WEBHOOK_SECRET derived from JWT_SECRET Separate secret for provider webhooks.
AWS_S3_FOLDER showcaseai-cc Folder inside the S3 bucket where every file is stored. Can also be set in Admin → Settings → Storage.
ALLOW_HTTP_REMOTE_URLS false In production only https provider files are downloaded unless this is true.
ℹ️

For compatibility, any Admin → Settings field can also be given in .env under its key name (e.g. KIE_API_KEY=…). A value saved in Admin → Settings always wins. You don't need this — the admin panel is the recommended way.

Frontend — frontend/.env

npm run setup (in backend/) creates this file from frontend/.env.example if it does not exist yet, with VITE_API_URL set to your Backend URL + /api/. Without the file, the frontend uses http://localhost:5000/api/.

Variable Purpose
VITE_API_URL required Base URL of the backend API, ending with /api/ (e.g. https://api.example.com/api/; locally http://localhost:5000/api/).
VITE_APP_NAME Product name shown in the browser tab, page titles and footer.
VITE_COOKIE_DOMAIN Only to share the login across subdomains (e.g. .example.com). Leave empty otherwise.
VITE_HOTJAR_ID Optional Hotjar analytics. Disabled when empty.
VITE_TERMS_URL, VITE_PRIVACY_URL Links in the login-page footer. When empty, they open the built-in Terms of Service and Privacy Policy pages (/terms-conditions and /privacy-policy) — replace their text first, see Branding & Customisation.
VITE_POWERED_BY_TEXT, VITE_POWERED_BY_URL "Powered by" credit in the login-page footer. Hidden when empty.
VITE_SAMPLE_MEDIA_BASE Base URL that hosts the sample product images offered under "Try Samples" on the Create pages. Defaults to https://d3orgd3vfbg4nb.cloudfront.net, a sample-image CDN hosted by the vendor and used only to show example images. You can point it at your own host — see Sample images.

Sample images

The Create pages (Product Showcase, Product Avatar and AI Ads) offer four example product photos under Try Samples. The browser loads them from VITE_SAMPLE_MEDIA_BASE, which by default is the vendor-hosted sample-image CDN https://d3orgd3vfbg4nb.cloudfront.net. It is used only to display these example images; nothing else in the app depends on it. If you prefer to host the images yourself, upload four JPEG files to a bucket or web server you control using this path convention, then set VITE_SAMPLE_MEDIA_BASE to its base URL and rebuild the frontend:

<base>/assets/assets/sample-images/1.jpg … 4.jpg
<base>/assets/assets/sample-images/thumb/1.jpg … 4.jpg   # smaller thumbnails of the same images
🔁

Backend: after editing backend/.env, restart the API (Ctrl+C then npm run dev, or pm2 restart in production). Frontend: VITE_* values are compiled into the build — run npm run build again after changing them. Never put secrets in the frontend .env.

Keys & Credentials — Step by Step #

Every value ShowcaseAI needs, what it is for, where it goes and exactly where to get it. Values marked .env go in backend/.env (most are written for you by npm run setup). Values marked Settings are entered in Admin → Settings after you log in — click Save, then the Test button next to the key.

Overview

Value Where it goes Needed for Required?
mongoDBURL.env (setup asks)Everything — the databaseYes
JWT_SECRET, SETTINGS_ENCRYPTION_KEY.env (setup generates)Logins, encrypted settingsYes (automatic)
FRONTEND_URL, BACKEND_URLSettings → Site & Branding (setup asks)Email links, payments, AI callbacksYes
VITE_API_URLfrontend/.envFrontend talking to the APIYes
KIE_API_KEYSettings → AI ProvidersAll image & video generation, all AI textYes for AI features
AWS_* (S3)Settings → StorageEvery upload and generated fileYes for AI features
SPEECHIFY_API_KEYSettings → AI ProvidersVoices, text-to-speech, voice cloningFor voice / avatar features
NewPortAI_KEYSettings → AI ProvidersTalking avatars (lip-sync), Ad LocalizerFor avatar features
Email (SMTP or Mandrill)Settings → EmailPassword reset & welcome emailsRecommended
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRETSettings → PaymentsSelling credit packsOptional
PIXABAY_KEY, UNSPLASH_KEY, PEXELS_KEYSettings → Stock MediaStock photo/video searchOptional

1. MongoDB — mongoDBURL

The connection string of your database, including the database name. npm run setup asks for it and writes it to .env.

Option A — MongoDB on your own computer/server (free)

  1. Install MongoDB Community Server from https://www.mongodb.com/try/download/community (on Windows tick "Install as a Service"; on Ubuntu follow the official apt instructions).
  2. Use this connection string: mongodb://127.0.0.1:27017/showcaseai

Option B — MongoDB Atlas (cloud, free tier available)

  1. Create an account at https://www.mongodb.com/cloud/atlas and create a cluster (the free M0 tier is fine to start).
  2. Database Access → Add New Database User → choose a username and a password (avoid @ : / ? in the password, or URL-encode them).
  3. Network Access → Add IP Address → add your server's public IP (use 0.0.0.0/0 only for a quick test).
  4. Database → Connect → Drivers → copy the mongodb+srv://… string.
  5. Replace <password> with your password and add the database name before the ?:
mongoDBURL=mongodb+srv://myuser:MyPassword123@cluster0.abcde.mongodb.net/showcaseai?retryWrites=true&w=majority

2. Secrets — JWT_SECRET and SETTINGS_ENCRYPTION_KEY

You don't need to do anything: npm run setup generates both and saves them in .env (they are empty in .env.example). Create one by hand only on a server where you prepare the .env yourself:

$ node -e "console.log(require('crypto').randomBytes(48).toString('base64url'))"
⚠️

Keep a private backup of both values (they live in backend/.env). Changing JWT_SECRET logs everyone out. Changing SETTINGS_ENCRYPTION_KEY makes the keys saved in Admin → Settings unreadable (you would have to enter them again). See Secrets – set once, never change.

3. Site URLs — FRONTEND_URL, BACKEND_URL, VITE_API_URL

Value On your computer On a live server
FRONTEND_URL (Admin → Settings)http://localhost:4001https://app.example.com
BACKEND_URL (Admin → Settings)http://localhost:5000 *https://api.example.com
VITE_API_URL (frontend/.env)http://localhost:5000/api/https://api.example.com/api/
🌐

* Kie.ai sends finished images and videos to BACKEND_URL, so it must be reachable from the internet over https. On your own computer, generations stay "processing" unless you open a tunnel: install ngrok (https://ngrok.com), run ngrok http 5000 and set BACKEND_URL (Admin → Settings → Site & Branding) to the https://….ngrok-free.app address it shows.

4. Kie.ai — KIE_API_KEY

Powers all image and video generation (Seedance, Kling, Veo, Grok, Nano Banana, Seedream, Flux, GPT Image…) and all AI text (scripts, ad copy, translations).

  1. Sign up at https://kie.ai.
  2. Open the API Key page in your Kie dashboard and create/copy your key.
  3. Add credit to your Kie account (Billing) — generation fails without a balance.
  4. Admin → Settings → AI Providers → paste into Kie API Key → Save → Test. The test shows your remaining Kie credit.

5. AWS S3 storage — AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_S3_BUCKET

Every uploaded and generated file is stored in your S3 bucket. The files must be publicly readable because the AI providers download them by URL.

  1. Sign in to https://console.aws.amazon.com → S3 → Create bucket. Pick a name (e.g. showcaseai-media) and a region (e.g. us-east-1). These are AWS_S3_BUCKET and AWS_REGION.
  2. In the bucket → Permissions → Block public access → Edit → untick Block all public access → Save.
  3. Same tab → Bucket policy → paste the policy below (replace the bucket name) → Save.
  4. Same tab → Cross-origin resource sharing (CORS) → paste the CORS rules below (needed for video thumbnails in the browser).
  5. IAM → Users → Create user (e.g. showcaseai) → Add permissions → Create inline policy → JSON → paste the IAM policy below.
  6. Open the user → Security credentials → Create access key → "Application running outside AWS" → copy the Access key ID and Secret access key (the secret is shown only once).
  7. Admin → Settings → Storage (AWS S3) → fill Region, Access Key ID, Secret Access Key and Bucket Name → Save → Test.

Bucket policy (public read):

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "PublicRead",
    "Effect": "Allow",
    "Principal": "*",
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::showcaseai-media/*"
  }]
}

CORS:

[{
  "AllowedOrigins": ["https://app.example.com", "http://localhost:4001"],
  "AllowedMethods": ["GET", "HEAD"],
  "AllowedHeaders": ["*"],
  "MaxAgeSeconds": 3600
}]

IAM policy (least privilege):

{
  "Version": "2012-10-17",
  "Statement": [
    { "Effect": "Allow", "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
      "Resource": "arn:aws:s3:::showcaseai-media/*" },
    { "Effect": "Allow", "Action": ["s3:ListBucket"],
      "Resource": "arn:aws:s3:::showcaseai-media" }
  ]
}

Optional: AWS_S3_PUBLIC_URL = a CloudFront/CDN domain in front of the bucket (e.g. https://media.example.com). AWS_S3_ACL = public-read only if your bucket uses ACLs instead of a bucket policy (then also add s3:PutObjectAcl to the IAM policy). Otherwise leave both empty. Every file is stored under one folder in the bucket (AWS_S3_FOLDER, default showcaseai-cc), so a bucket shared with another project stays tidy.

6. Speechify — SPEECHIFY_API_KEY

Powers the voice library, text-to-speech, voice-overs and voice cloning.

  1. Sign up for the Speechify API at https://console.sws.speechify.com.
  2. Open API Keys → create a key → copy it.
  3. Admin → Settings → AI Providers → Speechify API Key → Save → Test. The test also imports the Speechify voice library ("Imported N Speechify voices"). It is also imported automatically the first time someone opens the voice picker.

7. NewPortAI — NewPortAI_KEY

Powers lip-sync for talking-avatar videos and the Ad Localizer.

  1. Sign up at https://www.newportai.com.
  2. Open your dashboard → API Key → copy it, and add credit to your account.
  3. Admin → Settings → AI Providers → NewPortAI Key → Save → Test.

8. Email — SMTP or Mandrill

Used for password-reset links and the optional welcome email when an admin adds a user. Choose one provider with the Send Mail Via switch (Admin → Settings → Email / Notifications).

SMTP (any email provider)

Provider SMTP_HOST SMTP_PORT SMTP_USERNAME / SMTP_PASSWORD
Gmail / Google Workspacesmtp.gmail.com587Your address / an App Password (Google Account → Security → 2-Step Verification → App passwords)
SendGridsmtp.sendgrid.net587apikey / your SendGrid API key
Amazon SESemail-smtp.<region>.amazonaws.com587SMTP credentials from SES → SMTP settings
Mailgunsmtp.mailgun.org587SMTP login / password from Mailgun → Domain settings
Your hosting mailboxfrom your host465 (SSL) or 587Mailbox address / password

Then set Send Mail Via = SMTP, FROM_EMAIL (an address your provider allows you to send from, e.g. no-reply@example.com) and SUPPORT_EMAIL (shown in emails). Port 465 uses SSL automatically; 587 uses STARTTLS.

Mandrill (Mailchimp Transactional)

  1. In Mailchimp open Transactional (Mandrill) → Settings → Add API key → copy it.
  2. Under Domains add and verify your sending domain (DKIM/SPF records).
  3. Admin → Settings → Email: Send Mail Via = Mandrill, paste Mandrill API Key, set From Email on the verified domain. If Mandrill fails and SMTP is also filled in, SMTP is used as a fallback.

9. Stripe — STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET (optional)

Needed only if you sell credit packs on the Buy Credits page.

  1. Sign in at https://dashboard.stripe.com (use Test mode first).
  2. Developers → API keys → reveal and copy the Secret key (sk_test_… / sk_live_…).
  3. Developers → Webhooks → Add endpoint → URL https://api.example.com/api/payment/stripe/webhook → events checkout.session.completed and checkout.session.async_payment_succeeded → Add.
  4. Open the endpoint → Signing secret → reveal and copy (whsec_…).
  5. Admin → Settings → Payments (Stripe) → paste both → Save → Test. Then check your packs in Admin → Credit Packs.

Testing on your own computer: install the Stripe CLI and run stripe listen --forward-to localhost:5000/api/payment/stripe/webhook — it prints a whsec_… secret to use while it runs. Test card: 4242 4242 4242 4242, any future date, any CVC.

10. Stock media — PIXABAY_KEY, UNSPLASH_KEY, PEXELS_KEY (optional, free)

Key Where to get it
PIXABAY_KEYLog in at https://pixabay.com, open https://pixabay.com/api/docs/ — your key is shown under "Parameters → key".
UNSPLASH_KEYhttps://unsplash.com/oauth/applications → New Application → copy the Access Key.
PEXELS_KEYhttps://www.pexels.com/api/ → Your API Key → copy it.

Enter them in Admin → Settings → Stock Media. Any provider without a key is simply not searchable.

11. Credit prices and limits

Nothing to obtain — just decide your prices. Admin → Settings → Credit Prices sets what every action costs (image, avatar, talking video, showcase video, AI Ads, voice clone, voice-over, Ad Localizer, AI text); the app shows users the current price. Admin → Settings → Limits & Security sets the login session length, rate limits, maximum upload size and when held credits are returned. See the full list with defaults.

12. Complete example files

backend/.env — on your computer (written by npm run setup)

mongoDBURL=mongodb://127.0.0.1:27017/showcaseai
JWT_SECRET=<generated by npm run setup>
SETTINGS_ENCRYPTION_KEY=<generated by npm run setup>
PORT=5000
NODE_ENV=development

backend/.env — on a live server

mongoDBURL=mongodb+srv://myuser:MyPassword123@cluster0.abcde.mongodb.net/showcaseai?retryWrites=true&w=majority
JWT_SECRET=<generated by npm run setup>
SETTINGS_ENCRYPTION_KEY=<generated by npm run setup>
PORT=5000
NODE_ENV=production

That's all — the site URLs, keys, email, payments, prices and limits are entered in Admin → Settings.

frontend/.env (rebuild the frontend after changing it)

VITE_API_URL=https://api.example.com/api/
VITE_APP_NAME=ShowcaseAI
VITE_TERMS_URL=https://example.com/terms
VITE_PRIVACY_URL=https://example.com/privacy
✅

Finished? Run npm run check in backend — it shows a ✔ next to every configured setting and warns about anything missing (it never prints the values).

Admin → Settings #

One screen in the admin panel for every external key and site option. No file editing and no restart needed.

How it works

  • Values are saved in MongoDB (collection sitedatas, key app_credentials). Secret values are encrypted with AES-256-GCM using SETTINGS_ENCRYPTION_KEY (or JWT_SECRET if that is not set).
  • Precedence: value saved in Admin → Settings → otherwise the variable with the same name in backend/.env → otherwise empty.
  • Changes apply immediately.
  • Secrets are never sent back to the browser; they are shown as ••••••••. Leaving a secret field empty keeps the stored value.
  • Keep SETTINGS_ENCRYPTION_KEY stable. If it changes, the saved secrets can no longer be decrypted and must be re-entered.
🔐

Secrets – set once, never change. Save provider keys only after npm run setup has run. If SETTINGS_ENCRYPTION_KEY (in backend/.env) ever changes, all keys saved here become unreadable and show as not configured. Restore the original value and restart to get them back; otherwise you would have to re-enter every key. Details.

Groups and fields

Field Default What it does
Site & Branding — Public addresses of your installation and how your brand appears in emails.
Frontend URL
FRONTEND_URL
— Where users open the app, e.g. https://app.example.com (used in email links and payment return pages).
Backend URL
BACKEND_URL
— Public https URL of this API without /api, e.g. https://api.example.com. AI providers send finished results here.
App Name
APP_NAME
ShowcaseAI Shown in email subjects and as the email sender name.
Support URL
SUPPORT_URL
— Help/support link shown to users, e.g. https://support.example.com
Email Logo URL
EMAIL_LOGO_URL
— Optional. Image shown at the top of emails. Default: <Backend URL>/logo.png
AI Providers — Keys for the AI services that power generation.
Kie API Key
KIE_API_KEY secret, has Test button
— Powers image, video, and all text/translation.
Speechify API Key
SPEECHIFY_API_KEY secret, has Test button
— Powers the voice library, text-to-speech and voice cloning. Test also imports the voice library.
NewPortAI Key
NewPortAI_KEY secret, has Test button
— Powers lip-sync for talking videos and the Ad Localizer.
Storage (AWS S3) — Where uploaded and generated media is stored.
AWS Region
AWS_REGION
— e.g. us-east-1
AWS Access Key ID
AWS_ACCESS_KEY_ID secret
—
AWS Secret Access Key
AWS_SECRET_ACCESS_KEY secret, has Test button
—
S3 Bucket Name
AWS_S3_BUCKET
—
S3 Public / CDN URL
AWS_S3_PUBLIC_URL
— Optional. Defaults to the bucket's S3 URL.
S3 Folder
AWS_S3_FOLDER
showcaseai-cc Top-level folder inside the bucket where every file is stored (uploads, generated media and the sample assets under assets/). Change it if the bucket is shared with another project. Files already uploaded stay where they are.
S3 Object ACL
AWS_S3_ACL
— Optional. Set to public-read only if your bucket uses ACLs; leave empty when a bucket policy makes objects public.
Email / Notifications — How password-reset and welcome emails are sent.
Send Mail Via
SEND_MAIL_FROM
Mandrill / SMTP Choose which provider sends emails. SMTP is used as an automatic fallback if the primary send fails.
From Email
FROM_EMAIL
— The address emails are sent from, e.g. no-reply@example.com
Support Email
SUPPORT_EMAIL
— Shown to users in emails.
SMTP Host
SMTP_HOST
— e.g. smtp.gmail.com
SMTP Port
SMTP_PORT
— 587 (STARTTLS) or 465 (SSL)
SMTP Username
SMTP_USERNAME
—
SMTP Password
SMTP_PASSWORD secret, has Test button
—
Mandrill API Key
MANDRILL_API_KEY secret
— Only needed when sending via Mandrill.
Payments (Stripe) — Stripe keys for selling credit packs (Admin → Credit Packs).
Stripe Secret Key
STRIPE_SECRET_KEY secret, has Test button
— Test key starts with sk_test_.
Stripe Webhook Secret
STRIPE_WEBHOOK_SECRET secret
— Signing secret (whsec_…) of the webhook <Backend URL>/api/payment/stripe/webhook
Credit Prices — How many credits each action costs. Leave a field empty to use the default shown. 0 = free. Creator Studio prices are set per model.
Image generation
CREDITS_IMAGE
0.15 Image Studio, without a reference image.
Image generation with a reference image
CREDITS_IMAGE_WITH_REFERENCE
0.25 Image Studio with a reference, product shoot images, avatar + product images.
Create avatar from an image
CREDITS_AVATAR_FROM_IMAGE
5 Turns an uploaded photo into a moving avatar.
Create avatar from a video
CREDITS_AVATAR_FROM_VIDEO
2 Uses an uploaded video as an avatar.
Talking avatar video (lip-sync)
CREDITS_TALKING_AVATAR
2 Per talking-avatar video.
Product / fashion avatar video
CREDITS_PRODUCT_AVATAR
5 Avatar presenting or wearing a product.
Product / fashion showcase video (10 s)
CREDITS_SHOWCASE_VIDEO_10S
3 Product shoot and fashion show videos, 10 seconds.
Product / fashion showcase video (5 s)
CREDITS_SHOWCASE_VIDEO_5S
1.5 Product shoot and fashion show videos, 5 seconds.
AI Ads video
CREDITS_AI_ADS
3 Default AI Ads model.
AI Ads video (Veo)
CREDITS_AI_ADS_VEO
5 AI Ads with the Veo model.
Voice clone
CREDITS_VOICE_CLONE
5 Per cloned voice.
Voice-over (per 500 characters)
CREDITS_TTS_PER_500_CHARS
0.1 Saving a text-to-speech voice-over.
Voice preview
TTS_PREVIEW_CREDITS
0 Per text-to-speech preview (previews are rate-limited).
Ad Localizer (per language)
CREDITS_AD_LOCALIZER_PER_LANGUAGE
2 Per translated video, plus the AI text price for the translation.
AI text (per request)
LLM_TEXT_CREDITS
0.1 Ad scripts, ad copy, sales copy, prompt ideas and translations.
Limits & Security — Session length, rate limits and file size limits. Changes apply immediately.
Login session length (days)
SESSION_DAYS
7 Validity of a login token, 1–90 days. The browser keeps the sign-in for at most 7 days, so values above 7 do not extend it. Applies to new logins.
Failed logins allowed per 15 minutes
LOGIN_RATE_LIMIT
10 Per IP address. After that, logins are blocked for 15 minutes.
API requests per 5 minutes
API_RATE_LIMIT
1000 Per IP address, across the whole API.
Max upload size (MB)
MAX_UPLOAD_MB
100 Largest file a user can upload. Also raise client_max_body_size in nginx.
Max downloaded result size (MB)
MAX_REMOTE_FILE_MB
200 Largest generated file the API will download from a provider.
Return held credits after (minutes)
RESERVATION_TTL_MIN
120 If a provider never answers, reserved credits are returned after this time.
Stock Media — Optional keys for the stock image/video search inside the media picker.
Pixabay API Key
PIXABAY_KEY secret
— Free key from pixabay.com/api/docs
Unsplash Access Key
UNSPLASH_KEY secret
— Free key from unsplash.com/oauth/applications
Pexels API Key
PEXELS_KEY secret
— Free key from pexels.com/api

URL fields accept a trailing slash or not. Number fields are validated (an invalid value is rejected with a message); an empty field uses the default shown. Reset next to a field removes the saved value; Remove deletes a saved secret.

Where to get each key

Step-by-step instructions for every key — MongoDB, Kie.ai, AWS S3 (bucket policy, CORS and IAM policy), Speechify, NewPortAI, SMTP/Mandrill, Stripe (including the webhook) and the stock media providers — are in Keys & Credentials — Step by Step.

💳

Without the webhook secret, Stripe webhook calls are refused. The payment-success page still verifies the payment directly with Stripe, so a buyer who returns to your site still receives credits — but configure the webhook so credits are also granted when the buyer closes the browser before returning.

Email behaviour

If Mandrill fails and SMTP is configured, SMTP is used as a fallback. If nothing is configured, emails are skipped and a warning ("Email not sent") is logged — the API keeps running.

Production Deployment #

A typical setup is one Linux server running the API under PM2 behind nginx, with the built frontend served as static files and HTTPS from Let's Encrypt.

1 · Build the frontend

$ cd frontend
# set VITE_API_URL=https://api.example.com/api/ in .env first (copy .env.example to .env if it is missing)
$ npm ci
$ npm run build      # output in dist/

Upload the contents of dist/ to your web root. The web server must send every unknown path to index.html (single-page-app fallback), otherwise refreshing a page such as /dashboard returns 404.

2 · Install and start the API

$ cd backend
$ cp .env.example .env                # then set NODE_ENV=production in .env
$ npm ci --omit=dev
$ npm run setup -- --yes --no-demo --mongo "<your MongoDB URL>" \
    --admin-email you@example.com --admin-password "<a strong password>" \
    --frontend-url https://app.example.com --backend-url https://api.example.com
  # ^ first install only. Skip it if the database is already set up.
$ npm install -g pm2
$ pm2 start deploy/ecosystem.config.cjs
$ pm2 save
$ pm2 startup                         # follow the printed command so PM2 starts on boot

The package contains no .env file. On a live server, copy backend/.env.example to backend/.env and set NODE_ENV=production in it before you run npm run setup. Setup keeps the existing file and fills in mongoDBURL and the secrets; with NODE_ENV=production it never creates the demo user. Add --mongo, --backend-url, --frontend-url and --admin-email / --admin-password as needed (see the options); without a password, setup prints a generated admin password once — save it.

The ecosystem file sets the working directory to backend and NODE_ENV=production. Useful commands: pm2 logs, pm2 restart all, pm2 status.

⚙️

Run exactly one instance. Rate limits and provider polling are kept in memory, so do not use PM2 cluster mode or several instances.

3 · Configure nginx

Start from deploy/nginx.conf.example and replace the domain names. It contains:

  • An app domain (e.g. app.example.com) serving dist/ with try_files $uri $uri/index.html /index.html; for the SPA fallback.
  • An API domain (e.g. api.example.com), or an /api path, proxying to http://127.0.0.1:5000 with client_max_body_size 100m, 300-second proxy timeouts and the X-Forwarded-For / X-Forwarded-Proto headers.

Then enable HTTPS with Let's Encrypt, for example:

$ sudo certbot --nginx -d app.example.com -d api.example.com

Single-server option

Instead of a separate web root, set SERVE_FRONTEND_DIST=../frontend/dist in backend/.env. The API then serves the frontend too, on the same domain. Build the frontend with VITE_API_URL=https://yourdomain.com/api/ and point nginx at the API only.

When SERVE_FRONTEND_DIST is set and dist/index.html exists, the API serves, in this order: /api/* (the API), the built static files, and finally index.html for every other non-API path (so React Router URLs survive a refresh). If dist/index.html is missing the API logs a warning and serves no frontend — run npm run build in frontend/ first.

4 · Final configuration

  • Set FRONTEND_URL and BACKEND_URL to your real https domains.
  • Configure the Stripe webhook (see Admin → Settings).
  • Use your own admin login: change the password in Profile and the email in MongoDB Compass (see Changing the admin login).
  • Rotate a secret only if it has been exposed, and only JWT_SECRET (generate a new value as shown in Secrets, paste it into .env and restart — everyone is logged out). Never change SETTINGS_ENCRYPTION_KEY on a live site (see Secrets – set once, never change); rotate provider keys in their own dashboards instead.
  • Back up MongoDB regularly, and keep a copy of backend/.env somewhere safe.

Go-live checklist

Check How
NODE_ENV=production In backend/.env (PM2 ecosystem also sets it).
Strong, unique JWT_SECRET and SETTINGS_ENCRYPTION_KEY Generated by npm run setup; never reuse values from another site. Keep a backup of backend/.env and never change SETTINGS_ENCRYPTION_KEY afterwards.
HTTPS on every domain certbot / your host's certificate.
Public https BACKEND_URL Admin → Settings → Site & Branding; npm run check warns about localhost.
CORS restricted FRONTEND_URL set to the exact frontend origin.
All provider keys pass their Test button Admin → Settings.
S3 bucket publicly readable, IAM user least-privilege Bucket policy + IAM policy above.
Stripe live key + webhook sk_live_…, live webhook endpoint and its whsec_….
Email configured Send yourself a password reset.
Model prices reviewed Adjust pricingMatrix / costPerSecond in aimodelcatalogs and the credit-pack prices.
Admin login is your own Password in Profile; email in MongoDB Compass (how).
MongoDB secured Authentication enabled, network access restricted, backups scheduled.
Installation check passes npm run check.

Branding & Customisation #

Rename and rebrand the product without touching application code.

What Where
Product name in the browser, titles and footer VITE_APP_NAME in frontend/.env (rebuild afterwards).
Terms / privacy links, "Powered by" credit, sample images VITE_TERMS_URL, VITE_PRIVACY_URL, VITE_POWERED_BY_TEXT, VITE_POWERED_BY_URL, VITE_SAMPLE_MEDIA_BASE — see Configuration.
Privacy Policy and Terms of Service pages frontend/src/pages/legal/PrivacyPolicy.jsx and TermsConditions.jsx (rebuild afterwards) — see the note below.
Name in email subjects and sender name Admin → Settings → Site & Branding → APP_NAME.
Email content backend/src/emailTemplates/ (resetPassword.html, userCreated.html).
Logo in emails Replace backend/public/logo.png (served at <BACKEND_URL>/logo.png).
Favicon and app logo frontend/public/assets/images/ (rebuild afterwards).
⚖️

Replace the legal text before going live. The built-in Privacy Policy (/privacy-policy) and Terms of Service (/terms-conditions) pages are generic templates. They contain the contact address support@showcaseai.app and a "Last Updated" date. Replace them with your own company details, contact address and legal text (edit the two files in frontend/src/pages/legal/ and rebuild), or set VITE_TERMS_URL and VITE_PRIVACY_URL to your own pages.

Email template variables

The templates can use these placeholders, which are replaced when an email is sent:

Variable Value
{{app_name}} The APP_NAME setting.
{{logo_url}} URL of the email logo.
{{name}} Recipient's name.
{{email}} Recipient's email address (welcome email).
{{reset_url}} Password-reset link (reset email).
{{set_password_url}} "Set your password" link (welcome email, valid 48 hours).
{{login_url}} Login page link.
{{support_email}} The SUPPORT_EMAIL setting.
{{frontend_url}} The FRONTEND_URL setting.

Authentication & Users #

JWT authentication with email and password — there is no social/OAuth login. Visitors can create their own account on the sign-up page (/register), and admins can create accounts in the admin panel. The first admin is created by the setup script (see Admin Account & Starter Data).

Creating users

  • Self sign-up: /register (name, email, password). New accounts get the user role and start with no credits until an admin adds some or they buy a credit pack. Sign-ups are limited to 5 per hour per IP address.
  • An admin opens Admin → Users → Add User and enters a name, email and password, plus an optional starting credit balance. The server always creates the account as a regular user (user role, active status).
  • Deleted accounts: sign-up with the email of a deleted account is refused ("This email can't be used to register. Please contact the site administrator."). Only an admin can restore it, in Admin → Users → Add User with the same email; the account comes back with its previous data and credits ("Deleted account restored with its previous data").
  • With the Send Email checkbox ticked, the user receives a welcome email containing a "set your password" link valid for 48 hours (the email never contains a plaintext password). Email must be configured for this.

Login

  1. User submits email + password to POST /auth/login.
  2. Backend finds the user, verifies the password with bcrypt, and rejects deleted and inactive accounts. Failed attempts are rate-limited (LOGIN_RATE_LIMIT, default 10 per 15 minutes per IP).
  3. A JWT (HS256) is signed with JWT_SECRET, valid for the Login session length setting (SESSION_DAYS, 1–90 days, default 7), and returned with the user object (password removed). The browser keeps the auth_token cookie for at most 7 days, so a user stays signed in for the shorter of the two.
  4. Frontend stores the token in the auth_token cookie (SameSite=Lax, Secure on https), then redirects: admin → /admin/dashboard, others → /dashboard.

Token handling

  • Every API call sends Authorization: Bearer <token> (from the cookie).
  • MiddlewareModule.checkAuth verifies the signature (algorithm pinned to HS256), loads the user, checks the user is still active and not deleted, and checks the token version. It sets req.user plus request context (source, storeId, domain from headers). Failures return HTTP 401 {"status":"error","errorCode":"InvalidToken"}.
  • MiddlewareModule.checkAdmin runs after it on every /admin/* route and returns HTTP 403 {"errorCode":"Forbidden"} for non-admins.
  • Session revocation: each user has a token version. Logout (POST /auth/logout), a password change (by the user or by an admin), and an admin deactivating or deleting a user all invalidate every existing token of that user immediately.
  • On app load, ProtectedLayout calls GET /auth/get-auth-user to re-hydrate the session. Any 401 response clears the cookie and redirects to /login.

Password reset & password changes

  • POST /auth/forgot-password emails a reset link with a random single-use token. Only its SHA-256 hash is stored; it expires after 1 hour. The response is identical whether or not the email exists (no account enumeration).
  • POST /auth/reset-password takes { token, password }, sets the new hashed password and consumes the token.
  • Changing the password on the profile page (/update-profile) requires the current password. Minimum password length is 8 characters.
  • Forgot-password and reset-password requests share a limit of 5 per hour per IP. Sign-up has its own limit of 20 per hour per IP.

Roles & special flags

  • role — user or admin. Enforced on the server for every /admin/* route; the frontend also redirects non-admins away from /admin/* pages. There is exactly one admin account (created by setup); roles cannot be changed afterwards.
  • isSpecialUser — a "VIP" flag that grants effectively unlimited credits (returns a 10,000 balance and never deducts).
  • status (active/inactive), isDeleted (soft delete), userSource (where the account originated: web for sign-ups, admin for accounts added in Admin → Users, setup or seed for the accounts created by npm run setup). A soft-deleted email cannot register again; only an admin can restore it from Admin → Users → Add User.
  • An admin cannot deactivate or delete their own account, and the last admin cannot be removed.

Frontend access control

checkAccess() and applyAccessControl() in utils/Helper.js are the "pro feature" gate — currently set to unlock everything. They mark all menu items as available and remove "PRO/upgrade" badges.

The Credit System #

A ledger-based wallet. Every paid action costs credits; the balance is topped up by admin grants or Stripe purchases. All prices are calculated on the server — never taken from the browser.

The building blocks

Model / helper Role
Credit (collection credits) A batch of credits added (creditsAssigned, creditsUsed, type: creditPurchased for Stripe purchases, assignbyadmin for admin grants and demo data).
CreditReservation A temporary hold while a paid provider job runs, so credits cannot be double-spent.
CreditUsed (collection credituseds) A log of every deduction — the usage history.
CreditLock A short per-user lock so that balance checks and deductions for one user happen one at a time.
CreditHelper The functions all features call: reserve, attachTask, settle, release, chargeNow, getCreditDetails, plus the server-side price functions priceForStudio and priceForTts.

Available balance = active credits (assigned) − used − currently reserved.

Admins assign credits when creating or editing a user (Admin → Users). Users see their balance in the sidebar and their history on the billing page.

How credits are spent: reserve → settle on success

Users are only charged for generations that succeed.

  1. Reserve — before a paid provider is called, CreditHelper.reserve checks the balance and creates a CreditReservation (status RESERVED) inside the per-user lock. This lowers the available balance immediately, so parallel requests cannot overspend. If the provider rejects the job, the reservation is released at once.
  2. Settle — when the provider reports success (Kie webhook, or NewPortAI polling), CreditHelper.settle consumes the reservation and records the charge. Duplicate callbacks find no reservation, so a job can never be charged twice.
  3. Release — on failure the reservation is deleted with no charge.
  4. Timeout — a background sweeper releases reservations whose provider never answered within RESERVATION_TTL_MIN (default 120 minutes) and marks the related media as failed ("Generation timed out. Your credits have been returned.").

Instant actions (AI text, saved text-to-speech, direct video-to-avatar upload) use CreditHelper.chargeNow, which checks and deducts in one locked step.

Where credits actually leave the account

Flow Reserved in Charged in
Kie image / video / avatar / AI-ads / Creator Studio kieLib (before the Kie request) kieController on /api/kie/webhook (success)
NewPortAI lip-sync (talking avatar, Ad Localizer) render.js newportai poll (status completed)
Voice clone ttsController.addUserVoice Same request, after Speechify accepts the clone
Video-to-avatar (direct upload) — Immediate in videoController.addUserAvatar
Text-to-speech (saved) — Immediate in ttsController.synthesizeText
AI text tools — (balance checked first) After a successful response (LLM_TEXT_CREDITS)
Credit purchase (grant, not spend) — paymentController creates a Credit row once Stripe confirms the payment

Costs

All prices are set in Admin → Settings → Credit Prices (empty = default, 0 = free) and the app shows the current price to users. Creator Studio prices come from each model in the aimodelcatalogs collection.

Action Setting Default credits
Image generation
Image Studio, without a reference image.
CREDITS_IMAGE 0.15
Image generation with a reference image
Image Studio with a reference, product shoot images, avatar + product images.
CREDITS_IMAGE_WITH_REFERENCE 0.25
Create avatar from an image
Turns an uploaded photo into a moving avatar.
CREDITS_AVATAR_FROM_IMAGE 5
Create avatar from a video
Uses an uploaded video as an avatar.
CREDITS_AVATAR_FROM_VIDEO 2
Talking avatar video (lip-sync)
Per talking-avatar video.
CREDITS_TALKING_AVATAR 2
Product / fashion avatar video
Avatar presenting or wearing a product.
CREDITS_PRODUCT_AVATAR 5
Product / fashion showcase video (10 s)
Product shoot and fashion show videos, 10 seconds.
CREDITS_SHOWCASE_VIDEO_10S 3
Product / fashion showcase video (5 s)
Product shoot and fashion show videos, 5 seconds.
CREDITS_SHOWCASE_VIDEO_5S 1.5
AI Ads video
Default AI Ads model.
CREDITS_AI_ADS 3
AI Ads video (Veo)
AI Ads with the Veo model.
CREDITS_AI_ADS_VEO 5
Voice clone
Per cloned voice.
CREDITS_VOICE_CLONE 5
Voice-over (per 500 characters)
Saving a text-to-speech voice-over.
CREDITS_TTS_PER_500_CHARS 0.1
Voice preview
Per text-to-speech preview (previews are rate-limited).
TTS_PREVIEW_CREDITS 0
Ad Localizer (per language)
Per translated video, plus the AI text price for the translation.
CREDITS_AD_LOCALIZER_PER_LANGUAGE 2
AI text (per request)
Ad scripts, ad copy, sales copy, prompt ideas and translations.
LLM_TEXT_CREDITS 0.1
Creator Studio model catalog pricingMatrix per resolution/duration, or costPerSecond × duration
🔌

Operational note: Kie-based charges are finalised by the /api/kie/webhook callback, which must reach your server at BACKEND_URL (public https). If it cannot, jobs stay "Processing" until the sweeper returns the credits after RESERVATION_TTL_MIN. NewPortAI lip-sync completes via in-process polling and works without a public webhook.

AI Providers & Storage #

All AI work is delegated to three external services; media is stored on Amazon S3. Keys for all of them are entered in Admin → Settings.

Service Powers Code
Kie Image generation, video generation, and all LLM text (ad scripts, copy, translation) via its OpenAI-compatible Gemini endpoint lib/kieLib.js, helpers/LlmHelper.js
Speechify Text-to-speech (multilingual) and voice cloning lib/tts.js
NewPortAI Lip-sync — syncs an avatar's mouth to an audio track lib/newportai.js
Amazon S3 Stores all generated & uploaded media (optionally via CloudFront CDN). Files must be publicly readable because the providers download them. helpers/UploadHelper.js
Pixabay / Unsplash / Pexels Optional stock media search userMediaController (/get-thirdParty-media)
MongoDB All application data Mongoose models
🌍

The Ad Localizer is the clearest example of these working together: Kie translates the script → Speechify re-voices it in the target language → NewPortAI re-syncs the avatar's lips — reusing infrastructure the app already had.

Async Generation Flow #

Videos and images don't finish instantly. Here's the full lifecycle of a slow job.

Kie image / video

  1. The controller calculates the price on the server and calls kieLib.generateImage/generateVideo.
  2. kieLib first reserves the credits (CreditHelper.reserve). If the balance is too low, nothing is sent to Kie.
  3. The job is sent to Kie with the callback URL <BACKEND_URL>/api/kie/webhook?token=…. If Kie rejects it, the reservation is released; if Kie accepts it, the Kie task id is attached to the reservation, a PredictionReplicate tracking row is created, and the task id is stored on the media record's renderData.renderServerId. Status = processing.
  4. Kie calls back to POST /api/kie/webhook (kieController.ipn). Calls without a valid token are rejected with 401, and each task is claimed exactly once (duplicate or late callbacks are ignored):
    • Success → CreditHelper.settle charges the reserved credits, the result is re-uploaded to S3 and the record is marked completed.
    • Failure → the record is marked failed and the reservation is released (no charge).
  5. For videos, completion is finalised through commonComplete.js (generalVideoComplete / avatarComplete). An avatar clip additionally feeds a follow-up NewPortAI lip-sync job.
  6. If no callback ever arrives, the reservation sweeper returns the credits after RESERVATION_TTL_MIN and marks the record failed.

NewPortAI lip-sync

  1. render.js reserves the talking-avatar price (CREDITS_TALKING_AVATAR, default 2) and starts a lip-sync task; the Ad Localizer does the same with its own per-language price.
  2. newportai.js polls the task every 10 seconds (up to 15 minutes). On completion it uploads the video, marks the UserVideos record completed, and settles the reserved credits.
🖥️

On the frontend, creation pages poll the video/image list (e.g. useAvatarVideoPolling) so a card visibly moves from Processing → Completed/Failed in Projects.

Feature Guide — Creative Studios #

Image Studio

Core/imagestudio
What it does
Text-to-image generation, optionally guided by a reference image and a Brand Kit.
Endpoints
POST /generate-images, /get-generated-images-list, /add-manual-image
Provider / model
Kie (z-image, or nano-banana when a reference image is supplied)
Models
ImageGeneration
Cost
0.15 credits (0.25 with reference image)

How the user uses it

  1. Open Image Studio from the sidebar.
  2. (Optional) Upload a reference photo if you want the image based on it.
  3. Type a description of the image you want in the prompt box.
  4. (Optional) Choose a Brand Kit to apply your brand colours & style.
  5. Click Generate — the image appears in the gallery on the right within seconds.
  6. Hover any image to download, edit/regenerate, or delete it.

Video Studio

Core/videostudio · /create-*

Four video types, each with its own builder:

Type Route Endpoint Cost
Product Showcase /create-product-showcase-videos generate-product-shoot-images → generate-video-prompt → generate-video 3
Product Avatar /create-product-avatar-videos generate-avatar 5
Talking Avatar /create-avatar-videos synthesize-text → generate-user-avatar-video 2
Fashion / Try-on /create-tryon-videos combine-avatar-product 3
Providers
Kie (video), Speechify (voice), NewPortAI (lip-sync for talking avatars)
Models
UserVideos, UserAvatar, VideoAvatar, ProductAvatar

How the user uses it

  1. Open Video Studio and choose the video type (Product Showcase, Product Avatar, Talking Avatar, or Fashion).
  2. Select an avatar/presenter — or a product image, depending on the type.
  3. Write the script, or click AI Writer to generate one (optionally applying a Brand Kit).
  4. Choose a voice and preview the audio.
  5. Click Generate. The video renders in the background (a few minutes).
  6. Open Projects to watch it move from Processing → Completed, then download it.

AI Ads Video

Core/create-ai-ads
What it does
Generates short ad videos using cutting-edge models (SORA-style, Veo, Grok).
Endpoints
generate-video-prompt → generate-ai-ads-video
Cost
3 credits (5 for Veo)

How the user uses it

  1. On the Dashboard, click the AI Ads Videos card.
  2. Upload a product image or type your ad idea.
  3. Pick a model tab (SORA / Veo / Grok).
  4. Generate the prompt, then click generate to create the video.
  5. Find the finished ad in Projects.

Text to Speech

Core/voiceover-studio
What it does
Turns text into a natural voiceover; supports SSML-style emotion/pause tags; can save the audio to your media library.
Endpoints
synthesize-text, get-premium-voices-list, get-user-uploads
Provider
Speechify (simba-multilingual)
Cost
Charged when saved: 0.10 credit per started 500 characters, calculated on the server from the script. Previews cost TTS_PREVIEW_CREDITS (default 0) and are limited to 20 per minute.

How the user uses it

  1. Open Text to Speech.
  2. Pick a voice (filter by language, gender, or style).
  3. Type your text; optionally add emotion/pause tags for expression.
  4. Click Generate Audio to preview it.
  5. Save it to keep it in your media library (this uses credits).

Voice Clone

Core/voicestudio
What it does
Creates a custom voice from an uploaded sample, then selectable anywhere voices are picked.
Endpoints
file-to-upload → add-user-voice; list via get-user-voices-list
Provider
Speechify
Cost
5 credits (reserved first, charged once Speechify accepts the clone). A cloned voice can only be used by the user who created it.

How the user uses it

  1. Open Voice Clone.
  2. Upload or record a clear sample of the voice.
  3. Give it a name and click create (5 credits).
  4. Your new voice now appears under My Voices wherever a voice is selected.

Avatar Studio

Core/avatarstudio
What it does
Your personal library of avatars — the AI "presenters" used in talking videos. Browse the avatars you've created, and generate new ones (from an uploaded photo, which is turned into a moving avatar, or from a video). Created avatars become selectable in the Video Studio flows.
How to use
Open Avatar Studio → view "My Avatars" → use the generate/create option to add a new one → it then appears as a choice when building Talking / Product / Fashion avatar videos.
Endpoints
POST /get-user-avatar-list (your avatars), /add-user-avatar (create from image or video), /generate-avatar (product/fashion avatar). Template lists via /get-video-avatar-list and /get-product-avatar-list.
Provider
Kie (generates the avatar clip), then NewPortAI (lip-sync when the avatar speaks)
Models
UserAvatar (your avatars), VideoAvatar / ProductAvatar (the ready-made catalog)
Cost
Creating an avatar from an image: 5 credits · from a video: 2 credits (charged on success)

How the user uses it

  1. Open Avatar Studio to see My Avatars.
  2. To make a new one, upload a photo (or video) and generate.
  3. Wait for it to finish processing.
  4. It then appears as a selectable presenter when you build Talking / Product / Fashion avatar videos.

Creator Studio

Core/creator-studio/:type
What it does
A flexible, "power user" generator that isn't tied to one fixed flow. It reads the catalog of available AI models and builds its input form dynamically based on the model you pick — so the same page can drive different image or video models with their own settings (e.g. duration, aspect ratio).
How to use
Open Creator Studio (Video Models or Image Models) → pick a model → the form adapts to that model's options → generate. Output lands in Projects.
Endpoints
POST /get-model-list (available models, by type) → /generate-studio-media (dispatches to studio image or video)
Provider
Kie
Models
AIModelCatalog (model definitions + pricing), ImageGeneration / UserVideos (output)
Cost
Varies by model and settings. The server calculates it from the model's pricingMatrix (per resolution and duration) or costPerSecond — the price shown in the browser is never trusted. A model without a price cannot be used ("This model has no price configured").

How the user uses it

  1. Open Creator Studio and choose Video Models or Image Models.
  2. Pick a model — the form updates to show that model's options.
  3. Set the options (e.g. aspect ratio, duration) and your prompt.
  4. Click Generate; the result lands in Projects.

Feature Guide — AI Ad Tools #

Brand Kits

Core/tool/brand-kits
What it does
Save a brand identity (logo, colours, tagline, voice, keywords) and apply it so generations stay on-brand. Text tools get a full brand context; image generation gets a concise colour/style directive.
Endpoints
brand-kits/list · /create · /update · /set-default · /delete
Models
BrandKit
Cost
Free (rides along with the generation it enhances)
Applied in
Image Studio, AI Writer (ad scripts), Sales Copy, Ad Copy Studio

How the user uses it

  1. Open Brand Kits → Add Brand Kit.
  2. Fill in your logo, colours, tagline, brand voice, and keywords.
  3. Tick Set as default and click Create.
  4. In Image Studio, Ad Copy Studio, or the AI Writer, pick this kit to keep results on-brand.

Ad Copy Studio

Core/tool/ad-copy-studio
What it does
Generates multiple structured ad variants (headline + body + CTA) following a chosen framework (AIDA, PAS, FAB…), platform, tone, and language — with saved history.
Endpoints
ad-copy/generate · /history · /delete
Provider
Kie (LLM)
Models
AdCopy
Cost
LLM_TEXT_CREDITS per successful generation (default 0.1; set in Admin → Settings → Credits)

How the user uses it

  1. Open Ad Copy Studio and enter your product name.
  2. Choose a framework, platform, tone, language, and number of variants.
  3. (Optional) select a Brand Kit, then click Generate Ad Copy.
  4. Copy any variant with one click; open History to revisit past generations.

Sales Copy

Core/tool/sales-copy
What it does
The original single-block marketing-copy writer (Ad Copy Studio's predecessor). No sidebar link — reached by URL.
Endpoint
generate-llm-text (type=marketingCopy)
Cost
LLM_TEXT_CREDITS per successful generation (default 0.1)

How the user uses it

  1. Go to the Sales Copy page (via its URL /tool/sales-copy).
  2. Enter the product name and industry (plus any optional fields).
  3. (Optional) pick a Brand Kit, then click Generate.
  4. Copy the finished marketing copy.

Ad Localizer

CoreCredits/tool/ad-localizer
What it does
Recreates a completed talking-avatar video in other languages: translate the script → re-voice → re-lip-sync. One source video becomes many localized videos in Projects.
Endpoints
ad-localizer/languages · /sources · /localize
Providers
Kie (translate) + Speechify (voice) + NewPortAI (lip-sync)
Models
UserVideos (with language + localizedFrom)
Cost
2 credits per language plus the AI text cost (LLM_TEXT_CREDITS) for each translation — checked up front for the whole batch

How the user uses it

  1. First, create a Talking Avatar Video and let it finish.
  2. Open Ad Localizer and select that video as the source.
  3. Paste the original script (the words spoken in the video).
  4. Select the target languages — the credit cost updates live.
  5. Click Localize, then watch Projects for each localized video.

Feature Guide — Payments (Stripe) #

One-time credit packs, purchased through Stripe Checkout. Optional — without Stripe keys the rest of the app works normally. Setup steps: Admin → Settings → Payments.

The flow

  1. Admin defines packs on /admin/credit-packs (name, credits, price, currency).
  2. User opens /buy-credits, clicks Buy → backend creates a pending PaymentOrder + a Stripe Checkout session and redirects to Stripe.
  3. User pays (test card 4242 4242 4242 4242). Stripe returns them to /payment-success.
  4. Credits are granted by finalizeSession, triggered by both the Stripe webhook and the success-page verify call — made idempotent by the creditGranted flag so credits are never granted twice. Before granting, the paid amount, currency and session are checked against the stored order.

Architecture notes

  • The webhook POST /api/payment/stripe/webhook is mounted specially in app.js with a raw body parser (before JSON parsing) so Stripe's signature can be verified. A valid signature (STRIPE_WEBHOOK_SECRET) is required; unsigned calls are refused.
  • Webhook events used: checkout.session.completed and checkout.session.async_payment_succeeded.
  • The Stripe client is lazily initialised — the app boots fine before keys are added; Buy Credits shows "Payments are not configured yet" until STRIPE_SECRET_KEY is saved in Admin → Settings (no restart needed).
  • Users can only see their own payment orders.
  • Models: CreditPack (what's for sale), PaymentOrder (audit trail + idempotency).

Feature Guide — Community & Projects #

Projects

Core/projects

The user's library of generated images and videos. This is where async jobs are watched (Processing → Completed/Failed) and downloaded/deleted (get-user-videos-list, get-generated-images-list, delete-data).

How the user uses it

  1. Open Projects and switch between the Videos and Images tabs.
  2. Check each item's status: Processing, Completed, or Failed.
  3. On a completed item, download it or delete it.

Community

Core/community

A social feed: posts (up to 3 images), likes, comments, top contributors, and trending hashtags. Admins can create categories and pin posts.

Endpoints: community/get-posts · create-post · update-post · delete-post · toggle-like · get-comments · add-comment · delete-comment · get-top-contributors · get-trending-hashtags.

How the user uses it

  1. Open Community.
  2. Click to create a post — add a title, text, and up to 3 images.
  3. Like and comment on other posts; browse trending hashtags and top contributors.

Admin Panel #

Accessible to admin role accounts under /admin/*. Every admin API route is protected on the server: not logged in → 401, logged in but not an admin → 403.

Page Route Purpose
Dashboard /admin/dashboard Totals (users, active users, checkouts, paid orders) and a new-users chart for a chosen date range.
Users /admin/users Admin accounts are not listed here, only customer accounts. Add users (name, email, password, optional starting credit, optional welcome email with a "set your password" link), edit a user's name and password, add credits, set active/inactive and delete users. A user's email and role cannot be changed.
Prompts /admin/prompts Manage the preset-prompt library (prompt text, type and an example image). Starts empty — see Add your own content.
Product Avatar Templates /admin/product-avatar-templates Upload and manage the ready-made product/fashion avatars (requires S3).
Video Avatar Templates /admin/video-avatar-templates Upload and manage the ready-made talking avatars (requires S3).
User Analytics /admin/user-analytics Per-user creation counts.
Community Categories /admin/community-categories Create, edit, reorder and delete community categories (SVG icons are sanitised).
Community /admin/community Moderate the community feed: pin posts and remove posts.
Credit Packs /admin/credit-packs Create & price the packs users buy.
Settings /admin/settings Provider keys, storage, payments, email, site URLs and credit prices — with Test buttons. See Admin → Settings.

The admin tasks you'll use most

  1. Create a user — Admin sidebar → Users → Add User → name, email, password and an optional credit amount → Add User. Tick Send Email to send the welcome email with a link to set their own password.
  2. Give a user credits — Admin sidebar → Users → find the user → Edit → enter an amount under Assign Credit → Update. The amount is added to the user's balance.
  3. Set up something to sell — Admin sidebar → Credit Packs → Add Credit Pack → enter a name, credits, and price → Save. It then appears on the user's Buy Credits page.
  4. Change a provider key — Admin sidebar → Settings → edit the field → Save → Test.
🛡️

Deactivating or deleting a user, or setting a new password for them, logs that user out immediately. Roles cannot be changed: there is one admin account, created by npm run setup. A deleted user cannot sign up again with the same email; to bring the account back, add it again in Admin → Users → Add User — it is restored with its previous data and credits.

Data Models Reference #

26 Mongoose models. All use timestamps; most user-owned models share a platform sub-object and isDeleted/deletedAt soft-delete.

Users & Billing

Model Purpose Key fields
User Account name, email (unique), password (bcrypt), role (user/admin), status, isSpecialUser, userSource, isDeleted, tokenVersion, resetPasswordToken (SHA-256 hash), resetPasswordExpires
Credit Granted credit batch userId, creditsAssigned, creditsUsed, type (creditPurchased/assignbyadmin), status, expireAt, orderId
CreditUsed Deduction log userId, creditsUsed, type (16 feature types: imageGeneration, talkingAvatar, productAvatar, fashionAvatar, fashionShow, productShoot, aiAds, imageToAvatar, videoToAvatar, voiceClone, textToSpeech, studioVideo, studioImage, llmText, ttsPreview, adLocalizer)
CreditReservation In-flight hold userId, taskId, creditsUsed, deductType, type (kie/newportrai), status (RESERVED/COMPLETED/RELEASED)
CreditLock Per-user lock Short-lived lock that serialises balance checks and deductions for one user
CreditPack Purchasable pack name, credits, price, currency, isPopular, sortOrder, status
PaymentOrder Purchase attempt userId, packId, credits, amount, gateway, gatewaySessionId, status, creditGranted, paidAt
CreationCount Usage counters per-feature counts + last-track dates, overallCount

Videos, Avatars & Images

Model Purpose Key fields
UserVideos A video job/output userId, title, avatarType/avatarId, videoUseCaseType, portrait{image,video}, status, renderData, rawData, language, localizedFrom
UserAvatar User's custom avatar userId, avatarType, portrait, voice, status, renderData, usedCount
VideoAvatar Catalog talking avatar fullName, gender, voice, portrait/landscape/squared media, status
ProductAvatar Catalog product/fashion avatar fullName, avatarType (Product/Fashion), voice, media, status
ImageGeneration An image job/output userId, prompt, useCaseType, images[], status, renderData
PredictionReplicate Async task tracker taskId (unique), taskModel, taskType, taskStatus, taskOutput
UserMediaUploads Uploaded media userId, title, mediaType (video/image/audio/voiceover), audio/image/video, mettaData

Voice, Community & Content

Model Purpose Key fields
UserVoice Cloned voice userId, id (provider), display_name, locale, gender, provider, status
TtsSpeechify Voice catalog id, display_name, gender, locale, accent, use_case[], voice_style[], models[]
CommunityPost Feed post userId, title, description, category, hastags[], images[], stats{likes,comments,shares}, isPinned
CommunityPostComments Comment postId, userId, comment
CommunityPostLikes Like postId, userId (unique together)
CommunityCategories Category name, description, icon, ranking
PrePrompt Preset prompt title, prompt, type, image, status
SiteData Key/value config key, value (Mixed). Keys include ethnicities, profession and app_credentials (Admin → Settings, secrets encrypted)
AIModelCatalog Model catalog id, title, type (image/video), costPerSecond, pricingMatrix, settings
BrandKit Brand identity userId, name, logo, colors{}, tagline, industry, brandVoice, keywords[], isDefault
AdCopy Ad-copy generation userId, product/framework/tone/platform/language, brandKitId, variants[{headline,primaryText,cta}]

API Reference #

All paths are prefixed /api. GET/POST = method; Auth = requires a valid token; Admin = requires a logged-in admin.

Common responses

Situation HTTP status Body
Missing, invalid, expired or revoked token 401 {"status":"error","errorCode":"InvalidToken","message":"…"}
Logged in but not an admin (any /admin/* route) 403 {"status":"error","errorCode":"Forbidden","message":"…"}
Rate limit exceeded 429 Error message (e.g. "Too many login attempts. Please try again in 15 minutes.")
Unexpected server error 500 Generic message only — no stack traces or internal details.

System

Method Path Auth Purpose
GET /health Public Health check — returns {"status":"success","message":"ok"}
GET /config Public Non-secret public settings for the frontend: SUPPORT_URL, APP_NAME and the current credit prices object
POST /get-site-data Auth Public site lists only (ethnicities, profession); saved API keys can never be read through the API

Auth & Profile

Method Path Auth Purpose
POST /auth/login Public Log in, issue JWT (rate-limited)
POST /auth/register Public Sign up: body { name, email, password } creates a regular user account and returns a JWT (20 per hour per IP)
POST /auth/logout Auth Log out and revoke all of the user's tokens
POST /auth/forgot-password Public Email a reset link (same response for unknown emails; 5/hour)
POST /auth/reset-password Public Body { token, password } — token is the opaque 64-hex value from the email link; single use, valid 1 hour
GET /auth/get-auth-user Token Rehydrate session
POST /update-profile Auth Update name/picture; changing the password requires currentPassword

Billing & Payments

Method Path Auth Purpose
GET /get-billing-info Auth Credit balances + history
GET /payment/credit-packs Auth Active packs
POST /payment/create-checkout-session Auth Start Stripe checkout
POST /payment/verify-order Auth Confirm/finalize after return
POST /payment/history Auth Purchase history
POST /payment/stripe/webhook Public (signed) Grant credits on payment
POST /admin/payment/{get-packs,save-pack,delete-pack} Admin Pack management

Generation — Image, Video, Voice, LLM

Method Path Purpose
POST /generate-images Text-to-image
POST /generate-product-shoot-images Product shoot images
POST /combine-avatar-product Composite avatar + product
POST /generate-avatar Product/fashion talking avatar
POST /generate-video Fashion/product video
POST /generate-ai-ads-video AI ad video
POST /generate-user-avatar-video Talking-avatar lip-sync
POST /generate-studio-media Studio image/video
POST /synthesize-text Text-to-speech
POST /add-user-voice Clone a voice
POST /generate-ad-script LLM ad script
POST /generate-llm-text LLM marketing copy
POST /generate-video-prompt LLM video prompt from image

New Feature Endpoints

Method Path Purpose
GET/POST /brand-kits/{list,create,update,set-default,delete} Brand Kits CRUD
POST /ad-copy/{generate,history,delete} Ad Copy Studio
GET/POST /ad-localizer/{languages,sources,localize} Ad Localizer

Lists, Media, Community, Admin, Webhooks

Method Path Purpose
POST /get-generated-images-list · /get-user-videos-list List a user's media
POST /get-{product-avatar,video-avatar,user-avatar}-list Avatar lists
POST /get-premium-voices-list · GET /get-user-voices-list Voice lists
POST /file-to-upload · /get-user-uploads · /delete-user-uploads · /get-thirdParty-media Media & uploads
POST /get-model-list Active Creator Studio models, body { type: "image" | "video" } (Auth)
POST /add-user-avatar Create an avatar from an uploaded image or video URL, body { avatarUploadType, fileUrl, title, … }. An image starts a generation (CREDITS_AVATAR_FROM_IMAGE, charged on success); a video is charged at once (CREDITS_AVATAR_FROM_VIDEO) (Auth)
POST /add-manual-image Add an existing image URL ({ image, useCaseType }, http or https) to the user's images; no generation and no credits (Auth)
POST /delete-data Delete one of the caller's own items, body { type, id } where type is userVoice, userAvatar, userVideo, generatedImages or userImage; generatedImagesIndex with imageIndex removes one image from a generation (Auth)
GET /community/get-categories Community categories (public, no login needed)
POST /community/* Feed, likes, comments (Auth)
POST /admin/{get-users,add-user,update-user,delete-user,update-user-status} User management (Admin). add-user always creates a regular user (name, email, password, optional credit and sendMail). update-user takes userId plus name, password and/or credit (added to the balance); a different role is rejected.
POST /admin/get-*-analytics Analytics (Admin)
POST /admin/{get-avatar-templates,add-update-avatar,update-avatar-status,delete-avatar} Avatar template management (Admin)
POST /admin/add-pre-prompt · /admin/delete-pre-prompt · /get-pre-prompt-list Preset prompts (add and delete: Admin; list: Auth). Delete takes { id } and also removes the reference image.
POST /admin/community/{add-category,update-category,delete-category,reorder-categories,pin-post} Community administration (Admin)
POST /admin/settings/{get,save,test} Admin → Settings: masked values, save, connection tests (Admin)
POST /kie/webhook?token=… Kie callback — requires the secret token; each task processed once

Security & Hardening #

The protections built into ShowcaseAI, how to test them, and what you still need to do on your own server.

Access control

  • Every /admin/* API route requires a logged-in admin: 401 when not logged in, 403 when not an admin. This includes the avatar-template and settings routes.
  • /get-site-data requires a login and only returns the public lists (ethnicities, profession). Saved API keys can never be read through the API.
  • Ownership is checked on every user resource: users can only view or delete their own media, voices, avatars, ad copies, brand kits and payment orders. Cloned voices can only be used by their owner.
  • An admin cannot deactivate or delete themselves, and the last admin cannot be removed.

Sessions and passwords

  • JWT signed with HS256 (algorithm pinned on verification), valid for SESSION_DAYS (default 7 days); the browser keeps the sign-in for at most 7 days.
  • Per-user token version: logout, password change, and admin deactivation/deletion revoke all sessions of that user immediately. Inactive or deleted users cannot log in or use old tokens.
  • Password reset uses a single-use random token; only its SHA-256 hash is stored and it expires after 1 hour. Welcome emails contain a 48-hour "set your password" link instead of a plaintext password.
  • Minimum password length 8. Changing a password requires the current password. Forgot-password returns the same response for unknown emails (no account enumeration).

Request protection

  • Rate limiting: failed logins 10 per 15 minutes per IP (LOGIN_RATE_LIMIT); forgot-password and reset-password 5 per hour (one shared limit); sign-up 20 per hour; voice previews 20 per minute; a global API limit (API_RATE_LIMIT, 1000 requests per 5 minutes per IP).
  • Helmet security headers, a CORS allow-list (FRONTEND_URL), request body size limits, and generic error messages (no stack traces or internal errors are sent to clients).
  • NoSQL injection: MongoDB operators are stripped from all request bodies, ids are validated, and user search text is escaped before it is used in a regular expression.

Credits and payments

  • Prices are always calculated on the server (Creator Studio from the model catalog; text-to-speech from the script length), never taken from the browser.
  • Credits are reserved atomically (per-user lock) before a paid provider is called and released automatically on failure. Stale reservations are released after RESERVATION_TTL_MIN. Duplicate provider callbacks cannot charge twice.
  • Kie callbacks must carry a secret token (HMAC derived from WEBHOOK_SECRET, or from JWT_SECRET) and each task is processed only once.
  • The Stripe webhook requires a valid signature; the amount, currency and session are checked against the order before credits are granted.

Files and outgoing requests

  • Uploads: only image, video and audio files are accepted. The real file type is detected from the file content (magic bytes); HTML and SVG uploads are never stored. Storage keys are generated by the server inside the user's folder. Size limit MAX_UPLOAD_MB; temporary files are deleted.
  • SSRF protection: when the server downloads a URL, only public http(s) addresses are allowed. Private, loopback, link-local and cloud-metadata IP addresses are blocked, also after redirects and DNS resolution. Downloads are limited in size (MAX_REMOTE_FILE_MB) and time. In production only https URLs are allowed unless ALLOW_HTTP_REMOTE_URLS=true.
  • API keys saved in Admin → Settings are encrypted at rest (AES-256-GCM) and never sent back to the browser.

Frontend

  • The login token is kept only in the auth_token cookie (SameSite=Lax, Secure on https) — not in sessionStorage.
  • Automatic logout on any 401 response.
  • Notification text is rendered as plain text (no HTML injection). Community category SVG icons are sanitised with DOMPurify (and on the server).
  • External links open with noopener; a route error boundary shows a friendly error page instead of a blank screen.

Automated security checks

With the API running and setup finished, run from backend/:

$ SMOKE_ADMIN_PASSWORD="your-admin-password" npm run smoke                      # macOS / Linux, uses http://localhost:<PORT>
$ API_URL=http://localhost:5000 SMOKE_ADMIN_PASSWORD="…" npm run smoke     # explicit URL
> $env:SMOKE_ADMIN_PASSWORD="your-admin-password"; npm run smoke          # Windows PowerShell

npm run smoke runs scripts/security-smoke.js, which you can also start directly with node scripts/security-smoke.js. The script reads backend/.env, logs in as the admin (SMOKE_ADMIN_EMAIL, default admin@showcaseai.app, and SMOKE_ADMIN_PASSWORD — there is no default password, so you must set it to your admin password), creates a temporary test user and runs 33 checks — admin routes refused without login (401) and for normal users (403), saved keys never returned, NoSQL injection rejected, reset links single-use, tokens revoked after logout, users unable to delete other users' videos, client-supplied prices ignored, unsigned Kie/Stripe webhooks refused, HTML disguised as an image rejected, and login rate limiting. It prints a ✔/✖ line per check and a "passed / failed" summary. The temporary user and everything it created are deleted at the end; your real accounts are not touched.

⏳

The smoke test deliberately triggers the login rate limit. Afterwards, wait 15 minutes or restart the API before logging in from the same computer.

Your hardening checklist

  • NODE_ENV=production on the live server.
  • Strong, unique JWT_SECRET and SETTINGS_ENCRYPTION_KEY (generated by npm run setup).
  • HTTPS everywhere (frontend, API, webhooks).
  • Restrict CORS to your real frontend origin.
  • Use your own admin email and password (Changing the admin login).
  • MongoDB with authentication enabled and network access restricted (Atlas IP allow-list or firewall).
  • S3 IAM user with least privilege (see S3 setup).
  • Keep dependencies updated (npm audit) and back up MongoDB regularly.

Troubleshooting #

Start with npm run check inside backend/ — it finds most configuration problems in seconds. Then look up the symptom below. The API terminal (or pm2 logs) always shows the underlying error.

Symptom Cause Fix
Site won't load One app isn't running Make sure both the API and the frontend are running (two terminals, or PM2 + web server).
API does not start: "JWT_SECRET is not set" or "must be at least 32 characters" Missing or weak secret in backend/.env On a new install run npm run setup (generates it). If you prepare .env by hand, put a random value of at least 32 characters in it (see Secrets) and never change an existing one. On an installed site, restore the value you used before from your backup of .env; only if it is lost, put in a new value (everyone is logged out).
"Could not connect to MongoDB" Wrong mongoDBURL, MongoDB not running, or Atlas blocking your IP Check the connection string (including user, password and database name); start mongod for a local install; on Atlas add the server's IP under Network Access.
Login says "Too many login attempts" 10 failed logins from your IP in 15 minutes (also caused by the security smoke test) Wait 15 minutes, or restart the API (limits are kept in memory).
Sign-up or Forgot password says "Too many requests" More than 5 sign-up, forgot-password or reset-password requests from your IP in one hour Wait an hour, or restart the API (limits are kept in memory).
Everyone is logged out / 401 after a deploy JWT_SECRET changed Expected — log in again.
Saved keys "disappeared" or show as not configured after a restart or redeploy SETTINGS_ENCRYPTION_KEY (or JWT_SECRET, when no encryption key is set) is not the value the keys were saved with: .env was replaced, regenerated or restored from an old copy. The API log says "could not decrypt … was SETTINGS_ENCRYPTION_KEY / JWT_SECRET changed?" Admin → Settings shows a warning, and npm run check reports the keys that cannot be read. Put the original values back into backend/.env (from your backup) and restart; the keys come back. Only if the original is truly lost, re-enter every key in Admin → Settings.
Secrets – set once, never change: .env lost or secrets regenerated while the database has data A newly generated key cannot decrypt data encrypted with the old one; changing JWT_SECRET only logs users out. Restore backend/.env from your backup. Run npm run setup before using the app or saving any key, then back up .env. --rotate-secrets replaces only JWT_SECRET. See Secrets – set once, never change.
npm run setup says "already set up" The database already has an admin; setup runs once and never overwrites your data Nothing to do — log in. To start over use npm run setup -- --reset (deletes all data).
Locked out of the admin panel Forgotten password Use Forgot password, or set a new password with MongoDB Compass (steps).
Browser console shows a CORS error The frontend origin is not allowed Set FRONTEND_URL (Admin → Settings → Site & Branding) to the exact origin — scheme, host and port, e.g. https://app.example.com or http://localhost:4001. Restart the API.
Frontend calls the wrong API / old value VITE_* values are compiled in Fix frontend/.env and run npm run build again (or restart npm run dev).
"Insufficient credit" Empty balance Assign credits in Admin → Users, or buy a credit pack.
Generation stuck on "Processing" Kie cannot reach BACKEND_URL BACKEND_URL must be a public https URL reachable by Kie (use ngrok locally). Reserved credits return automatically after RESERVATION_TTL_MIN (default 120 minutes).
"File storage is not configured" / uploads fail S3 settings missing or wrong Admin → Settings → Storage → Test. Check the bucket policy allows public read and the IAM user can write.
Generation fails right after upload Provider cannot download the file Make sure S3 files are publicly readable (open a file URL in a private browser window).
The "Try Samples" images on the Create pages do not load The host set in VITE_SAMPLE_MEDIA_BASE is unreachable (offline, blocked, or your own host is missing the files) Check VITE_SAMPLE_MEDIA_BASE in frontend/.env and the file layout described in Sample images, then rebuild the frontend.
"The AI service is not configured yet" No Kie key Enter KIE_API_KEY in Admin → Settings → AI Providers and click Test.
Emails not arriving No email provider, or wrong SMTP details Configure Admin → Settings → Email. Look for "Email not sent" in the API log. Port 465 = SSL, 587 = STARTTLS.
"This model has no price configured" The Creator Studio model has no price Set pricingMatrix or costPerSecond on that document in the aimodelcatalogs collection.
"Payments are not configured yet" No Stripe secret key Enter STRIPE_SECRET_KEY in Admin → Settings → Payments.
Stripe payment succeeded but no credits Webhook missing or misconfigured Check the webhook URL (<BACKEND_URL>/api/payment/stripe/webhook), the two events, and STRIPE_WEBHOOK_SECRET. Stripe's dashboard shows each delivery attempt.
Ad Localizer language "Failed" Translate/voice/lip-sync step errored Hover for the reason; verify all three provider keys with their Test buttons.
Refreshing a page returns 404 in production No single-page-app fallback Configure the web server to send unknown paths to index.html (nginx: try_files $uri /index.html;).
Large uploads fail with 413 in production nginx body size limit Set client_max_body_size 100m; (as in deploy/nginx.conf.example).

Glossary #

Term Meaning
Credit The app's usage currency. Every generation spends credits.
Reservation A temporary hold on credits while an async job runs; becomes a real charge on success or is released on failure.
Lip-sync Matching an avatar's mouth movements to an audio track (NewPortAI).
Avatar The AI "presenter" in a talking video — from the catalog or user-created.
Brand Kit A saved brand identity applied to generations to keep output on-brand.
Webhook A callback an external service (Kie, Stripe) makes to the backend to report an event.
JWT JSON Web Token — the signed login token carried on every request.
IPN Instant Payment/Process Notification — the name used for the Kie completion webhook.
Starter data Data every installation needs: model catalog with example prices, avatar-form lists, community categories and credit packs.
Demo data The demo user (user@showcaseai.app) and that user's starting credits. For local testing only; never created when NODE_ENV=production.
Seed The starter data (admin, demo user, model catalog, community categories, credit packs, dropdown lists) that npm run setup loads into MongoDB. Prompts and avatar templates are added by you.
Admin → Settings The admin screen where provider keys and site options are stored (encrypted) in the database.
Tunnel A tool such as ngrok that gives a local API a temporary public https URL so providers can call it.