Widget Integration
Como embeber el widget de Rivelko en tu sitio web / How to embed the Rivelko widget
The Rivelko widget is a standalone JavaScript bundle (~28KB) that renders a floating chat bubble or classic form on any website. It uses Shadow DOM for complete style isolation -- it will never conflict with your CSS.
Embed Snippet
Add this before </body> on any page:
<script
src="https://app.rivelko.com/api/widget"
data-form-id="YOUR_FORM_ID"
></script>
| Attribute | Required | Description |
|---|---|---|
data-form-id | Yes | Your form's unique ID (found in the dashboard) |
data-base-url | No | Override the API base URL (for self-hosting) |
How It Works
- The script loads and reads
data-form-idfrom the<script>tag - Fetches the form configuration from
GET /api/forms/:id/config - Checks if the current domain is in the form's
allowedDomainslist - Renders the appropriate UI based on
formType:- CONVERSATIONAL -- AI chat that asks questions one by one
- CLASSIC -- Traditional form with all fields visible at once
- Submits responses via the public API (
/api/chator/api/forms/:id/submit)
Domain Setup
Your website's domain must be in the form's Allowed Domains list. Configure this in the form editor under Settings > Allowed Domains.
Tu dominio debe estar en la lista de dominios permitidos del formulario.
| Domain configured | Allows |
|---|---|
example.com | example.com, www.example.com, sub.example.com |
localhost:3000 | localhost:3000 only |
| (empty list) | Blocked everywhere -- must add at least one domain |
Subdomains are automatically allowed when you add the root domain. Protocols (https://) are stripped automatically.
Widget Behavior
Conversational (Chat)
- Floating bubble button appears at the configured position
- Click opens a chat panel with the welcome message
- AI asks questions one at a time based on your form config
- Conversation state is persisted in localStorage (survives page reloads)
- After completion, shows the goodbye message
Classic (Form)
- Same floating bubble button
- Click opens a panel with all form fields visible
- Client-side validation for required fields and field types
- After submission, shows the goodbye message and a "Submit another" option
- Panel auto-closes 3 seconds after successful submission
Blocked
If the requesting domain is not in the form's Allowed Domains, the widget shows an informative message explaining that the domain is not allowed.
Domain enforcement is applied server-side: GET /api/forms/:id/config checks the request Origin and only returns the form content (name, welcome/goodbye messages, questions) to allowed origins. Unauthorized origins receive a minimal projection with those fields stripped, so no form data is exposed. The POST /api/chat and POST /api/forms/:id/submit endpoints reject unauthorized origins with 403 Domain not allowed.
An empty Allowed Domains list blocks the widget on every domain — you must add at least one domain for the form to serve.
Customization
Style options are configured in the form editor under Design:
| Option | Values | Description |
|---|---|---|
primaryColor | Any hex color (default #E11D62) | Accent color for bubble and UI |
avatarUrl | Image URL or null | Avatar shown in chat header |
position | bottom-right, bottom-left | Where the bubble appears |
bubbleIcon | Icon name or null | Custom icon for the bubble button |
Custom Integration (Without Widget)
If you need full control over the UI, you can skip the widget and call the APIs directly.
Si necesitas control total sobre la interfaz, puedes usar las APIs directamente sin el widget.
Conversational Form
// 1. Fetch form config
const config = await fetch(
"https://app.rivelko.com/api/forms/YOUR_FORM_ID/config"
).then((r) => r.json());
// 2. Start a conversation
const response = await fetch("https://app.rivelko.com/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
formId: "YOUR_FORM_ID",
message: "Hello",
}),
});
const conversationId = response.headers.get("X-Conversation-Id");
// Read the streaming response
const reader = response.body.getReader();
const decoder = new TextDecoder();
let assistantMessage = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
assistantMessage += decoder.decode(value, { stream: true });
// Update your UI with the progressive text
}
// 3. Continue the conversation
const next = await fetch("https://app.rivelko.com/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
formId: "YOUR_FORM_ID",
conversationId: conversationId,
message: "My name is Juan",
}),
});
// ... read stream again
Classic Form
// 1. Fetch form config to get questions
const config = await fetch(
"https://app.rivelko.com/api/forms/YOUR_FORM_ID/config"
).then((r) => r.json());
// 2. Render your own form using config.questions
// Each question has: text, type, required, options
// 3. Submit answers
const result = await fetch(
"https://app.rivelko.com/api/forms/YOUR_FORM_ID/submit",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
answers: {
"What is your name?": "Juan Perez",
"Email": "juan@example.com",
},
}),
}
).then((r) => r.json());
console.log(result.conversationId); // Store for reference
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Widget shows "Domain not allowed" | Domain not in allowedDomains | Add your domain in form Settings |
| Widget doesn't appear | Form not published | Publish the form in the editor |
| Widget doesn't appear | Wrong form ID | Check the data-form-id attribute |
| Chat doesn't respond | Form type is CLASSIC | CLASSIC forms use the submit endpoint, not chat |
| "Account inactive" error | Subscription expired (paid plans) | Renew subscription in Settings > Billing |