Build with Lovable
This guide walks through building a working app on Caramel using Lovable’s AI assistant as the integration layer. By the end, you have a signup form that creates a contact in your Caramel audience and enrolls them in a welcome journey.
The example app is a coffee-shop newsletter signup called BrewClub. Estimated time: 30 minutes.
Note This guide assumes basic Lovable familiarity. If you’re new to Lovable, complete their getting-started tutorial at lovable.dev first.
Why MCP for Lovable
Section titled “Why MCP for Lovable”Lovable’s assistant generates React + TypeScript. It’s most effective when it can discover your API’s schema rather than having each endpoint spelled out. The MCP connector provides the full tool catalog via tools/list at connection time — so you prompt in plain language and Lovable writes the calls.
For server-side integrations (a Node.js backend calling Caramel outside Lovable), use the REST gateway at gateway.caramelme.com directly.
Step 1 — Connect Caramel to your Lovable workspace
Section titled “Step 1 — Connect Caramel to your Lovable workspace”- In Lovable, open Workspace Settings → Connectors → Add MCP server.
- Enter the URL:
https://app.caramelme.com/api/functions/caramel-mcp - Click Connect.
- Complete the magic-link OAuth flow — enter your Caramel email, click the link in your inbox.
- Lovable returns connected.
Verify by asking Lovable’s assistant:
What Caramel tools do I have available?
The assistant calls caramel.v1.meta.capabilities and lists all 19 tools. If nothing comes back, reconnect the connector.
Default scopes granted: meta:read, forms:read, forms:write, audience:read. Add audience:write when you need to upsert contacts directly.
Step 2 — Configure Caramel (one time, ~5 minutes)
Section titled “Step 2 — Configure Caramel (one time, ~5 minutes)”Do this once per business in the Caramel dashboard.
- Create a form — Open Forms → New form. Add these fields:
email(required, type email)first_name(required, type text)favorite_drink(optional, type text)subscribe_to_emails(optional, type checkbox)
- Activate the form. Note the form ID (shown in Forms → Share — looks like
frm_01h7…). - Verify a sender domain — Settings → Domains → Add domain. Publish the DNS records Caramel provides (DKIM, SPF, DMARC). Verification takes 5–30 minutes after DNS propagates.
- Activate a welcome journey — AI Agent → Template Library, search “welcome series” for your industry, click Activate.
That’s the Caramel side. No Caramel-specific code required.
Step 3 — Generate the signup page
Section titled “Step 3 — Generate the signup page”In Lovable’s chat:
Build a coffee-shop newsletter signup page for BrewClub. Header with the BrewClub logo, hero section with “Sign up for fresh-roast news and a free coffee”, and a form with fields for email, first name, and favorite drink, plus a “Yes, send me news and offers” checkbox. Style with warm brown tones. Use shadcn/ui components.
Iterate with follow-up prompts as needed:
- “Add a success state showing ‘Check your inbox!’ after submission.”
- “Require email and first name. Validate email format.”
- “Add a loading spinner during submission.”
Step 4 — Wire the form to Caramel
Section titled “Step 4 — Wire the form to Caramel”Prompt Lovable:
When the form is submitted, call
caramel.v1.form.submitvia the MCP connector. Use form IDfrm_01h7…. Pass email, first_name, favorite_drink, and subscribe_to_emails. Include the current URL as source_url. Show success state on success, error toast on failure.
Lovable generates a handler like this:
const handleSubmit = async (formData: FormData) => { setLoading(true); try { const res = await fetch('https://app.caramelme.com/api/functions/caramel-mcp', { method: 'POST', headers: { 'Authorization': `Bearer ${import.meta.env.VITE_CARAMEL_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'tools/call', id: Date.now(), params: { name: 'caramel.v1.form.submit', arguments: { form_id: 'frm_01h7…', data: { email: formData.email, first_name: formData.firstName, favorite_drink: formData.favoriteDrink, subscribe_to_emails: formData.subscribeToEmails, }, source_url: window.location.href, }, }, }), });
const result = await res.json(); if (result.error || result.result?.isError) { toast({ title: 'Something went wrong', description: 'Please try again.', variant: 'destructive' }); return; } setSuccess(true); } catch { toast({ title: 'Network error', description: 'Please check your connection.', variant: 'destructive' }); } finally { setLoading(false); }};A few things to note about this code:
VITE_CARAMEL_TOKEN— Lovable’s connector injects and refreshes this automatically.- MCP envelope — every call needs
jsonrpc,method,id, andparams. - Error handling — MCP tool errors surface as
result.result.isError === true. Protocol errors surface asresult.error. Check both.
Step 5 — Test the end-to-end flow
Section titled “Step 5 — Test the end-to-end flow”Click Preview in Lovable, fill the form with your own email, and submit.
Expected behavior:
- Loading state for ~300 ms.
- Success state with “Check your inbox!”.
- Welcome email arrives within 30 seconds.
Troubleshooting:
| Symptom | Likely cause | Fix |
|---|---|---|
Authentication required toast | OAuth token expired | Reconnect the Caramel connector in Workspace Settings |
form_not_found in console | Wrong form ID or form is inactive | Run caramel.v1.form.list to get the correct ID |
| Success state but no email | Sender domain not verified | Run caramel.v1.domain.status — check DNS propagation |
validation error | Required field missing | Verify email and first_name are present in the submission |
Step 6 — Track behavioral events
Section titled “Step 6 — Track behavioral events”When a customer hits a milestone (100th order, anniversary), upsert updated traits:
async function trackMilestone(email: string) { await fetch('https://app.caramelme.com/api/functions/caramel-mcp', { method: 'POST', headers: { 'Authorization': `Bearer ${import.meta.env.VITE_CARAMEL_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'tools/call', id: Date.now(), params: { name: 'caramel.v1.contact.upsert', arguments: { business_id: import.meta.env.VITE_CARAMEL_BUSINESS_ID, email, }, }, }), });}Plan
caramel.v1.contact.upsertrequires the Business tier or above. Starter and Growth callers receive403 tier_required. For custom trait writes on lower tiers, submit to a dedicated “milestone” form instead — form submit works on every tier.
Step 7 — Add admin observability
Section titled “Step 7 — Add admin observability”Show the business owner this month’s signup count:
Add an admin panel at
/adminthat shows this month’s signup count and remaining AI credits fromcaramel.v1.meta.usage. Refresh every 10 minutes.
Lovable generates:
useEffect(() => { const fetchUsage = async () => { const res = await fetch('https://app.caramelme.com/api/functions/caramel-mcp', { method: 'POST', headers: { 'Authorization': `Bearer ${import.meta.env.VITE_CARAMEL_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'tools/call', id: Date.now(), params: { name: 'caramel.v1.meta.usage', arguments: { business_id: import.meta.env.VITE_CARAMEL_BUSINESS_ID }, }, }), }); const { result } = await res.json(); setUsage(JSON.parse(result.content[0].text)); }; fetchUsage(); const id = setInterval(fetchUsage, 10 * 60 * 1000); return () => clearInterval(id);}, []);Render usage.forms_submitted_this_month for signups and usage.ai_credits_remaining for credits.
What BrewClub does at this point
Section titled “What BrewClub does at this point”- Customer fills the form → Caramel creates the contact.
- Day 0: welcome email from your verified domain.
- Day 3: follow-up email (configured in the journey).
- The owner checks the admin panel for signup counts.
Current gaps and workarounds
Section titled “Current gaps and workarounds”Coming soon The following are not yet available:
- No imperative
email.send. You can activate a journey but cannot callcaramel.email.send({ to, subject, body })directly. Use journey activation instead. - No outbound webhooks. Your app polls for status — use
caramel.v1.meta.usageor the dashboard. Outbound webhooks are on the roadmap. - No programmatic business provisioning. Multi-tenant SaaS where each customer needs their own Caramel business must redirect customers to
caramelme.com/signupuntil API provisioning ships.
Next steps
Section titled “Next steps”- Embed a form — render Caramel forms natively with your own components.
- Sync contacts — bulk-import from a CRM or database.
- Connect a ChatGPT Custom GPT — same Caramel backend, different AI client.
- Authentication — full OAuth details for when Lovable’s connector isn’t managing tokens for you.