Send the payment-failed email from a Replit app (React and Express, BuildBase sign-in)
Turn on the email BuildBase sends when a card fails, plus one prompt for a signed webhook route in a Replit Agent app for your own side effects.
Replit can also connect to BuildBase itself while it builds: Use BuildBase in Replit.
When a renewal fails, BuildBase emails the workspace owner from your sending domain. That needs no code, only two switches in the console. The prompt adds a webhook route for what you want to happen besides, such as flagging the workspace, verified with your webhook secret. This page assumes the app already signs people in with @buildbase/sdk, as the Replit guide sets up. If it signs people in with something else, such as Supabase or Clerk, read keep your existing sign-in first.
Before you start
A sender connected (connect a sender) and the
Payment Failed notification event switched on
(notification events). For the route, an endpoint at
https://your-app.replit.app/api/webhooks/buildbase under Settings >
Webhooks, with payment.failed ticked (add an
endpoint).
The prompt
The link opens Replit Agent with the prompt filled in, for a new app; you still send it yourself. For the app you already have, paste the prompt into Agent's chat in that project instead.
This app signs people in with BuildBase (@buildbase/sdk), set up as https://www.buildbase.app/guides/add-login-teams-and-paid-plans-to-a-replit-app shows. If it does not, stop and tell me.
Read https://docs.buildbase.app/webhooks/overview.md first and do not invent event names.
Add server/buildbase-webhook.ts and mount it in server/index.ts as app.post('/api/webhooks/buildbase', express.raw({ type: 'application/json' }), buildbaseWebhook), before express.json(). Verify it with parseWebhookEvent from @buildbase/sdk on req.body.toString('utf8'), the x-buildbase-signature and x-buildbase-timestamp headers and BUILDBASE_WEBHOOK_SECRET from Secrets; answer 401 when it returns null.
On payment.failed, flag the workspace in Replit Database; do not send an email, BuildBase sends it. Answer 200 to every other event.Where the secrets go
The console shows the endpoint's signing secret once, when you create it. Add it as BUILDBASE_WEBHOOK_SECRET in the Secrets tool (Tools > Secrets), and again under Publishing > Adjust settings > Production app secrets, then republish. Replit's docs say secrets set in the editor do not carry over to the published app.
The Replit trap
Agent's app-wide express.json() parses the body before a webhook route registered after it sees the raw bytes, and the signature no longer matches: every delivery answers 401. Mount the webhook route with express.raw above express.json(), as the prompt says.
A 401 in the console's delivery log after that means the secret is in the editor's Secrets but not in Production app secrets.
Full guides
- Add login, teams and paid plans to a Replit Agent app
- Failed payments: retries, grace days and the emails
- Add BuildBase to Replit as a skill