How to Integrate Optimly Using JavaScript (fetch) — The Complete Guide
If you’re building a website, a web-app, a custom widget, a backend, or a headless integration, the easiest way to send messages to Optimly is with native JavaScript fetch(). No SDKs, no frameworks required — just a POST request.
In this guide, you'll learn:
- How the Optimly agent endpoint works
- How to authenticate your requests
- How to create new chats
- How to append messages
- How to request agent responses
- How to integrate Optimly directly into any website or frontend
- How to handle errors and edge cases
- How to build a minimal working UI chat widget
- How to track sessions, sources, and user IDs
- How to debug integrations in the Optimly dashboard
This guide is fully self-contained. You don’t need any other documentation.
1. Understanding the Optimly Agent API
Optimly exposes multiple endpoints, but for most JavaScript integrations you’ll use only two:
- Trigger agent response
POST /external/agent - Append message to an existing chat
POST /external/message/new-message
Optional: Create a new chat
POST /external/chat
The typical frontend flow is:
- On page load → create a chat (optional but recommended)
- User sends a message
- You POST the message to your backend or directly to Optimly
- Optimly returns the AI-generated response
- You render the conversation
- The dashboard logs everything: sentiment, topic, frustration flags, intent, etc.
2. Authentication: How Optimly Validates Requests
Optimly accepts three ways to authenticate:
A. Add header: x_api_key
headers: {
"x_api_key": API_KEY
}
B. Add header: Authorization: Bearer <token>
headers: {
"Authorization": `Bearer ${API_KEY}`
}
C. Add query parameter
?access_token_query=YOUR_API_KEY
In browser environments, avoid exposing API keys publicly. You should proxy requests through your backend (covered later in this guide).
3. Quick Start — Send a Message to Optimly Using fetch()
Let’s start with the simplest working example.
Minimal Request: JavaScript fetch()
const API_KEY = "YOUR_OPTIMLY_API_KEY";
const API_URL = "https://api.optimly.io/external/agent";
async function sendMessage() {
const payload = {
chat_id: "web_demo_001",
content: "Hello Optimly, I'm testing via fetch()."
};
const response = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x_api_key": API_KEY
},
body: JSON.stringify(payload)
});
const data = await response.json();
console.log("Agent response:", data);
}
sendMessage();
This example:
- Sends a message
- Gets an agent response
- Stores analytics
- Creates or continues a chat automatically
If you see a JSON response with content or response, you’re fully integrated.
4. Creating a New Chat in JavaScript
While you can use a static chat_id, best practice is:
- Create a new chat when the user opens the page
- Persist the chat ID in
localStorage - Reuse the same chat for all messages
Create chat:
async function createChat(clientId) {
const response = await fetch("https://api.optimly.io/external/chat", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x_api_key": API_KEY
},
body: JSON.stringify({
client_id: clientId,
ai_enabled: true,
lead_collected: false
})
});
const chat = await response.json();
console.log("Chat created:", chat);
return chat.chat_id;
}
Store chat in localStorage
let chatId = localStorage.getItem("optimly_chat_id");
if (!chatId) {
chatId = await createChat("anon_user_" + Date.now());
localStorage.setItem("optimly_chat_id", chatId);
}
Now the chat persists across:
- page reloads
- multi-message sessions
- embedded widgets
5. Sending a User Message + Getting the Agent Response
When the user submits a message:
Append message (optional step)
POST /external/message/new-message
But you can skip it — /external/agent automatically logs the user message.
Here’s the recommended pattern:
sendUserMessage() function
async function sendUserMessage(content) {
const payload = {
chat_id: chatId,
content: content
};
const response = await fetch("https://api.optimly.io/external/agent", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x_api_key": API_KEY
},
body: JSON.stringify(payload)
});
const data = await response.json();
return data.response; // Agent's reply
}
Example usage:
const reply = await sendUserMessage("Can you help me choose a plan?");
console.log("Agent replied:", reply);
6. Building a Minimal Chat UI (Frontend Only)
Here is a barebones UI you can drop into any HTML page.
index.html
<div id="chatbox">
<div id="messages"></div>
<input id="input" placeholder="Type a message...">
<button onclick="handleSend()">Send</button>
</div>
chat.js
async function handleSend() {
const input = document.getElementById("input");
const text = input.value;
input.value = "";
addMessage("user", text);
const reply = await sendUserMessage(text);
addMessage("assistant", reply);
}
function addMessage(sender, content) {
const el = document.createElement("div");
el.className = sender;
el.textContent = sender + ": " + content;
document.getElementById("messages").appendChild(el);
}
You now have:
- a working chat UI
- powered by your Optimly agent
- full analytics automatically tracked
7. Securing Your API Key (Important!)
Never expose API keys in frontend code.
If users can view page source or network calls, they can steal your key.
Best practice: Use a backend proxy
Frontend → Your backend → Optimly
Example backend route:
// Node.js Express
app.post("/optimly-proxy", async (req, res) => {
const response = await fetch("https://api.optimly.io/external/agent", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x_api_key": process.env.OPTIMLY_API_KEY
},
body: JSON.stringify(req.body)
});
const data = await response.json();
res.json(data);
});
Your frontend calls:
POST /optimly-proxy
Safe and secure.
8. Error Handling in fetch()
Optimly’s API returns standard HTTP codes:
| Status | Meaning |
|---|---|
| 200 | Success |
| 401 | Missing or invalid API key |
| 403 | Forbidden (wrong agent key) |
| 404 | Chat not found |
| 422 | Validation error (bad JSON) |
| 500 | Internal error |
Example error wrapper
async function safeFetchAgent(payload) {
const res = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x_api_key": API_KEY
},
body: JSON.stringify(payload)
});
if (!res.ok) {
const err = await res.json();
console.error("Optimly error:", err);
throw new Error(err.detail || "Unexpected API error");
}
return res.json();
}
9. Tracking User Sessions and Sources
Optimly automatically tracks metadata such as:
- channel (web, email, WhatsApp)
- intent
- topic
- flags
- sentiment
- toxicity
- message complexity
- timing metrics
You can optionally pass a custom client_id when creating chats:
client_id: "user_12345"
or set chat IDs based on your own logic:
chat_id: "web_<yourSessionId>"
Replace
<yourSessionId>with your own session identifier variable in your application code. This is a placeholder for demonstration purposes.
This lets you unify:
- web conversations
- WhatsApp threads
- email replies
- CRM contacts
All under the same Optimly analytics profile.
10. Full End-to-End Example: Browser + Backend Proxy
This is the recommended production architecture:
Frontend → Backend → Optimly
Backend (Node.js)
app.post("/api/chat", async (req, res) => {
const response = await fetch("https://api.optimly.io/external/agent", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x_api_key": process.env.OPTIMLY_KEY
},
body: JSON.stringify(req.body)
});
const json = await response.json();
res.json(json);
});
Frontend
async function sendToOptimly(content) {
const response = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: chatId,
content: content
})
});
return response.json();
}
Why this is best practice:
- no API keys in the browser
- no CORS issues
- full control over rate limits
- ability to add business logic
11. Validating Your Integration in the Dashboard
Once messages start flowing, visit:
Dashboard → Agents → Conversations
You will see:
- Complete conversation timeline
- User messages
- AI responses
- Sentiment
- Emotions
- Toxicity
- Topics
- Intent
- Flags
- Response times
- Tools used
- Knowledge sources (if RAG is enabled)
If your conversation appears here, your integration is confirmed and working.
Conclusion
This guide walked you through:
- Making your first request with
fetch() - Creating chats and persisting IDs
- Using
/external/agenteffectively - Building a full JS chat widget
- Securing API keys
- Error handling
- Using a backend proxy
- Ensuring production-quality communication
With this structure in place, you can now integrate Optimly into any:
- website
- SaaS app
- CRM
- client portal
- landing page
- chatbot
- JavaScript framework (React, Next.js, Vue, Svelte, Astro…)
All future functionality (RAG, lead detection, multi-agent workflows, WhatsApp automation, etc.) builds on this same foundation.
