You've shipped a Lovable app. The UI looks done. Paddle or Stripe is wired. Resend is supposed to send the invoice.
You still do not know whether a duplicate webhook grants membership twice, or whether a Resend 401 drops the receipt with no log.
This walkthrough builds a gym membership app in Lovable, connects FetchSandbox through Lovable's MCP connector, points the app at twins (API-compatible hosts, not a tap on live Paddle), and injects the failures that show up on a normal week.
Short answer
- Prompt Lovable to build the app on its stack (React, TypeScript, Lovable Cloud). Do not ask it for Next.js or a Python backend.
- Create a key at fetchsandbox.com/keys. Add MCP server
https://fetchsandbox.com/mcp/v1with Bearer token, not None. - Ask the agent to boot Paddle and Resend twins, put
sandbox_base_urlandsandbox_api_keyin secrets, and run checkout. - Arm
rate_limited,auth_failure, and (on Stripe)webhook_retries. Check the membership row, not the 200.
Lovable and RapidDev still stop at test cards and a green webhook delivery. That is the happy path.
Step 1: Build the sample app in Lovable
Open lovable.dev and start a project. Use a prompt that names the providers and the failures:
Build a gym membership app with Paddle subscriptions, Resend for
emailing customer invoices, and resilient handling for duplicate
webhooks, auth failures, rate limits, and payment invariants,
tested against FetchSandbox twins. Build on Lovable's stack
(React, TypeScript, Lovable Cloud) without Next.js or a Python backend.

That run produced Ironworks — a membership landing page with Sign in / Join now, not a Next.js app.

Bookstore variant if you want Stripe + SMS:
Build a bookstore where users browse, add to cart, and check out
with Stripe. Send an order SMS via Twilio and a receipt via Resend.
Handle duplicate webhooks, 429s, and auth failures. Lovable Cloud
stack only — no Next.js, no Python.
You need a checkout path and a place the UI reads for "member" or "order confirmed." Stubbed buttons are not enough.
Step 2: Get a FetchSandbox API key
Sign in at fetchsandbox.com/keys. Create another key. Copy the secret once. The list later only shows fsk_… prefixes.
Do not paste it into Lovable chat.
Step 3: Add the MCP connector
Open lovable.dev/dashboard?connectors → + → MCP server.
| Field | Value |
|---|---|
| Server name | FetchSandbox |
| Server URL | https://fetchsandbox.com/mcp/v1 |
| Authentication | Bearer token or API key (not OAuth, not None) |
| Token | the key from /keys |
If the form still shows a connection type, pick the HTTP / direct option. This endpoint is streamable HTTP, not SSE.
Add server. If MCP server is greyed out, a workspace admin disabled custom servers.
Chat connectors are personal. They are not in the published app. Screenshots of the keys page and the form: Test Your Lovable Integration With a Custom MCP Connector.
Step 4: Boot twins and point the app at them
In Lovable chat:
Use FetchSandbox to spin up a Paddle twin and a Resend twin.
Give me sandbox_base_url and sandbox_api_key.
Put those in secrets. Point the Paddle and Resend clients at
the twin hosts, not api.paddle.com or api.resend.com.
For the bookstore prompt, boot Stripe and Twilio twins the same way.
FetchSandbox does not sit in front of live Paddle and "capture" production traffic. The Edge Function calls a twin that speaks the same paths and error envelope. Same idea as the Cozy Closet Stripe/Resend preview checkout.
Then a sanity run:
Run a membership checkout against the Paddle twin.
Show the subscription (or transaction) id, then SELECT the
columns the Join now / member gate uses for this user.
If Paddle says paid and the gate still says visitor, stop. That join is paid without unlock — same bug on Stripe.
Resend:
Send the invoice email on the Resend twin after a successful
checkout. Show me the POST /emails status. Do not claim
delivered unless email.delivered exists on the twin.
Twilio (bookstore path):
Send the order SMS on the Twilio twin. Show the message sid
and status. queued is not delivered.
Step 5: Inject the messy week
Ask what the sandbox can actually inject, then arm named scenarios. Do not invent provider errors the twin does not have.
What failure scenarios can this Paddle sandbox inject?
Arm rate_limited and run checkout again.
Then arm auth_failure and run it again.
| Prompt in Lovable chat | Twin scenario | What you check |
|---|---|---|
| Duplicate membership / double invoice | Stripe: webhook_retries. Paddle: ask for duplicate-notification / retry | Gate row and invoice count stay 1 |
| SMS or API 429 | Twilio or Paddle rate_limited | App retries or surfaces an error; it does not drop the send with a 200 UI |
| Email 401 | Resend auth_failure | No silent success; no "receipt emailed" until 2xx |
| Payment failed | Stripe payment_declined | No member row |
| Slow webhook | Stripe slow_network | Handler still 2xx or you see the retry; no double grant |
| Bounce | Resend email_bounced | You do not treat bounce as delivered |
Arm webhook_retries on the Stripe twin and check out again.
Did event.id create one order or two?
Arm Twilio rate_limited and trigger the order SMS.
Does the UI lie that it sent?
A handler 500 on a webhook is useful only if you then watch the retry with the same event id. That is webhook_retries, not a one-shot 500.
If Stripe's POST never reaches Deno, you have a JWT 401 after redeploy, not an idempotency bug.
Step 6: Fix, then run the same failure again
Membership is granted twice on a retried Paddle/Stripe notification.
Deduplicate on the stable event id before you upsert the member row.
Then arm the retry scenario again and show me the gate row.
On Resend 401, do not flip receipt emailed. Surface the error.
Arm auth_failure again.
The second run is the proof. "I asked Lovable to fix it" is not.
What this is not
- Not live Paddle or Stripe. Swap real keys after the twin join holds.
- Not Gmail. Twin
POST /emails200 ≠ inbox. - Not Stripe.js inside Lovable's preview iframe. Twins are called from Edge Functions.
- Not an intercepting proxy. If the app still talks to
api.paddle.com, the twin never sees the call.
Broader loop: integration testing for AI-built apps.
Questions about this Lovable tutorial
Do I need FetchSandbox to build the Lovable UI?
No. Lovable builds the UI. FetchSandbox is how you prove Paddle/Stripe/Resend/Twilio behavior before live keys.
Can I pick No authentication on the MCP form?
No. Use a bearer token from fetchsandbox.com/keys. None 401s.
Why Paddle in the screenshots and Stripe in other posts?
Ironworks was prompted with Paddle + Resend. Cozy Closet used Stripe + Resend. The connector and the twin host override are the same. Failure scenario names differ per spec — ask the sandbox, then arm what it lists.
Is a webhook 200 enough?
No. 200 means the function returned 200. Membership means the gate columns for auth.uid() changed once.