Security checklist for AI client work
Building an app for a client? Put the keys in their accounts, turn on RLS, keep secret keys on the server, cap AI spend, and hand over access cleanly.
By ShipSecurely Editorial · Updated · How we make these guides
Shows up in: Supabase, Vercel, GitHub, OpenAI, Anthropic, Gemini API
What it is
If you build apps for clients, the app should belong to the client from the first day. Create every key in the client's own accounts. Lock down the database before handoff. Put a spend limit on every AI provider key. Write down who rotates which key. Then transfer ownership and remove your own access.
This page is for freelancers and new AI agencies. Each check links to the vendor page it is based on. It is educational guidance, not a security audit.
Whose account should the API keys live in?
The client's. Create the Supabase project, the hosting project, the repository, and every AI provider key in accounts the client owns, then have the client invite you. Anthropic's API key safety guide says: "If someone needs access to the Claude API, they should obtain their own key." The same page suggests separate keys for development, testing, and production, so a compromised key can be turned off for just that use.
Do not send keys to the client in a message. Supabase's API keys guide lists chat, email, and SMS among the ways a secret key must never travel. When the keys are created in the client's accounts, nobody has to send them.
If the work started in your accounts, plan the move before launch. The transfer steps for Supabase, Vercel, and GitHub are under How to fix.
How do I cap AI spend on a client's key?
Set the limit in the client's account at each provider. The controls differ, so here is what each provider's own page says.
OpenAI. The spend limits guide has two controls. A spend alert "Sends a notification; API traffic continues". A hard spend limit applies to the organization or to one project, and when it is reached, affected API requests "return a 429 error". To set one, go to Organization limits, or to the project's settings and then Limits. Select Edit spend limit, enter the Monthly spend limit, and turn on Enforce a hard limit. The guide says "Hard spend limits can interrupt production traffic" and "Enforcement is not instantaneous, so recorded spend can slightly exceed the configured amount." Add alerts below the limit so you hear about it first.
Anthropic. The rate limits page says the Start, Build, and Scale tiers each carry a monthly spend cap, and "You can also set your own spend limit below your tier's cap to control costs." Set it in the Claude Console on the Billing page, under Spend limits. When usage reaches a limit you set, "requests return HTTP 400 with error type invalid_request_error". If the client's organization runs more than one app, workspaces can each have a spend limit, set on the Workspaces page. That page also says "You cannot set spend limits on the Default Workspace," so give the app its own workspace.
Gemini API. Google's billing page says "You can set your own project-level spend caps in AI Studio." Set it on the Spend page, under Monthly spend cap, then Edit spend cap. The page marks project spend caps as experimental and says billing data can be delayed up to around 10 minutes, so spend can go past the cap before billing catches up. Long-running tasks such as batch mode completions and agent sessions may also exceed the project spend cap.
A provider limit is a backstop for the whole account. The app still needs its own per-user cap. See No cap on LLM API spend.
Who rotates which key?
Write a short key note and hand it over with the app. One row per key: the service, the account that owns it, where the app reads it (for example, the host's environment variables), who rotates it, and how. Leave the key values out of the note.
For a Supabase secret key, the API keys guide gives the order. Create a new secret key in Settings, then API Keys. Replace the old key everywhere the app uses it. Confirm every component uses the new key. Then retire the old key. Deleting a secret key can't be undone, so do it last. The same guide suggests a separate secret key for each backend component, so a leak means rotating only that one.
For AI provider keys, Anthropic's API key safety guide suggests rotating "on a consistent schedule (for example, every 90 days) by creating new ones and deactivating old ones." Put the schedule in the key note, with the client's name next to it.
How to check
Open each service the app uses: the database, the host, the repository, and each AI provider. Every one should be in an account the client owns.
In Supabase, check that row level security is on for every table in an exposed schema. Supabase's Row Level Security guide says "A table in an exposed schema without RLS is readable and writable by any role with a grant on it."
Search the live site's JavaScript for
sb_secret_,service_role, and AI provider keys. A secret key belongs only on a server. See Secret keys shipped in the frontend.Check every view in
public. A view that should respect row level security needssecurity_invoker. See Do Supabase views bypass RLS?Open each AI provider's limits or billing page in the client's account and confirm a spend limit is set.
Confirm the client has the key note, and that each key in it has a named person who rotates it.
After the transfer, check the member list on each service. Remove your account, or lower its role, unless the client asked you to stay.
How to fix
Supabase
Start the project in the client's organization, or transfer it there before launch. Supabase's project transfer guide says you start a transfer from the project's general settings. You need to be the owner of the source organization, and at least a member of the target organization. The guide lists things to clear first: an active GitHub integration connection, project-scoped roles pointing to the project, and log drains.
Before the transfer, turn on row level security on every table in an exposed schema, and write a policy for each operation the app needs. Supabase's API keys guide says secret keys provide "full access to your project's data, bypassing Row Level Security," so keep them on the server. Supabase's Views guide says "If a privileged role creates a view, everyone reading it does so through that role's permissions," and that security_invoker makes the underlying tables' policies apply. The SQL is in Do Supabase views bypass RLS?
After the transfer, the client should own the organization. Supabase's access control guide says each organization must have at least one owner, and an owner can leave with Leave team in the organization's team settings once another owner exists. If you stay on for maintenance, ask for a smaller role. The guide says a Developer has "read-only access to organization resources and content access to project resources but cannot change any project settings."
Vercel
Vercel's transfer guide says you can move a project between teams with zero downtime. You must be an owner of the team you transfer from, and a member of the team you transfer to. Start in the project's Settings, then General, then Transfer Project.
Environment variables are copied, except ones defined in the env and build.env settings of vercel.json. Integrations are not transferred and must be added again. If the project uses a subdomain, the guide says the subdomain is delegated to the new team and the root domain stays on the original team. After the transfer, ask the client to review the team's members.
GitHub
GitHub's transfer guide says you need administrator access to the repository to transfer it, and a new personal-account owner gets an email to accept it. Two lines matter at handoff. "The original owner of the repository is added as a collaborator on the transferred repository." And "If the transferred repository contains webhooks, services, secrets, or deploy keys, they will remain associated after the transfer is complete."
So after the transfer, the client should remove you as a collaborator if you no longer need access, and review the repository's secrets and deploy keys.
Copy the fix prompt
Paste this into Cursor, Lovable, Bolt, or Claude Code. Read the diff before you merge it.
You are preparing this app for handoff to a client. Do not add features. Do not run anything against the database, and do not change any dashboard or account setting. Ask me first. 1. List every API key and secret the app uses: the service, the environment variable name, and every file that reads it. Do not print the values. 2. Flag any secret key, service_role key, or AI provider key that is in client code, in a NEXT_PUBLIC_ or VITE_ variable, or in git history. 3. From the migrations, list every table and view in the public schema. Say whether row level security is enabled on each table, and whether each view sets security_invoker. 4. Check that every AI provider call runs on the server, requires a signed-in user, and sets a maximum output token limit. 5. Draft a key note with one row per key: the service, the account that should own it, where the app reads it, who rotates it, and the rotation steps. Leave the owner and the person blank for me to fill in. Show me the findings first. Propose fixes as a diff, and wait for my approval before you change anything.
Questions
Should I email the client their keys?
No. Supabase's API keys guide lists chat, email, and SMS among the ways a secret key must never travel. Have the client create the keys in their own accounts. Then nothing has to be sent.
Is a provider spend limit enough?
No. It caps the whole account or project, not each user. OpenAI says recorded spend can slightly exceed a hard limit, and Google says spend can go past a project cap while billing data catches up. The app still needs a signed-in user and a per-user daily cap. See No cap on LLM API spend.
The client used a coding agent to add features after handoff. What should they check?
The same list, plus the agent's own report. Check your coding agent's security work has a prompt that asks the agent to list its findings before it changes anything, and the checks to run yourself.
Sources
- Supabase API keys
- Supabase Row Level Security
- Supabase Views
- Supabase project transfers
- Supabase access control
- Vercel: Transferring a project
- GitHub: Transferring a repository
- OpenAI spend limits
- Anthropic rate limits and spend limits
- Anthropic workspaces
- Anthropic: keeping API keys safe
- Gemini API billing
Build apps for clients? Request a free review on /help
/help lists agencies and consultants whose own public pages offer paid, hands-on security or production-readiness work on apps built with Supabase, Lovable, Bolt, or Cursor. Email team@shipsecurely.dev with the page that offers the work for a free review, or pay for Featured at $49/month.
Up to three agencies can be pinned there. Featured never changes the order below the pin.
Related
Guides
Need someone to fix it for you? See /help.
The weekly fix
The weekly fix: 3 real security mistakes in vibe-coded apps and how to fix them, every Tuesday.