A custom GPT lives inside ChatGPT, you don't own the users, the billing, or the experience.
Here you rebuild everything that makes it work as your own standalone app you own and can charge for.
Four parts: get it out of ChatGPT → put it online → add logins → add payments.
👉🏽 Haven't written your assistant's instructions yet? Do that first at drerinjacques.com/write-instructions — it hands you the file this build asks for.
The Claude Code extension. In VS Code, click Extensions in the left sidebar, search Claude Code, install the one by Anthropic, then sign in.
An Anthropic API key at console.anthropic.com (with billing). This is separate from ChatGPT Plus and separate from your Claude subscription, different key, different bill.
A free Supabase account at supabase.com (you'll make the project as you go).
A free Stripe account at stripe.com (you'll make the product as you go).
👉🏽 You don't need a terminal for any of this. The extension runs Claude Code for you.
Then install the assistant skill. Open the Claude Code panel and paste this in:
Prompt — Install the skill
Download https://raw.githubusercontent.com/ejacques1/leveraging-ai-skills/main/plugins/leveraging-ai/skills/build-assistant/SKILL.md and save it to ~/.claude/skills/build-assistant/SKILL.md — create the folders if needed. Then restart so it loads.
Approve it when it asks. It loads right away, that's the skill installed.
👉🏽 This is the skill that does the work, it asks you the questions, builds the app, and opens your .env file for you so you never have to go hunting for a hidden file.
Part 1 — Get it running on your machinePayoff: your GPT answers, outside ChatGPT
1 Copy your GPT's brain
Open your GPT → Configure tab. Click inside the Instructions box, select all, and copy. Keep it on your clipboard, you'll paste it in step 2.
👉🏽 Everything that makes your GPT special is trapped in that Instructions box. In a minute it's going to be a file you own.
2 Build it
Make a new empty folder, open it in VS Code (File → Open Folder), open Claude Code, and paste:
Prompt — Build the assistant
build me a custom AI assistant
It asks what your assistant is about, how it should behave, the look, standalone or inside your web app, and paywall or not.
When it asks how it should behave, paste in your GPT's instructions. Claude writes them into a file called assistant-instructions.md for you, along with a knowledge folder. Drop your GPT's knowledge files in there.
👉🏽 That file is your assistant. Everything in it gets sent as instructions on every message, exactly like the Instructions box in your GPT. Open it any time, rewrite it, save, and send a message, you'll see the difference immediately. That's what owning it means.
👉🏽 Click Approve / Yes to everything it asks. It's working inside your empty folder, there's nothing there to damage.
Claude creates a file called .env and opens it for you. Paste your key right after ANTHROPIC_API_KEY= and save with Cmd+S.
👉🏽 No spaces, no quotes around it. Just the key.
4 Run it
Ask Claude to start it:
Prompt — Start the app
Start the app and give me the link to open it.
Open the link it gives you (usually localhost:3000) and send one message.
It answers. That's your GPT, running on your machine, not inside ChatGPT.
👉🏽 If you added your key while the app was already running, tell Claude to restart it. It only reads the key on startup.
Part 2 — Put it livePayoff: a link you can text someone
5 Push to GitHub
Paste this into Claude:
Prompt — Push to GitHub
Push this to a new private GitHub repo. Confirm my .env file is excluded before you push. If I'm not signed in to GitHub yet, walk me through connecting it.
👉🏽 No GitHub account yet? Make one free at github.com. You don't need to download anything — no desktop app. Claude will open a browser window to connect it, you click authorize, and it keeps going.
👉🏽 Your key does not go to GitHub, it's protected by the .gitignore. That's why the prompt asks Claude to confirm it before pushing.
6 Deploy on Vercel
Go to vercel.com/new, sign in with GitHub, and import the repo.
👉🏽 Private repo? Vercel may ask for repository access first. Click Configure GitHub App, grant it, then come back. That's normal.
👉🏽 Sign in with GitHub so the accounts match. Otherwise the deploy can silently fail.
7 Add your key, then deploy
Expand Environment Variables and add your ANTHROPIC_API_KEYbefore you deploy. Then Deploy, and open the live URL on your phone.
⚠️ Add the key now, not after. Skip it and your first deploy is broken.
Part 3 — Add loginsPayoff: a login screen and users in your dashboard
8 New Supabase project
supabase.com → New project (name it, set a database password, pick a region). It takes a couple of minutes to set up.
That's all you need here for now. You'll come back for your keys in step 11, once Claude has written the code that uses them.
👉🏽 Supabase handles sign-up, login, and password resets for you, so you're not building any of that yourself. The free plan is plenty to start.
9 Set your login URLs
Authentication → URL Configuration → add http://localhost:3000 and your Vercel URL.
⚠️ Skip this and login works on your computer but breaks on the live site. Supabase only sends people back to addresses you've listed here.
Nothing else to change here. Supabase already requires people to confirm their email — it's on by default. That means every new signup clicks a link in their inbox before they can use your app, which keeps fake and mistyped addresses off your list. You can see it under Authentication → Providers → Email if you want to look, but leave it alone.
👉🏽 So when you test, you'll go find that confirmation email each time. Expect a wait, and know that Supabase's built-in email sender is rate-limited, so several signups in a row may stop arriving. That's the free sender, not your app. Connecting your own email service fixes it.
10 Add login and gate it
Paste this into Claude:
Prompt — Add login
Add Supabase login so people can create accounts and log in, and gate the assistant so only logged-in users can use it. I've already created my Supabase project, so don't tell me to make one. Ask me once for all three values — Project URL, publishable key, and secret key — and put them in my .env now, even if the secret key isn't used until I add payments later.
Claude writes the login screen, the sign-out button, and the gate on your chat. Then it opens your .env so you can paste your keys in.
👉🏽 Paste your three Supabase values in when it opens the file, and save.
11 Now grab your keys, then test
Back to Supabase. Grab all three at once so you're not making this trip twice:
Project URL — Settings → Data API
Publishable key — Settings → API Keys
Secret key — same page. You won't use it until Part 4, but grab it now.
👉🏽 Ignore the "Legacy" tab, those keys are being retired. Use publishable + secret.
Claude will add all three to your .env for you — paste them in when it asks.
Next, put the same three into Vercel → your project → Settings → Environment Variables. Claude will tell you the exact names it used, usually:
NEXT_PUBLIC_SUPABASE_URL — your Project URL
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY — your publishable key
SUPABASE_SECRET_KEY — your secret key
Now push the login code up. Everything Claude wrote is still only on your computer:
Prompt — Push the login
Push these changes to GitHub so my live site gets the login.
⚠️ Keys into Vercel first, then push — same rule as step 7. Pushing first deploys the login code before the keys exist, so the live site breaks until you redeploy again. And don't reach for Vercel's "Redeploy" button instead of pushing: it just re-runs your last push, it can't see files on your computer.
Give it a minute to deploy, then sign up on the live site and watch yourself appear in Supabase under Authentication → Users.
⚠️ The secret key never gets NEXT_PUBLIC_ in front of it. That prefix ships it to the browser for anyone to steal. The first two are meant to be public, the third is not.
Part 4 — Add paymentsPayoff: someone subscribes and gets in
12 Create the product
Stripe → Product catalog → create a product with a monthly price. Name it whatever your assistant is — customers see this at checkout.
Before you copy anything, give it somewhere to go.
⚠️ Close your .env tab first if it's still open from Part 3. Claude is about to write to that file, and if your editor has an older copy open, saving fails with "the content of the file is newer" — which looks alarming and isn't. Close it, then run the prompt.
Prompt — Prepare your .env for Stripe
Add all the Stripe environment variables to my .env with empty values and open the file for me — publishable key, secret key, price ID, and webhook signing secret — using whatever names the code will expect. Include the webhook one even though I won't have that value until later, so the file is complete. Before you edit it, check whether I have the file open, since editing it behind me will break my save. I'll paste the values in as I get them.
Your .env opens with all four labels waiting. Collect the first three now, pasting each into the file as you go:
Price ID — on the product you just made, starts with price_
Publishable key — Developers → API keys, starts with pk_
Secret key — same page, starts with sk_
Webhook signing secret — leave this one empty. It doesn't exist until step 14, because Stripe won't give you one until your site is deployed and has a URL to point at.
👉🏽 Take the one starting price_, not prod_. Everyone grabs the wrong one.
⚠️ Keep your secret key private. Don't paste it into a chat, email it, or put it in a screenshot. Anyone who has it can charge cards, issue refunds, and see your payout details.
Save the file. Now put those same three into Vercel while you have them → your project → Settings → Environment Variables. Copy the name and the value straight across from your .env so they match exactly.
👉🏽 Same as Part 3 — .env is your computer, Vercel is your live site, and neither can see the other. Every key goes in both. Leave the webhook one out for now; it's coming in step 14.
Last thing here: back in Stripe → Coupons → make a 100%-off coupon and a promotion code, so you can test the whole thing without charging yourself.
13 Add payments
Your keys are already in .env from the last step, so Claude has everything it needs. Paste this in:
Prompt — Add payments
Add a Stripe subscription: a subscribe button, a webhook handler, and gate the assistant so only paying subscribers get in. Turn on row level security for every table you create. Anything that records who has paid must be read-only for users — only the webhook writes to it — so nobody can mark themselves as subscribed and skip paying. On the checkout session, turn on promotion codes and set payment_method_collection to if_required, so anyone using a 100%-off code isn't asked for a card. Save the SQL I need to run in Supabase to a file called supabase/schema.sql, and tell me when it's there.
👉🏽 In your VS Code sidebar you'll now see a supabase folder with schema.sql inside it. Open it, select all, copy. Then Supabase → SQL Editor → paste → Run. It creates the table that tracks who's paid, with the security rules already set.
Now push it up. Your keys are already in Vercel from step 12, so this deploy will work first time:
Prompt — Push the payments
Push these changes to GitHub so my live site gets the payments. Then give me the full webhook URL to paste into Stripe — my live site address plus the path you created. My live site address is: PASTE-YOUR-URL-HERE
👉🏽 Replace PASTE-YOUR-URL-HERE with your live site address before you send it. Get it from Vercel → your project → the big link at the top.
⚠️ Vercel shows you more than one address. Use the short one — something like your-project.vercel.app, with no random characters in it. The long ones point at one single deploy, so a webhook aimed at those works today and quietly stops working after your next push.
Claude hands you back the complete webhook address. That's what you'll paste into Stripe in step 14.
14 Connect the webhook
In Stripe, go to Settings and type webhook in the search bar → Create a webhook. Leave the scope on Your account and the API version as-is.
Paste in the webhook URL Claude gave you, then use the search box under Events to find and tick these four:
checkout.session.completed
customer.subscription.created
customer.subscription.updated
customer.subscription.deleted
👉🏽 Search for them one at a time rather than using the suggested groups — those add far more than you need. Selected events should say 4 before you finish.
Then copy the signing secret (whsec_...) into your .env — that's the last empty slot — and into Vercel as STRIPE_WEBHOOK_SECRET. Then redeploy.
⚠️ Without the deleted event, people who cancel keep access forever.
15 Test the whole thing
Sign out. Create a new account. Subscribe using your 100%-off promo code. Get in.
That's the product. The three steps below are add-ons, except one: step 18 saves people's conversations. Right now a refresh wipes the chat, and nobody pays twice for something that forgets them. Do that one before you sell this.
🎉 You just built a product. Your instructions, your knowledge, your domain, your users, your money, not a GPT you rent from OpenAI.
Optional — add more assistantsPayoff: one login, one payment, as many assistants as you want
16 Turn it into a set of tools
Right now you have one assistant. If you've built several custom GPTs, you don't need a separate app for each one — you can put them all behind the same login and the same subscription, with a dashboard where people pick the one they want.
Only do this once everything above is working. Get one assistant running end to end first, then add the rest. Then paste this in:
Prompt — Add a dashboard
Add a dashboard after login that lists my assistants as cards. Move my instructions into an assistants folder with one markdown file per assistant, and have the dashboard read that folder, so adding a new file adds a new assistant automatically, with no code changes. Keep the same login and paywall covering all of them. Give each assistant its own knowledge folder named to match its file — assistants/email-writer.md reads knowledge/email-writer/ — and move my existing knowledge into the folder for my first assistant. If I already have saved conversations, tag them to my first assistant so they don't disappear. Update the file tracing config in next.config.ts so the new folders still ship when I deploy.
Your single assistant-instructions.md becomes a folder, and each assistant gets its own knowledge to match:
The names match on purpose. That's how each assistant knows which knowledge is its own — there's no list to keep updated. Open Competitor Scout and it reads only the Competitor Scout folder.
Adding another assistant after that is one line. Paste this into Claude, with your own name and instructions:
Prompt — Add another assistant
Add a new assistant called [name]. Here are its instructions: [paste them]. Create its knowledge folder too, even if I don't have anything to put in it yet.
Claude writes the file into your assistants folder. Push it up and it appears on the dashboard on its own. You only do the dashboard step once — everything after that is this one line.
👉🏽 Nothing changes in Supabase, Stripe, or Vercel. Same login, same payment, same keys — your assistants are just files in your project.
Make the dashboard yours. Three optional add-ons, in any order. Each one is a prompt, and two of them need you to put a file somewhere first.
1. Put them in the order you want. Right now they're alphabetical.
Prompt — Set the order
Let me control the order the assistant cards appear on the dashboard. Don't do it by renaming the files, since the filename is how each assistant is linked to its saved conversations and its knowledge folder. Use a small block at the top of each assistant's markdown file instead, and make sure that block is stripped out before the file is sent to the AI so it isn't read as an instruction. Anything without an order falls back to alphabetical.
After that, each assistant file starts with a number you can change:
assistants/prd-builder.md
---
order: 1
---
# PRD Builder
Change 1 to 3 and it moves down. The file is never renamed.
2. Add your logo. This one tells you where to save the file and opens the folder for you.
Prompt — Add your logo
Add my logo to the dashboard and to the header on each assistant's chat screen. First tell me the exact folder to put the logo file in, open that folder for me, and wait until I tell you it's there. Then size it by height so the shape of the file doesn't matter, keep it sharp on phones, and make sure it still reads in dark mode.
3. Give each assistant its own picture, so they don't all look alike on the dashboard. Same as above, it walks you through where the images go and what to call them.
Prompt — Cover image per assistant
Let me give each assistant its own cover image on the dashboard card. First create the folder the images go in, open it for me, and tell me exactly what to name each file so it matches the right assistant. Wait until I tell you they're in. Then match the image to the assistant by name, the way the knowledge folders work, so I just drop a file in and it appears. Accept png, jpg, webp and svg. Crop images to a consistent shape so the cards line up no matter what size file I use, and if an assistant has no image, fall back to something that looks deliberate rather than a broken image.
Then tell Claude "push these changes to GitHub" and your live site picks all of it up.
👉🏽 There's no upload screen, and you don't need one. You add the files in VS Code the same way you add an assistant. Keep each image under about half a megabyte though — they ship with your app, so a photo straight off your phone will slow the page down.
Optional — email your buyersPayoff: paying customers land on your list automatically
17 Tag buyers in Systeme.io
Right now someone pays and you never talk to them again. This connects Stripe to your email list, so paying gets them tagged and your welcome sequence fires on its own.
Don't have an email platform yet? I use Systeme.io — the free plan covers everything below.
First, three things in Systeme.io (Claude can't log into your account, so these are yours):
Settings → Public API Keys → create one and copy it
Contacts → Tags → create a tag called paid
Automations → new automation, trigger = Tag added (paid), action = send your welcome email
Then paste this into Claude:
Prompt — Tag buyers in Systeme.io
When someone completes a Stripe payment, tag them in Systeme.io as "paid." Add this to my existing Stripe webhook handler — don't create a second one. On checkout.session.completed, verify the Stripe signature, take the customer's email, and use the Systeme.io API to find or create the contact and add the "paid" tag. Read the key from a SYSTEME_API_KEY env variable. Check the current Systeme.io API docs first so the endpoints are right, and handle the case where the contact already exists.
Then tell Claude "push these changes to GitHub", add SYSTEME_API_KEY to Vercel's Environment Variables, and redeploy.
⚠️ Don't let it build a second webhook. You already have one from step 14 listening for checkout.session.completed. Two handlers on the same event is how you end up with the tag firing but access not granted — that's why the prompt says to extend the existing one.
👉🏽 Test it with your 100%-off promo code, then check Contacts in Systeme.io. You should see yourself with the paid tag and the welcome email on its way.
Do this before you sell itPayoff: people close the app and pick up where they left off
18 Save people's conversations
Right now a refresh wipes everything. Close the tab and the conversation is gone. This adds a sidebar of past chats, the way ChatGPT has, so people come back and their work is still there.
This isn't a bonus. It's last on the list because it needs your database, not because it's optional — a paid assistant that forgets every conversation feels broken to the person paying for it. Do it once your paywall works, so only one thing is new at a time. Then paste this in:
Prompt — Save chat history
Add saved chat history. Every conversation should be stored so people can close the app, come back, and pick up where they left off. Add a sidebar listing their past conversations, newest first, with a New chat button and a way to delete one. Name each conversation from the first message they sent. Save the messages on the server, in the same route that talks to the AI, not in the browser — that's the only place that sees the finished reply, and the only place someone can't fake a transcript. If I have more than one assistant, record which one each conversation belongs to: only show that assistant's chats in its sidebar, and don't load a conversation that belongs to a different assistant even if someone puts its id in the address bar. Turn on row level security so people can only ever see their own conversations. When a message is written, check two things — that it belongs to the person writing it, and that the conversation it's going into is also theirs. Checking only the first still lets someone write into a stranger's chat. Save the SQL I need to run in Supabase to supabase/schema.sql, and tell me when it's there.
When Claude says the file is ready, do the same thing you did in step 13: open supabase/schema.sql in the side panel, copy all of it, and in Supabase go SQL Editor → New query → paste → Run. Then tell Claude "push these changes to GitHub" so your live site gets it.
⚠️ Two checks, not one. That line in the prompt about checking both the person and the conversation isn't padding. Without it, someone who's logged in can write messages into another customer's chat. It's the kind of thing that looks finished and isn't, so don't trim the prompt down.
👉🏽 Conversations from before you added this don't come back — nothing was being saved. History starts from the next message.
If you get stuck
Common snags
"Do I need to know how to code?" No. You're not writing any. You're pasting prompts and clicking Approve.
"Isn't my Claude subscription enough?" For Claude Code, yes. The app itself needs a separate API key from console.anthropic.com. Different key, different bill.
"Nothing happened when I typed the slash command." Make sure you're typing it in the Claude Code chat box, not a terminal. And run them one at a time.
"It's asking me to approve something and I don't know what it is." Approve it. It's working inside your empty folder, there's nothing there to damage.
"My key isn't working." Check there's no space after the = and no quotes around the key. Then tell Claude to restart the app, it only reads the key on startup.
"My first Vercel deploy is broken." You probably deployed before adding the API key. Add it under Settings → Environment Variables, then redeploy.
"I added login (or payments) but my live site doesn't have it." You didn't push. Vercel's "Redeploy" button re-runs your last push — it can't see files on your computer. Tell Claude "push these changes to GitHub", wait a minute, then reload.
"It says 'the content of the file is newer' and won't let me save my .env." Nothing is lost or exposed. You had the file open while Claude wrote to it, so your editor is holding an older copy. Click Overwrite — that keeps what you typed. If Claude's new lines disappear, ask it to add them again. To avoid it entirely, close .env before asking Claude to change it.
"I paid with my test code but I'm still locked out." That's the webhook. Check the endpoint URL matches the one Claude gave you, and that all three events are selected.
"Can I change my assistant later?" Yes, that's the whole point. Open assistant-instructions.md, rewrite it, save. Add files to knowledge any time. Push to GitHub and it's live in about a minute.
Want the parts that turn it into a business?
Custom domain and business email, usage limits so no one runs up your bill, pricing, and how to find clients who pay four figures for this, that's what we build together inside the community.