@rehers/rehers-roleplay-sdk
v2.5.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-sdk1. Wrap your app with the Provider
Add this once, above all your routes. It initializes the SDK for the logged-in Seamless user.
import { SeamlessRoleplayProvider } from "@rehers/rehers-roleplay-sdk/react";
function App() {
// You already have the logged-in user from your auth/session.
// The SDK needs their id and email.
const me = useCurrentUser(); // however you get the logged-in user
return (
<SeamlessRoleplayProvider
publishableKey="pk_live_..."
userId={String(me.id)}
userEmail={me.username}
userRole={me.orgRole}
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 fetch the user via API:
const res = await fetch("https://api.seamless.ai/api/users/me", {
credentials: "include",
}).then((r) => r.json());
const me = res.data ?? res;
// me.id → pass as userId (convert to string)
// me.username → pass as userEmail
// me.orgRole → pass as userRole directlyThat'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({
publishableKey: "pk_live_...",
userId: "...",
userEmail: "...",
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