Websites

One script tag puts the copilot on any website — plain HTML, React, Vue, Angular, Next.js or a CMS theme.

The web SDK is one JavaScript file with no dependencies. It does not care what your site is built with, and it speaks the same protocol as the Flutter SDK: the same components, the same actions from the console, the same persona. One app in the console can serve your mobile app and your website with the same key.

1. Add the tag

Create an app in the console and copy its test key: Settings → API keys, Copy beside Test. Then:

index.html
<!-- Inside <head>, on every page. Swap in the live key when the site goes live.
     data-app-key: your app's key — Rendel console → Settings → API keys → Copy.
     data-languages: "tr" Turkish only, "tr,en" Turkish or English,
     "en" English only. Leave it out to answer in whatever the visitor writes. -->
<script
  src="https://rendel.ai/sdk/web/0.10.0/rendel.js"
  data-app-key="rd_pk_test_…"
  data-languages="tr"
></script>

That is the whole installation. A button appears in the corner and opens the copilot. Where the tag goes depends on the site:

Your siteFile
Plain HTMLevery page, inside <head>
React (Vite), Vue, Svelteindex.html, inside <head>
Angularsrc/index.html, inside <head>
Next.jsapp/layout.tsx, as a <Script> (below)
WordPressyour theme's header.php, before </head>

In Next.js a bare <script> is not the idiom; the root layout loads it:

app/layout.tsx
import Script from "next/script";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="tr">
      <body>
        {children}
        {/* data-app-key: your app's key — Rendel console → Settings → API keys → Copy. */}
        <Script
          src="https://rendel.ai/sdk/web/0.10.0/rendel.js"
          data-app-key="rd_pk_test_…"
          data-languages="tr"
          strategy="beforeInteractive"
        />
      </body>
    </html>
  );
}

Pin the version in the address. 0.10.0 stays 0.10.0 after the next release ships, so your site changes only when you change it.

2. Tell it who is signed in

In your own sign-in code:

your sign-in code
// Right after your sign-in succeeds:
Rendel.identify({ userId: user.id });

// On every page: the signed-in person's current token, or null, so the
// copilot can reach your API as them — no API key.
Rendel.setUserToken(() => getAccessToken());

// When the user signs out:
Rendel.reset();

setUserToken is how the copilot answers from your own API — "my order", "my routine" — as the person asking, with no API key to issue: in the console, set those endpoints to the signed-in user's token. The function is asked before every question, so return the current token (refresh it there if it has run out), or null when nobody is signed in. It goes on to your endpoints as Authorization: Bearer, is never stored, and each person reaches only what their own token opens. Call it on every page, since a function cannot be remembered across loads.

An endpoint that changes something — "book me in on Tuesday" — is asked on a confirmation card first. Once the person confirms, Rendel makes the call as them and answers with what happened; there is no handler to write on the page.

userId is the id your own backend knows the person by — the same one your mobile app passes, so a person has one history across both. The SDK remembers them across page loads and visits, so a returning visitor needs no second call. Pass traits ({ plan: "pro" }) for anything the copilot should know about them, and userHmac when identity verification is on.

Without these, every visitor is anonymous and their conversations belong to the browser. That is the right default for a site with no sign-in.

The conversation follows the visitor from page to page within the tab: a link to another page of your site does not start it over.

3. Allow your domain

The test key works on localhost and 127.0.0.1, on any port, straight away. Before the site goes live, add its domain in the console under Settings → Keys → Allowed domains. A key in a web page can be read by anyone who opens its source; the list is what stops it working on their site. Until the domain is listed, the live key answers that site with origin_not_allowed.

musteri.com means https://musteri.com. www.musteri.com is a separate entry, and *.musteri.com covers every subdomain. Your mobile apps send no domain and are not affected by the list.

Permissions

Nothing to add to any file. The "+" offers Photos and Files, which open the browser's own file picker (pictures; PDFs and text files), and Camera, which the browser asks the visitor for the first time it is used. The microphone uses the browser's speech recognition — the browser asks the visitor for the microphone the first time it is pressed. It opens voice mode: what the visitor says is written in the middle of the panel as they say it, and sent on its own when they stop.

  • The microphone needs https (localhost counts). Firefox has no speech recognition, so it is not shown there; everything else works.
  • Inside an <iframe>, the frame needs allow="microphone; camera".
  • If your site sends a Content-Security-Policy, allow the SDK and the API:
Content-Security-Policy
script-src https://rendel.ai;
connect-src https://api.rendel.ai;
img-src https:;

Actions

Actions you connect in the console run on our servers and work on the website with no code. An action that should run in the page, as the signed-in visitor, is registered the way the Flutter SDK registers one:

app.js
Rendel.registerAction({
  name: "get_order_status",
  description: "Fetch the delivery status of an order",
  params: {
    type: "object",
    properties: { order_id: { type: "string" } },
    required: ["order_id"],
  },
  risk: "read",
  handler: async ({ order_id }) => {
    const response = await fetch(`/api/orders/${order_id}`);
    return response.json();
  },
});

params is JSON Schema. Return what the copilot should know, or throw to say it failed; keep the result under 16 KB. write and destructive actions get the same server-built confirmation card as on a phone, and confirmTemplate words it.

Options

The data attributes cover the common case. For the rest, leave data-app-key off the tag and call init yourself:

app.js
Rendel.init({
  appKey: "rd_pk_test_…",
  languages: ["tr"],
  assistantName: "Pati",
  suggestions: ["Milo bugün nasıl?", "Aşı takvimini göster"],
  launcher: false, // your own button: Rendel.open()
});
Option
languages, localeWhat it answers in; locale is your site's language picker.
assistantName, launcherLabel, suggestions, emptyStateTitle, emptyStateBody, composerHintThe words on screen.
themesurface, onSurface, accent, onAccent and the rest of the console's appearance roles.
launcherfalse hides the corner button. Open it with Rendel.open(), close() or toggle().
actionsThe same as registerAction, all at once.
onLogReceives the SDK's warnings instead of the browser console.

Appearance and the opening surface published from the console override these, on the website and the app alike, without a release.

Rendel.on("opened" | "closed" | "messageSent" | "actionExecuted", fn) subscribes to what happens; Rendel.deviceId is the anonymous id to quote in a support ticket.

TypeScript

Types are published beside the file. Save rendel.d.ts into your sources and window.Rendel is typed.