Widgets
Casey emits lightweight widget hints — the frontend owns rendering and data fetching.
When Casey thinks a visual component would help, it emits a widget SSE event with a hint — just the widget type and a minimal identifier. The frontend is fully responsible for rendering the widget and fetching any data it needs.
Casey does NOT send widget data. It sends a type and an identifier. The frontend renders the component using its own data-fetching hooks.
Widget Hint Format
Every widget event arrives as:
data: {"type": "widget", "widget": {"type": "deal_card", "dealId": 123}}
The widget object always has a type field, plus optional identifiers depending on the widget type.
Widget Types
deal_card
An interactive deal card shown when Casey is discussing a specific deal.
{ "type": "deal_card", "dealId": 123 }
| Field | Type | Description |
|---|---|---|
type | "deal_card" | Widget type |
dealId | number | The deal's database ID |
Frontend: Fetch deal data using the deal ID and render a deal card component.
founder_card
A single rich founder card shown when Casey is discussing a deal's founders. Displays founder name, title, description, and social media link. Casey emits one founder_card widget per founder — a deal with 3 founders produces 3 separate widget events.
{ "type": "founder_card", "dealId": 123, "founderId": 456 }
| Field | Type | Description |
|---|---|---|
type | "founder_card" | Widget type |
dealId | number | The deal's database ID |
founderId | number | The founder's person ID (person.id) |
Frontend: Fetch the specific founder's data using founderId (and optionally dealId for context) and render a single founder card (name, title, bio, social link). Multiple founder_card widgets for the same deal should be rendered side by side.
investment_summary
Shows the user's investment details for a specific investment.
{ "type": "investment_summary", "investmentId": 456 }
| Field | Type | Description |
|---|---|---|
type | "investment_summary" | Widget type |
investmentId | number | The investment's database ID |
Frontend: Fetch investment data and render an investment summary card.
portfolio_overview
Shows the user's overall portfolio summary.
{ "type": "portfolio_overview" }
| Field | Type | Description |
|---|---|---|
type | "portfolio_overview" | Widget type |
Frontend: Fetch portfolio data and render the portfolio overview component. No identifier needed — it's scoped to the current user.
support
Shows a support/help prompt with a contextual message.
{
"type": "support",
"message": "Need help with your payment? Contact our team."
}
| Field | Type | Description |
|---|---|---|
type | "support" | Widget type |
message | string | A short, contextual message to display in the widget |
Frontend: Render a support widget/card with the provided message and a link to contact support.
TypeScript Types
type CaseyWidgetHint =
| { type: "deal_card"; dealId: number }
| { type: "founder_card"; dealId: number; founderId: number }
| { type: "investment_summary"; investmentId: number }
| { type: "portfolio_overview" }
| { type: "support"; message: string };
Rendering Strategy
Widgets should be rendered inline within the chat message where they appear. A simple approach:
function ChatMessage({ message, widgets }: ChatMessageProps) {
return (
<div>
<Markdown>{message.content}</Markdown>
{widgets?.map((hint, i) => (
<WidgetRenderer key={i} hint={hint} />
))}
</div>
);
}
function WidgetRenderer({ hint }: { hint: CaseyWidgetHint }) {
switch (hint.type) {
case 'deal_card':
return <DealCard dealId={hint.dealId} />;
case 'founder_card':
return <FounderCard dealId={hint.dealId} founderId={hint.founderId} />;
case 'investment_summary':
return <InvestmentSummary investmentId={hint.investmentId} />;
case 'portfolio_overview':
return <PortfolioOverview />;
case 'support':
return <SupportCard message={hint.message} />;
default:
return null; // Gracefully ignore unknown types
}
}
Always handle unknown widget types with a fallback. Casey may add new widget types in the future, and the frontend should degrade gracefully.
During Streaming vs. History
- During streaming: Widget hints arrive as real-time
widgetSSE events. Render them as they arrive. - From conversation history: Widget hints are stored in the
widgetsarray on assistant messages (see Conversations). Render them alongside the message content.
Last updated Mar 26, 2026
Built with Documentation.AI