FUB Embedded App Guide
Copy-paste snippets for setting up FUB embedded apps in Base44
1. Make the App Public
Settings → General → Make Public. FUB's iframe can't authenticate with Base44.
2. FUB Settings URL
In FUB → Admin → Integrations → Embedded Apps, use ONLY the base URL. FUB appends ?context=...&signature=... automatically.
Base URL (replace with your app)
https://YOUR-APP.base44.app/FubEmbed
3. Exclude FubEmbed from Layout
In Layout.js, skip the sidebar for the embedded page:
Layout.js — add near top of component
if (currentPageName === "FubEmbed") {
return <>{children}</>;
}
4. FubEmbed Page Template
Create pages/FubEmbed.js. Uses direct fetch() — NOT base44.functions.invoke().
pages/FubEmbed.js
import React, { useState, useEffect } from "react";
import { Loader2, AlertTriangle } from "lucide-react";
export default function FubEmbed() {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [contextData, setContextData] = useState(null);
useEffect(() => {
// Load FUB Embedded App SDK
const script = document.createElement("script");
script.src = "https://eia.followupboss.com/embeddedApps-v1.0.1.js";
script.async = true;
document.head.appendChild(script);
// Read context from URL — FUB appends these automatically
const params = new URLSearchParams(window.location.search);
const ctx = params.get("context");
const sig = params.get("signature");
if (!ctx) {
setError("No FUB context. Open from Follow Up Boss.");
setLoading(false);
return;
}
// Call YOUR backend function with direct fetch
async function init() {
const res = await fetch(
"https://YOUR-APP.base44.app/api/functions/yourFunction",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
action: "verifyContext",
context: ctx,
signature: sig || ""
})
}
);
const data = await res.json();
if (!data.success) {
setError(data.error || "Verification failed");
} else {
setContextData(data);
}
setLoading(false);
}
init();
return () => {
if (script.parentNode) script.parentNode.removeChild(script);
};
}, []);
if (loading) return (
<div className="flex items-center justify-center py-10 gap-2 text-gray-400 text-sm">
<Loader2 className="w-4 h-4 animate-spin" /> Loading...
</div>
);
if (error) return (
<div className="p-3">
<div className="flex items-center gap-2 bg-amber-50 border border-amber-200 rounded-lg p-3 text-xs text-amber-700">
<AlertTriangle className="w-4 h-4" /> {error}
</div>
</div>
);
return (
<div className="p-3 max-w-xl mx-auto">
{/* Your embedded UI here */}
<p>Context loaded! Build your UI.</p>
</div>
);
}
5. Backend: Verify FUB Signature
In your backend function, verify the HMAC signature using FUB_EMBEDDED_APP_SECRET:
Backend function snippet
import crypto from "node:crypto";
// Inside your Deno.serve handler:
const { context, signature } = await req.json();
const secret = Deno.env.get("FUB_EMBEDDED_APP_SECRET");
const expectedSig = crypto
.createHmac("sha256", secret)
.update(context)
.digest("hex");
if (expectedSig !== signature) {
return Response.json({ success: false, error: "Invalid signature" }, { status: 403 });
}
// Decode the context
const contextData = JSON.parse(atob(context));
// contextData = { personId, userId, accountId, ... }
// Fetch person from FUB API
const fubKey = Deno.env.get("FOLLOWUPBOSS_API_KEY");
const personRes = await fetch(
`https://api.followupboss.com/v1/people/${contextData.personId}`,
{ headers: { Authorization: "Basic " + btoa(fubKey + ":"), Accept: "application/json" } }
);
const person = await personRes.json();
6. Required Secrets
Set these in Dashboard → Settings → Environment Variables:
FUB_EMBEDDED_APP_SECRET— From FUB embedded app settingsFOLLOWUPBOSS_API_KEY— Your FUB API key
Common Mistakes
- ❌ Don't use
base44.functions.invoke()— use directfetch() - ❌ Don't add
?context=...&signature=...to the URL in FUB settings - ❌ Don't require Base44 login — app must be public
- ❌ Don't wrap FubEmbed in authenticated layout/sidebar