Skip to content

Widget & Flowbuilder Implementatie

v9 Implementatie gids voor het toevoegen van de Magic Agent chatwidget en flowbuilder aan een tenant.


┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Storefront │───▶│ Widget Server │───▶│ Agent Backend │
│ (widget-loader) │ │ (port 4060) │ │ (port 40XX) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
┌─────────────────┐ │
│ Flowbuilder │◀─────┘
│ (port 3003) │ │
└─────────────────┘ │
┌──────────────┐ ┌─────────────────┐
│ n8n │ │ PostgreSQL │
│ (port 80XX) │ │ magic_agent_{t} │
└──────────────┘ └─────────────────┘
ComponentLocatieFunctie
Widget Loader/widget/widget-loader.jsLaadt de chatwidget op de storefront
Widget Core/widget/widget-core.jsChat UI, berichten, uploads, knoppen
Widget Server/widget/widget-server.tsProxy + config API + chat history
Agent Backend/agents/{tenant}/Flow executor, AI, API endpoints
Flowbuilder/flowbuilder/Visuele flow editor (React + ReactFlow)
Flow API/flow-api/CRUD voor flows

Maak een nieuwe database magic_agent_{tenant} aan:

CREATE DATABASE magic_agent_{tenant};

De tabellen worden automatisch aangemaakt bij eerste start van de agent. De belangrijkste tabellen:

TabelFunctie
chat_sessionsActieve chat sessies met klantinfo
chat_messagesAlle berichten per sessie
agent_flowsFlowbuilder flows (nodes + edges als JSON)
flow_sessionsActieve flow status per chat sessie
widget_configsWidget configuratie (kleuren, teksten, domeinen)
conversation_contextGesprekscontext (product, budget, etc.)
ai_promptsSysteem prompts per agent
ai_metricsToken gebruik en kosten

Kopieer een bestaande agent als template:

Terminal window
cp -r /mnt/data/magic_omniverse/magic_agent/agents/jodasign \
/mnt/data/magic_omniverse/magic_agent/agents/{tenant}
# Database
DB_HOST=192.168.1.26
DB_PORT=5432
DB_USER=postgres
DB_PASS=<your-db-password>
DB_NAME=magic_agent_{tenant}
# Medusa commerce database
MEDUSA_DB_HOST=192.168.1.26
MEDUSA_DB_NAME=magic_b2b_{tenant}
# Agent
AGENT_NAME={tenant}
AGENT_PORT=40XX
NODE_ENV=production
# AI
ANTHROPIC_API_KEY=sk-ant-...
# FastEditor (optioneel)
FASTEDITOR_API_URL=https://api.editor.fasteditor.com/api
FASTEDITOR_API_KEY=...
FASTEDITOR_PARTNER_ID=magiceverse
# Email
SMTP_HOST=mail.magiceverse.nl
SMTP_PORT=587
SMTP_USER=portal@magiceverse.nl
SMTP_PASS=...
Terminal window
cd /mnt/data/magic_omniverse/magic_agent/agents/{tenant}
npm install
npm run build
pm2 start dist/src/index.js --name magic-agent-{tenant}

De widget configuratie wordt opgeslagen in de widget_configs tabel. Stel deze in via de Flowbuilder UI of direct in de database:

