Embed: inbox dentro de tu app
Empotrá el inbox de IxiChat en tu propia UI vía iframe firmado. Tus usuarios ven y responden mensajes sin salir de tu aplicación. Patrón Stripe Connect Embedded — el iframe no expone tu api_key, usa un session token opaco de corta vida que vos minteás desde el backend.
Pre-requisito
Necesitás un api_key del workspace que querés mostrar. Ver flujo OAuth →
Cómo funciona
- Tu backend llama a
POST /api/embed/sessionusando elapi_keydel install. Recibe un session token (TTL 10 min) y una URL ya armada. - Tu frontend pone esa URL en
<iframe src=...>. - El iframe carga
/embed/inboxcon UI nativa (lista de conversaciones, thread, composer, botones para resolver y pausar IA). - Para sesiones de más de 10 min, tu frontend escucha el evento
postMessageixichat:session-expiredy mintea una nueva sesión sin recargar la página.
SDK npm: @ixichat/embed
Para no escribir el wire a mano, publicamos un SDK oficial con helper de backend (Node ≥18) + componente React.
npm install @ixichat/embed
# o pnpm / yarnBackend — mintear sesiones
import { IxichatEmbed } from "@ixichat/embed/server";
const ixc = new IxichatEmbed({
apiKey: process.env.IXICHAT_API_KEY!, // el api_key del install OAuth
});
// En tu handler (Express, Next route, etc.)
const { iframeUrl, expiresAt } = await ixc.createSession({
view: "inbox",
channelId: req.user.assignedChannelId, // opcional
actorEmail: req.user.email,
actorName: req.user.name,
theme: {
primaryColor: "#10B981",
logoUrl: "https://miclinic.com/logo.png",
mode: "light",
},
ttlMinutes: 30, // 1-60, default 10
});
res.json({ iframeUrl, expiresAt });Frontend (React)
import { IxichatInbox } from "@ixichat/embed/react";
export function ClinicaInbox() {
const [url, setUrl] = useState<string | null>(null);
async function fetchSession() {
const r = await fetch("/api/my-erp/ixichat-session", { method: "POST" });
const { iframeUrl } = await r.json();
setUrl(iframeUrl);
}
useEffect(() => { fetchSession(); }, []);
if (!url) return <p>Cargando inbox…</p>;
return (
<div style={{ height: 600 }}>
<IxichatInbox
url={url}
onRefreshNeeded={async () => {
const r = await fetch("/api/my-erp/ixichat-session", { method: "POST" });
return (await r.json()).iframeUrl;
}}
onConversationOpened={(id) => console.log("opened", id)}
onMessageSent={(id) => console.log("sent", id)}
/>
</div>
);
}Endpoint del session mint
/api/embed/sessionAuth: Authorization: Bearer ixc_live_*. Scope: read.
{
"view": "inbox",
"channel_id": "uuid-opcional",
"actor_email": "agente@miapp.com",
"actor_name": "Carlos del equipo",
"theme": {
"primaryColor": "#10B981",
"logoUrl": "https://miapp.com/logo.png",
"mode": "light"
},
"ttl_minutes": 30
}Respuesta:
{
"session_token": "ems_<48-chars>",
"iframe_url": "https://ixichat.com/embed/inbox?session=ems_...",
"expires_at": "2026-05-27T14:30:00Z"
}Vistas disponibles
| view | Qué muestra |
|---|---|
inbox | Lista de conversaciones + thread + composer. Acciones: responder, pausar IA, resolver. |
conversation | Solo la vista detalle de UNA conversación (reservado para Fase 4.E). |
Theme override
Pasá un objeto theme al mintear la sesión para que el iframe use tu identidad visual:
| Campo | Tipo | Descripción |
|---|---|---|
logoUrl | string URL | Logo que aparece en el header del iframe (square, ~32px). |
primaryColor | string hex/css | Color de los bubbles outbound, el botón send y los CTAs. |
mode | "light" | "dark" | Modo claro u oscuro. Default: light. |
postMessage protocol
El iframe envía estos mensajes al parent. El SDK los re-expone como callbacks; si no usás el SDK, podés escuchar directamente:
window.addEventListener("message", (ev) => {
if (ev.origin !== "https://ixichat.com") return;
switch (ev.data?.type) {
case "ixichat:session-expired": // mintear nueva sesión
case "ixichat:conversation-opened": // ev.data.conversationId
case "ixichat:message-sent": // ev.data.conversationId
case "ixichat:resize": // ev.data.height
// ...
}
});Origin matching
ev.origin antes de confiar en el mensaje. El SDK lo hace por defecto. Si no lo verificás, cualquier ventana puede spoofear estos eventos.CSP del iframe
IxiChat permite que /embed/* sea iframeable desde cualquier origen (frame-ancestors *). El resto del sitio mantiene política estricta — la landing y /app rechazan iframes.
Errores
| Status | error | Causa |
|---|---|---|
| 400 | invalid_view | El view no está en la whitelist. |
| 401 | invalid_session | Session token expirado, revocado o nunca existió. |
| 401 | unauthorized | Auth bearer faltante o inválido al mintear. |
| 403 | channel_forbidden | La conv que se está accediendo no pertenece al channel_id de la sesión. |
| 404 | conversation_not_found | La conv no existe en este tenant. |