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.