INSERT INTO widget_configs (
agent_name, brand_name, bot_name, primary_color,
welcome_message_nl, welcome_message_en, welcome_message_de,
position, is_active, allowed_domains
) VALUES (
'{tenant}', 'Bedrijfsnaam', 'Nova', '#CF0D65',
'Hallo! Waarmee kan ik je helpen?',
'Hello! How can I help you?',
'Hallo! Wie kann ich Ihnen helfen?',
'bottom-right', true,
ARRAY['*.magiceverse.online', 'www.{tenant}.nl']
);
ParameterTypeBeschrijving
agent_namestringUnieke agent identifier
brand_namestringBedrijfsnaam in de widget
bot_namestringNaam van de chatbot (bijv. “Nova”, “Luke”)
primary_colorhexHoofdkleur van de widget
secondary_colorhexSecundaire kleur
accent_colorhexAccent kleur
gradient_colorhexGradient kleur voor header
positionstringbottom-right of bottom-left
logo_urlurlLogo URL voor in de widget
welcome_message_nlstringWelkomstbericht (Nederlands)
welcome_message_enstringWelkomstbericht (Engels)
welcome_message_destringWelkomstbericht (Duits)
welcome_message_returning_nlstringTerugkerend bezoeker bericht
allowed_domainsarrayDomeinen waar de widget mag laden
is_activebooleanWidget aan/uit

Maak een ChatWidget component:

storefront/src/modules/common/components/chat-widget/index.tsx
"use client"
import { useEffect } from "react"
type Customer = {
id: string
email: string
first_name: string
last_name: string
}
export default function ChatWidget({ customer }: { customer?: Customer | null }) {
useEffect(() => {
// Set customer context
if (customer) {
window.__MEDUSA_LOGGED_IN__ = true
window.__MEDUSA_CUSTOMER__ = customer
window.MagicWidgetConfig = {
customer_context: {
email: customer.email,
name: `${customer.first_name} ${customer.last_name}`.trim(),
customer_id: customer.id,
},
}
}
// Inject widget loader script
if (!document.getElementById("magic-widget-loader")) {
const script = document.createElement("script")
script.id = "magic-widget-loader"
script.src = "https://agent.magiceverse.online/widget/widget-loader.js"
script.setAttribute("data-agent", "{tenant}")
script.setAttribute("data-tenant", "{tenant}")
script.setAttribute("data-api-url", "https://agent.magiceverse.online")
script.setAttribute("data-position", "bottom-right")
script.setAttribute("data-language", "nl")
if (customer) {
script.setAttribute("data-logged-in", "true")
}
document.body.appendChild(script)
}
}, [customer])
return null
}
storefront/src/app/layout.tsx
import { cookies } from "next/headers"
import ChatWidget from "@modules/common/components/chat-widget"
async function getCustomer() {
const cookieStore = cookies()
const token = cookieStore.get("_medusa_jwt")?.value
if (!token) return null
// Fetch customer from Medusa API
const res = await fetch(`${MEDUSA_BACKEND_URL}/store/customers/me`, {
headers: { Authorization: `Bearer ${token}` },
})
if (!res.ok) return null
const { customer } = await res.json()
return customer
}
export default async function RootLayout({ children }) {
const customer = await getCustomer()
return (
<html>
<body>
{children}
<ChatWidget customer={customer} />
</body>
</html>
)
}
<script
id="magic-widget-loader"
src="https://agent.magiceverse.online/widget/widget-loader.js"
data-agent="{tenant}"
data-tenant="{tenant}"
data-api-url="https://agent.magiceverse.online"
data-position="bottom-right"
data-language="nl"
></script>

Open de Flowbuilder op https://agent.magiceverse.online/flowbuilder/ en selecteer de juiste agent.

Een flow bestaat uit nodes (stappen) en edges (verbindingen):

Node TypeFunctieVoorbeeld
markerStartpunt / trigger”Offerte Start”
responseTekst tonen aan gebruiker”Welkom! Hoe kan ik helpen?”
user_inputWacht op gebruikersinvoerKnoppen, vrije tekst, foto upload
switchRouteer op basis van waardeAls keuze = “offerte” → …
actionVoer een actie uitAPI call, DB query, order aanmaken
set_variableVariabele opslaanquotation_id = {{__user_input}}
http_requestExterne API aanroepenn8n webhook, product zoeken
trigger_flowStart een andere flowHoofdmenu → Offerte flow
endBeëindig de flowBedankt en tot ziens

Flows gebruiken {{variabele}} syntax voor dynamische waarden:

