AI app builders
No backend? Still a real business.
Apps from Lovable, v0, Bolt, or Replit are mostly frontend, so the usual move is a payment link from a payments API. That collects money and records an amount, and the amount is all you hold. A Flint link records the sale itself: what sold, tax computed, refunds per item, a receipt, a buyer portal. Your app has no database, so that record is your entire back office.
{
"data": {
"payment_link_id": "plink_1kmn0aExample",
"status": "active",
"payment_link_type": "standard",
"url": "https://pay.withflintpay.com/plink_1kmn0aExample"
}
}This URL is the whole integration for a client-only app. The amounts behind it were fixed server-side when the link was created, so it is safe in a button, a bio, or a QR code.
zero keys in the app · zero card fields in your code · every payment lands as a real order
01The rule
The key never ships to the browser.
This is the one thing to hold the line on, because the builder's agent will not. Prompted for payments, it pastes a key into the page and computes the total there too. Everyone who opens the app can read both.
// what a prompt happily writes into the page
const API_KEY = "sk-live-8f2a..."; // now in every browser
async function pay(total) { // and totals from the client
await fetch("https://api.some-processor.com/charges", {
method: "POST",
headers: { Authorization: "Bearer " + API_KEY },
body: JSON.stringify({ amount: total * 100 }),
});
}Frontend code is public code. A key here can be lifted by anyone who opens devtools, and a client-computed total can be edited before it is charged.
Every Flint key is a server credential. There is no browser variant to reach for, so the mistake has no valid form.
server-only keysWhat the app exposes instead is a checkout URL. Amounts were fixed when it was created, so exposure costs nothing.
pay.withflintpay.comCard fields render on Flint's hosted page, never in your generated components, so card data never touches your app.
hosted checkoutTell the agent the rule and it follows it. The prompt at the bottom of this page includes it verbatim.
no keys in app code02Path one
A payment link, before the app has a server.
Any payments API can hand you a link; the difference is what the link leaves behind. A payment models money arriving. An order models the sale: what sold, what tax, what is refundable, and to whom. One call makes this one, or the dashboard does with no code at all.
curl -X POST https://api.withflintpay.com/v1/payment-links \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Idempotency-Key: payment-link-spring-tee-001" \
-d '{
"name": "Limited spring tee",
"line_items": [
{ "name": "Spring tee", "quantity": 1,
"unit_price_money": { "amount": 3500, "currency": "USD" } }
]
}'Make it in the dashboard instead and the result is identical. Links can also take donations with suggested amounts, sell event tickets, or start subscriptions.
Every payment through the link creates a real order with server-computed totals and tax, not a bare charge.
origin: payment_linkBuyers get an emailed receipt and a hosted page; you get the order in the dashboard with refunds one click away.
order.paidLinks deactivate cleanly when the price or product changes, and the orders they created stay.
POST /v1/payment-links/{payment_link_id}/deactivateLink types and depth, donations, events, capacity, custom fields, live on the payment links page.
03Path two
One tiny function, when the builder gives you a server.
Most builders do have a server side, it is just not where the agent defaults to putting things. One function that creates the order and returns the checkout URL is the entire backend a store needs.
// one server function, any runtime that has fetch.
// shown as a Supabase edge function; the body is identical
// in a Node server or a route handler.
Deno.serve(async () => {
const key = Deno.env.get("FLINT_API_KEY"); // a secret, not app code
const order = await fetch("https://api.withflintpay.com/v1/orders", {
method: "POST",
headers: { Authorization: `Bearer ${key}` },
body: JSON.stringify({
line_items: [
{ name: "Riso print", quantity: 1,
unit_price_money: { amount: 1800, currency: "USD" } },
],
}),
}).then((r) => r.json());
const session = await fetch("https://api.withflintpay.com/v1/checkout-sessions", {
method: "POST",
headers: { Authorization: `Bearer ${key}` },
body: JSON.stringify({
order_id: order.data.order_id,
redirects: { success_redirect_url: "https://example.com/thanks" },
}),
}).then((r) => r.json());
// the app opens this URL; the key never left the function
return Response.json({ url: session.data.url });
});Plain fetch, so it runs anywhere: a Supabase Edge Function, a Node server, a route handler. The key comes from the runtime's secret store and never appears in app code.
Lovable
Server work runs in Supabase Edge Functions. Put the key in a Supabase secret and paste the function above nearly verbatim. No Supabase connected? Use path one.
v0
v0 generates Next.js, so you have server actions and route handlers with env vars. The Next.js commerce page walks the exact integration, webhooks included.
Bolt
Bolt builds full-stack Node apps, so the function is a plain server route. Set the key as an env var when you deploy, not in the generated source.
Replit
A real server plus a Secrets pane, which is exactly the shape this needs. Ask the agent to read the key from secrets and keep it out of the repo.
Building in v0 or exporting to Next.js? The Next.js commerce page is the deeper version of this section.
04Underneath
Rebuild the app; keep the store.
Builder apps get regenerated, forked, and abandoned, sometimes in the same week, and they have no database for a store to live in. The record outside the app is not a nice-to-have. It is the business.
Orders, buyers, payment history, and receipts live on Flint. Regenerating the frontend does not touch a single sale.
GET /v1/ordersThe dashboard is the back office you did not have to build: today's orders, who bought what, one-click refunds, payouts. A feed of amounts answers almost none of that.
app.withflintpay.comWhen you outgrow the builder, the integration deepens instead of restarting: same records, same key, more calls.
link → checkout → your own frontendThe general argument for why the record matters more than the app is on the sell online with AI page.
05Start
One prompt, one rule.
Add a Buy button for my eighteen dollar riso print.
Open my Flint payment link when it is clicked. Do
not put any API key or card form in the app code.Create the link first in the dashboard, then give the agent its URL. The rule in the last sentence is the one from section 01, stated where the agent will obey it.
Ready for path two? Swap the last sentence for: create the order and checkout session in a server function with the key from my secrets, and open the returned URL.
Then read these
FAQ
Questions worth asking first.
Why not just use a Stripe payment link?
For collecting money once, either works, and Stripe processes Flint's card payments anyway. The difference is the record each link leaves. A payments API models the payment: an amount that arrived. A Flint link creates an order: line items, computed tax, per-item refunds, an emailed receipt, and a buyer portal, accumulating into history you can run a business on. In an app with no backend that matters double, because these records are the only back office you have. And when the app grows into hosted checkout or subscriptions, it grows onto the same order records instead of starting over.
How do I add payments to a Lovable app?
Lovable keeps server work in Supabase Edge Functions, which is exactly where Flint belongs. Store your Flint key as a Supabase secret, create the order and checkout session inside a function, and have the app open the returned URL. If you have not connected Supabase, skip the server entirely: create a payment link and point your Buy button at it.
How do I add payments to a v0 app?
v0 generates Next.js, so the integration is a server action or route handler with the key in a server-only env var, two calls, order then checkout session, and a redirect to the returned URL. The Next.js commerce page walks that exact code, including the webhook route that confirms payment.
How do I add payments to a Bolt or Replit app?
Both give you a real server, so use it. Keep the Flint key in the environment, Bolt sets env vars at deploy and Replit has a Secrets pane, create the order and session server-side, and send the browser the URL. The prompt to give either agent is on this page.
Can I accept payments with no backend at all?
Yes. A payment link is a full Flint checkout behind a URL: create it in the dashboard or with one API call, then use it as a plain link anywhere, a button, a bio, a DM, a QR code. Every payment through it creates a real order with computed totals and tax, and refunds and receipts work from the dashboard.
Why not just paste my API key into the generated code?
Anything in a builder app's frontend ships to every visitor, so a pasted key is public the moment the app loads, and a Flint key can read orders and move money. Flint keys are server credentials on purpose. The browser only ever needs the checkout URL, which is safe to expose because the amounts behind it were fixed server-side when it was created.
What happens when I outgrow the builder?
You keep the store. Orders, buyers, payment history, and receipts live on Flint, not in the generated app, so rebuilding the frontend, in the same builder or in Next.js with your own code, does not touch them. The integration deepens from a link to hosted checkout to your own frontend against the same order records. Nothing gets migrated because nothing was stored in the app.