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 settings
  • FOLLOWUPBOSS_API_KEY — Your FUB API key

Common Mistakes

  • Don't use base44.functions.invoke() — use direct fetch()
  • 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