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, and messages: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, and messages: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.
index.html
<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

HTML
<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)

javascript
// 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

index.html
<!-- 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

jsx
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

tsx
'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

POSThttps://your-replai-domain.com/api/public/v1/tickets

Creates 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.

ParameterTypeStatusDescription
external_customer_idstringRequiredID of this user in your system.
customer_emailstringRequiredThe email address of the customer creating the ticket.
customer_namestringRequiredThe display name of the customer.
subjectstringRequiredThe title or brief summary of the support request.
descriptionstringRequiredThe initial message or body of the ticket.
prioritystringOptionalThe urgency level of the ticket. Valid values are low, medium, high, or critical. Defaults to medium.
attachmentsarrayOptionalUp 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_metadataobjectOptionalA 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

bash
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

json
{
  "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