Alchemist: Embedded Agent — Inline Panel
Embed a native chat panel as part of your application interface.
The inline panel is a full-height, native chat interface that lives in your application alongside other content. Unlike the floating widget, it is part of your page layout and does not float over content.
When to use the inline panel
Use the inline panel when:
- You want the agent as a main navigation item (e.g., a “Chat” or “Assistant” tab)
- The agent is a core feature of your application, not supplementary
- You want the agent interface to span the full height of its container
- You need fine-grained control over where the panel appears (sidebar, main column, modal, etc.)
For a persistent button that floats over your entire site, use the floating widget instead.
Basic setup
1. Install the feature pack
Follow the installation guide first. Once installed, your project has a ChatPanel component ready to import.
2. Import the component
In your page or layout file:
import { ChatPanel } from '@/components/embedded-agent/ChatPanel';3. Render the panel
Place the component in your layout:
<div className="flex h-screen">
<nav className="w-48 border-r">
{/* Sidebar navigation */}
</nav>
<main className="flex-1 flex flex-col">
{/* Your main content */}
<ChatPanel
projectId="proj_..."
sessionEndpoint="/api/chat/session"
/>
</main>
</div>The panel fills its container. Wrap it in a flex container with flex-1 to make it responsive.
Component props
interface ChatPanelProps {
// Required
projectId: string; // Your Alchemist project ID
sessionEndpoint: string; // Your backend session endpoint (e.g., /api/chat/session)
// Optional: Identity
externalUserId?: string; // Authenticated user ID (email for email-based auth)
metadata?: Record<string, any>; // Additional user context (e.g., { "plan": "pro" })
// Optional: Appearance
theme?: 'light' | 'dark'; // Default: inherits from page
placeholder?: string; // Chat input placeholder
welcomeMessage?: string; // Initial message before first user input
// Optional: Behavior
maxMessages?: number; // Limit conversation length (default: unlimited)
autoScroll?: boolean; // Auto-scroll to latest message (default: true)
allowFileUpload?: boolean; // Allow users to upload files (default: true)
// Optional: Callbacks
onSessionStart?: (sessionId: string) => void; // Fired when session is created
onMessageSent?: (message: string) => void; // Fired after user message
onError?: (error: EmbedError) => void; // Fired on chat errors
}Authentication & identity
Authenticated users (email-based)
If your application has logged-in users, pass their identity to the panel:
const user = useAuthContext(); // Your app's auth hook
<ChatPanel
projectId="proj_..."
sessionEndpoint="/api/chat/session"
externalUserId={user.email} // ← User's email
metadata={{ plan: user.plan, account_id: user.id }}
/>Your backend endpoint receives the externalUserId and metadata, mints a Chipp session, and returns a token. The user’s conversations are retained across browser sessions.
Anonymous/visitor mode
For unauthenticated visitors, omit the externalUserId:
<ChatPanel
projectId="proj_..."
sessionEndpoint="/api/chat/session"
// externalUserId omitted → anonymous mode
/>The backend mints an anonymous session (stored in a browser cookie). The conversation is retained for the duration of the session (browser tab).
Upgrading from anonymous to authenticated
If a user starts as anonymous and later logs in, call the upgrade endpoint:
const sessionId = await fetch('/api/chat/session/upgrade', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
currentSessionId: previousSessionId,
externalUserId: user.email,
}),
});The conversation is merged into the authenticated session.
Styling & customization
The panel uses your project’s design system by default (no iframe, no style isolation). It inherits colors, fonts, and spacing from your application’s CSS.
Theme override
Force light or dark mode:
<ChatPanel theme="dark" />Custom CSS classes
Wrap the panel in a container and apply custom styles:
<div className="custom-chat-wrapper">
<ChatPanel projectId="..." sessionEndpoint="..." />
</div>.custom-chat-wrapper :global(.embed-panel) {
border-left: 4px solid var(--primary);
}Placeholder & welcome messages
Customize input placeholders and initial prompts:
<ChatPanel
projectId="..."
sessionEndpoint="..."
placeholder="Ask me anything about your account..."
welcomeMessage="👋 Hi! I can help you find information. What would you like to know?"
/>Error handling
Errors are emitted via the onError callback. Common errors:
interface EmbedError {
code: string; // e.g., 'AUTH_FAILED', 'SESSION_EXPIRED', 'NETWORK_ERROR'
message: string; // Human-readable message
retryable: boolean; // Whether the error can be retried
originalError?: Error; // Underlying JS error
}Example handler:
const handleError = (error: EmbedError) => {
if (error.code === 'SESSION_EXPIRED') {
// Prompt user to re-authenticate
window.location.href = '/login';
} else if (error.retryable) {
console.log('Temporary error; will retry automatically.');
} else {
showToast(`Chat unavailable: ${error.message}`);
}
};
<ChatPanel
projectId="..."
sessionEndpoint="..."
onError={handleError}
/>Responsive layout
The panel fills its container. For responsive designs:
// Mobile: Panel as full-height overlay
// Desktop: Panel in a sidebar
import { useMediaQuery } from '@/hooks/use-media-query';
const isMobile = useMediaQuery('(max-width: 768px)');
{isMobile ? (
<Modal open={showChat} onOpenChange={setShowChat}>
<ChatPanel projectId="..." sessionEndpoint="..." />
</Modal>
) : (
<div className="flex">
<aside className="w-96">
<ChatPanel projectId="..." sessionEndpoint="..." />
</aside>
<main className="flex-1">{/* Page content */}</main>
</div>
)}File uploads & attachments
By default, users can attach files to messages (images, PDFs, etc.). Disable this if needed:
<ChatPanel
projectId="..."
sessionEndpoint="..."
allowFileUpload={false}
/>Uploaded files are processed by the bound Chipp agent according to its configuration (vision, file reading, etc.).
Conversation history
The panel automatically loads previous messages from the current session. To reset or start a new conversation, reload the panel or create a fresh session token (by updating the externalUserId prop).
Troubleshooting
”Session endpoint returned 401”
- Verify your backend session endpoint is correctly configured.
- Ensure your project’s client key is valid: check Project Settings → API Keys.
- If using authenticated mode, confirm the
externalUserIdis being passed correctly.
”Chat not loading / shows error spinner indefinitely”
- Check browser console for network errors.
- Verify the bound Chipp agent is online (check Chipp workspace).
- Confirm
projectIdandsessionEndpointare correctly set.
”Messages not persisting across sessions”
- In authenticated mode, ensure
externalUserIdis consistent (same email). - In anonymous mode, sessions are cookie-based; clearing cookies will reset the conversation.
Styling does not match my design system
- The panel inherits from your page’s CSS. Verify your design system CSS is loaded before the panel renders.
- Use custom CSS classes (see “Styling & customization” above) to override specific elements.
Next steps
- Floating widget — Add a persistent chat widget
- Backend client — Integrate agent calls in your server routes