Introduction
Replai adds AI-powered customer support to your product. There are two ways to integrate, and you can use either or both:
1. The Widget
A drop-in chat launcher you add with a single script tag. No UI to build. Best for most teams. See Quick Start and Framework Examples.
2. The REST API
Create tickets server-to-server from any backend. Best when you want full control. See Create a Ticket.
What you need
- • A Replai workspace (one per merchant, created by the Replai team).
- • An API key from Settings → API Keys with the
tickets:read,tickets:write, andmessages:writescopes (the "widget" preset selects all three). - • Your Replai base URL — every endpoint lives under it.
Quick Start: Embed the Widget
The fastest way to add Replai to your product is the embed script. It drops a chat launcher into the corner of your site — no UI to build, works on any stack (the script is plain JavaScript, so your backend language doesn't matter). Paste this once, just before the closing </body> tag:
Before you begin
- • A Replai workspace (created for you by the Replai team).
- • An API key from Settings → API Keys with the
tickets:read,tickets:write, andmessages:writescopes (the "widget" preset selects all three). - • Your Replai base URL — every endpoint lives under it (no separate api.* host).
- • Optional: your logged-in user's id, name, and email to personalize tickets.
<script
src="https://your-replai-domain.com/widget.js"
data-api-key="YOUR_API_KEY"
data-customer-id="LOGGED_IN_USER_ID"
data-customer-name="LOGGED_IN_USER_NAME"
data-customer-email="LOGGED_IN_USER_EMAIL"
defer
></script>data-api-key — generate this in your Replai dashboard under Settings → API Keys (it needs the tickets:read, tickets:write, and messages:write scopes — the widget preset selects all three).
data-customer-id — the logged-in user's unique id in your system. Render it from your server so each customer sees their own "My Tickets". Leave it out for anonymous visitors.
data-customer-name / data-customer-email — optional, used to label tickets and reach the customer.
That's the entire integration for most teams. The branding (colors, title, welcome message) is controlled centrally from your Replai Widget Config page — no code change needed to restyle it. Use the REST API below only if you want to create tickets server-to-server without the widget.
Show it only to logged-in customers
By default the launcher appears for everyone — that's the right choice for public marketing pages. If you'd rather show support only after a customer signs in, add data-require-identify="true" to the tag. The widget then stays hidden until you call Replai.identify(). It's your call — gate it or leave it open.
1. The script tag
<script
src="https://your-replai-domain.com/widget.js"
data-api-key="YOUR_API_KEY"
data-require-identify="true"
defer
></script>2. After login (your code)
// After your customer logs in:
Replai.identify({
id: "ext_001", // your user's id
name: "Jane Doe",
email: "jane@example.com",
})
// On logout:
Replai.hide()JS API: Replai.identify({ id, name, email }) shows the widget for that customer, Replai.show() shows it anonymously, and Replai.hide() removes it (call this on logout).
Framework Examples
The widget is a single script tag plus a small JavaScript API, so it works on any stack — React, Vue, Svelte, Angular, Rails, Django, Laravel, or plain HTML. The examples below all do the same thing: load the loader in gated mode, then call Replai.identify() when your customer is logged in and Replai.hide() on logout. Pick whichever matches your setup, or adapt the vanilla version.
Vanilla JS (any framework)
The universal pattern. If you're not using React or Next.js, just call Replai.identify() from wherever your app already knows who the user is.
Any stack
<!-- Add once, before </body>. Gated: hidden until you identify a user. -->
<script
src="https://your-replai-domain.com/widget.js"
data-api-key="YOUR_API_KEY"
data-require-identify="true"
defer
></script>React (Vite + Redux)
Reads the logged-in user from your Redux auth state. Mount it once inside your app shell.
React integration
import { useEffect } from 'react';
import { useSelector } from 'react-redux';
const API_KEY = import.meta.env.VITE_REPLAI_API_KEY;
const BASE_URL = import.meta.env.VITE_REPLAI_BASE_URL;
const SCRIPT_ID = 'replai-widget-loader';
function ensureScript() {
if (!BASE_URL || !API_KEY || document.getElementById(SCRIPT_ID)) return;
const s = document.createElement('script');
s.id = SCRIPT_ID;
s.src = `${BASE_URL.replace(/\/$/, '')}/widget.js`;
s.setAttribute('data-api-key', API_KEY);
s.setAttribute('data-require-identify', 'true');
s.defer = true;
document.body.appendChild(s);
}
function whenReady(cb, tries = 50) {
if (window.Replai?.identify) return cb();
if (tries <= 0) return;
setTimeout(() => whenReady(cb, tries - 1), 100);
}
export default function ReplaiWidget() {
const user = useSelector((s) => s.auth.user);
const isAuthenticated = useSelector((s) => s.auth.isAuthenticated);
useEffect(() => {
if (!BASE_URL || !API_KEY) return;
ensureScript();
if (isAuthenticated && user) {
whenReady(() =>
window.Replai.identify({
id: user._id || user.id || user.email,
name: user.fullName || user.name || 'Customer',
email: user.email,
})
);
} else {
whenReady(() => window.Replai.hide());
}
}, [isAuthenticated, user]);
return null;
}Next.js (App Router)
Reads the session in a Server Component and passes the user to a Client Component that loads the widget.
Next.js integration
'use client';
import { useEffect } from 'react';
const API_KEY = process.env.NEXT_PUBLIC_REPLAI_API_KEY;
const BASE_URL = process.env.NEXT_PUBLIC_REPLAI_BASE_URL;
const SCRIPT_ID = 'replai-widget-loader';
declare global {
interface Window {
Replai?: {
identify: (info: { id: string; name?: string; email?: string }) => void;
show: () => void;
hide: () => void;
};
}
}
type ReplaiWidgetProps = {
user: { _id: string; email: string } | null;
};
export default function ReplaiWidget({ user }: ReplaiWidgetProps) {
useEffect(() => {
if (!BASE_URL || !API_KEY) return;
if (!document.getElementById(SCRIPT_ID)) {
const s = document.createElement('script');
s.id = SCRIPT_ID;
s.src = `${BASE_URL.replace(/\/$/, '')}/widget.js`;
s.setAttribute('data-api-key', API_KEY);
s.setAttribute('data-require-identify', 'true');
s.defer = true;
document.body.appendChild(s);
}
let tries = 50;
const tick = () => {
if (window.Replai?.identify) {
if (user) {
window.Replai.identify({
id: user._id,
name: user.email.split('@')[0],
email: user.email,
});
} else {
window.Replai.hide();
}
return;
}
if (tries-- > 0) setTimeout(tick, 100);
};
tick();
}, [user]);
return null;
}Create a Ticket
https://your-replai-domain.com/api/public/v1/ticketsCreates a new support ticket in the Replai system on behalf of a customer. If the provided customer record does not exist, Replai automatically creates the customer and associates it with this ticket.
Authentication
This endpoint requires authentication using a Bearer token. Include your API key in the Authorization header of your request.
Keep your API keys secure. Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth.
Request Body
The request body should be a JSON object containing the following properties.
| Parameter | Type | Status | Description |
|---|---|---|---|
external_customer_id | string | Required | ID of this user in your system. |
customer_email | string | Required | The email address of the customer creating the ticket. |
customer_name | string | Required | The display name of the customer. |
subject | string | Required | The title or brief summary of the support request. |
description | string | Required | The initial message or body of the ticket. |
priority | string | Optional | The urgency level of the ticket. Valid values are low, medium, high, or critical. Defaults to medium. |
attachments | array | Optional | Up to 5 files attached to the opening message. Each item is { url, name, size, contentType }. Upload files to your storage (e.g. Cloudinary) first, then pass the resulting URLs here. |
source_metadata | object | Optional | A set of key-value pairs that can store useful context about the object in a structured format. |
Example Request
Send the request from any backend — pick your language below.
POST /tickets
curl -X POST https://your-replai-domain.com/api/public/v1/tickets \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"external_customer_id": "cus_9f8e7d6c",
"customer_email": "jane.doe@example.com",
"customer_name": "Jane Doe",
"subject": "Unable to access dashboard",
"description": "I keep getting a 403 error when trying to view my analytics.",
"priority": "high",
"source_metadata": {
"plan": "enterprise",
"page": "analytics"
}
}'Returns
Returns a ticket object if the creation succeeds. Returns an error object if the request is invalid, missing required fields, or authentication fails.
Example Responses
{
"data": {
"id": "tkt_1a2b3c4d5e",
"ticket_number": "REP-1042",
"status": "open",
"created_at": "2026-05-21T15:00:00.000Z"
}
}Need help integrating?
Our developer success team is available to help you set up your API integration and optimize your workflows.
Contact Support