VariabeleBronVoorbeeld
{{__user_input}}Laatste gebruikersinvoer”100806-001”
{{session_id}}Huidige chat sessie”chat_1234_abc”
{{customer_email}}Klant email (uit sessie)klant@bedrijf.nl
{{customer_name}}Klant naam”Jan de Vries”
{{offerte_result.quotation_number}}API response variabele”Q-2026-0042”
{
"nodeType": "user_input",
"userInputData": {
"inputType": "buttons",
"variable": "main_keuze",
"noTyping": true,
"buttons": [
{ "value": "product", "text": "Product zoeken", "label_en": "Find product" },
{ "value": "offerte", "text": "Offerte aanvragen", "label_en": "Request quote" },
{ "value": "voorraad", "text": "Voorraad checken", "label_en": "Check stock" }
]
}
}
{
"nodeType": "action",
"actionData": {
"type": "http_request",
"url": "http://localhost:8090/webhook/magic-agent/{tenant}/search-products",
"method": "POST",
"body": "{\"query\": \"{{__user_input}}\", \"session_id\": \"{{session_id}}\"}",
"responseVariable": "search_result"
}
}
TypeFunctie
SHOW_QUOTATION_SESSIONSToon openstaande offertes
SHOW_QUOTATION_LINESToon regels van een offerte
CREATE_ORDER_APPROVED_LINESMaak order van goedgekeurde regels
TRIGGER_FLOWStart een andere flow
http_requestExterne API call

Maak een n8n workflow met een Webhook trigger:

Webhook URL: POST /webhook/magic-agent/{tenant}/{actie}
NodeFunctie
WebhookOntvang request van agent
PostgreSQLQuery op commerce database
CodeData transformatie
HTTP RequestExterne API calls
Send EmailSMTP email versturen
Respond to WebhookResponse terugsturen

Maak een PostgreSQL credential in n8n:

  • Host: 192.168.1.26
  • Database: magic_b2b_{tenant}
  • User: postgres
  • Password: (zie .env)

  1. Open de storefront in de browser
  2. De chatwidget bubble verschijnt rechtsonder
  3. Klik op de bubble → welkomstbericht verschijnt
  4. Test de flow door op knoppen te klikken
  1. Open de Flowbuilder (/flowbuilder/)
  2. Selecteer een flow
  3. Klik op “Test” in de toolbar
  4. Voer berichten in en controleer de respons
Terminal window
curl -X POST https://agent.magiceverse.online/api/{tenant}/send-message \
-H "Content-Type: application/json" \
-d '{"session_id": "test_123", "message": "hallo"}'

  1. Gebruiker typt bericht in widget
  2. widget-core.js stuurt POST naar /api/{agent}/send-message
  3. Widget Server proxy’t naar Agent Backend
  4. Agent checkt actieve flow sessie in flow_sessions
  5. Flow Executor verwerkt het bericht door de flow nodes
  6. Bij http_request nodes → n8n webhook of externe API
  7. Bij action nodes → ingebouwde handlers (DB queries, order aanmaken)
  8. Response wordt teruggestuurd naar widget
  9. Widget toont bericht + eventuele knoppen
  • Chat sessie (chat_sessions): Uniek per bezoeker, bevat klantinfo
  • Flow sessie (flow_sessions): Actieve flow status per chat sessie
  • Browser opslag: localStorage voor sessie-id persistentie

De widget ondersteunt NL, EN, DE, FR. Knoppen in flows kunnen per taal gelabeld worden:

{
"value": "offerte",
"label_nl": "Offerte aanvragen",
"label_en": "Request quote",
"label_de": "Angebot anfordern"
}

ProcesPortFunctie
magic-widget-server4060Widget proxy + config
magic-agent-brinxx4040Brinxx agent
magic-agent-jodasign4071JoDa Sign agent
magic-agent-logohorloge4072Logohorloge agent
magic-agent-spranz4073Spranz agent
flow-builder3003Flowbuilder UI
flow-api3004Flow CRUD API