@rehers/rehers-roleplay-sdk
v3.1.1
Published
Seamless Roleplay SDK — embed roleplay call sessions via a modal + iframe
Maintainers
Readme
Roleplay SDK for Seamless.AI
Install
npm install @rehers/rehers-roleplay-sdkFor a working end-to-end example in this repo, use sdk-demo/.
1. Wrap your app with the Provider
Add this once, above all your routes. It initializes the SDK for the logged-in Seamless user.
Production flow:
- Your backend requests a short-lived
userTokenfromPOST /api/seamless/auth/user-token - Your frontend provides a
getUserToken()callback that calls your backend route - The SDK calls
getUserToken()on startup and before the iframe session expires
The browser should not mint sessions from raw identity fields in production.
import { SeamlessRoleplayProvider } from "@rehers/rehers-roleplay-sdk/react";
function App() {
async function getUserToken() {
const tokenRes = await fetch("/api/roleplay/user-token", {
method: "POST",
credentials: "include",
}).then((r) => r.json());
return tokenRes.userToken;
}
return (
<SeamlessRoleplayProvider
getUserToken={getUserToken}
onReady={() => console.log("Roleplay SDK ready")}
onError={(err) => console.error("Roleplay SDK error", err)}
>
<Routes>
<Route path="/roleplay" element={<RoleplayPage />} />
<Route path="/contacts" element={<ContactSearch />} />
{/* ...your other routes */}
</Routes>
</SeamlessRoleplayProvider>
);
}If you need a backend shape, the secure flow looks like this:
const tokenRes = await fetch("/api/roleplay/user-token", {
method: "POST",
credentials: "include",
}).then((r) => r.json());
const userToken = tokenRes.userToken;The SDK owns session refresh timing and will call getUserToken() again before
the embedded app session expires. That's the only setup. Everything below just
works.
2. Full Roleplay page
For the dedicated Roleplay tab/page, drop in RoleplayEmbed. It fills its container.
import { RoleplayEmbed } from "@rehers/rehers-roleplay-sdk/react";
function RoleplayPage() {
return (
<RoleplayEmbed style={{ width: "100%", height: "100%" }} />
);
}Make sure the parent has a height (height: 100%, height: 100vh, flex: 1, etc).
Mounts automatically. Cleans up automatically when the user navigates away.
3. Roleplay dialog on Contact Search
When a user clicks "Roleplay" on a contact row, a dialog opens over the page.
import { useState } from "react";
import { RoleplayDialog } from "@rehers/rehers-roleplay-sdk/react";
function ContactSearch() {
const [activeContact, setActiveContact] = useState(null);
return (
<>
{/* Your existing contact table — just add an onClick to each row's button */}
<table>
{contacts.map((contact) => (
<tr key={contact.id}>
<td>{contact.name}</td>
<td>{contact.company}</td>
<td>{contact.title}</td>
<td>
<button onClick={() => setActiveContact(contact)}>
Roleplay
</button>
</td>
</tr>
))}
</table>
{/* This renders nothing visible — the SDK creates the overlay */}
<RoleplayDialog
open={activeContact !== null}
name={activeContact?.name ?? ""}
domain={activeContact?.domain ?? ""}
company={activeContact?.company ?? ""}
title={activeContact?.title ?? ""}
liUrl={activeContact?.linkedinUrl}
companyDescription={activeContact?.companyDescription}
onClose={() => setActiveContact(null)}
onError={(err) => console.error("Error", err)}
/>
</>
);
}How it works: Set activeContact to a contact object to open the dialog. Set it back to null to close. The onClose callback fires when the user closes it themselves.
4. Add to Scenario (bulk)
When users select multiple contacts and click "Add to Scenario", a scenario picker dialog opens.
import { useState } from "react";
import { AddToScenarioDialog } from "@rehers/rehers-roleplay-sdk/react";
function ContactSearch() {
const [selectedContacts, setSelectedContacts] = useState([]);
const [showScenario, setShowScenario] = useState(false);
return (
<>
{/* Your existing table with checkboxes that populate selectedContacts */}
<button
disabled={selectedContacts.length === 0}
onClick={() => setShowScenario(true)}
>
Add {selectedContacts.length} to Scenario
</button>
<AddToScenarioDialog
open={showScenario}
contacts={selectedContacts.map((c) => ({
name: c.name,
company: c.company,
title: c.title,
domain: c.domain,
liUrl: c.linkedinUrl,
}))}
onComplete={(data) => {
console.log(`Added ${data.addedCount} to ${data.scenarioName}`);
setShowScenario(false);
setSelectedContacts([]);
}}
onClose={() => setShowScenario(false)}
onError={(err) => console.error("Error", err)}
/>
</>
);
}Accepts 1 to 25 contacts. Each contact needs name, company, title, and domain.
Contact field mapping
When passing contact data to RoleplayDialog or AddToScenarioDialog:
| SDK prop | Your contact field |
|---|---|
| name | Full name |
| domain | Company domain (e.g. "stripe.com") |
| company | Company name |
| title | Job title |
| liUrl | LinkedIn URL (optional) |
| companyDescription | Company description (optional) |
That's it
- Provider wraps your app once — handles auth, sessions, cleanup
- RoleplayEmbed is your full Roleplay page — one component
- RoleplayDialog opens per-contact from Contact Search — controlled by a boolean
- AddToScenarioDialog opens for bulk import — controlled by a boolean
All three clean up after themselves. No manual destroy(), no useEffect, no refs.
TypeScript types are included — your editor will autocomplete all props.
Trial and upgrade handling
If a user doesn't have a Roleplay subscription, the SDK shows the upgrade/trial UI automatically inside the iframe. No client-side logic needed.
Advanced: Vanilla JavaScript
If you need the vanilla SDK without React:
import "@rehers/rehers-roleplay-sdk";
// Initialize once
SeamlessRoleplay.init({
getUserToken: async () => {
const res = await fetch("/api/roleplay/user-token", {
method: "POST",
credentials: "include",
});
const data = await res.json();
return data.userToken;
},
onReady() { console.log("ready"); },
});
// Full page embed
SeamlessRoleplay.mount(document.getElementById("container"));
// Contact dialog
SeamlessRoleplay.open({ name: "...", domain: "...", company: "...", title: "..." });
// Bulk import
SeamlessRoleplay.addToScenario({ contacts: [...], onComplete(data) { } });
// Cleanup
SeamlessRoleplay.close(); // close dialog
SeamlessRoleplay.unmount(); // remove embed
SeamlessRoleplay.destroy(); // full cleanup