Skip to main content

How to Integrate Optimly Using JavaScript (fetch) — The Complete Guide

· 5 min read
Daniel Garcia
CEO @ Optimly

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:

  1. Trigger agent response POST /external/agent
  2. 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:

StatusMeaning
200Success
401Missing or invalid API key
403Forbidden (wrong agent key)
404Chat not found
422Validation error (bad JSON)
500Internal 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/agent effectively
  • 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.