Cookbook
Capability Examples
Focused examples showing each SDK capability in a working Surface component. Each example is self-contained — copy it into a surface file and add the corresponding permission to your manifest.json.
context.read — Reading Platform Context
Read customer and session data provided by the framework. The useContextData hook handles loading state automatically.
Permission: context:read
import { useContextData, Surface, ui } from '@stackable-labs/sdk-extension-react'
export function Header(): React.ReactElement {
const { loading, customerId, customerEmail } = useContextData()
if (loading) {
return (
<Surface id="slot.header">
<ui.Text className="text-xs text-muted-foreground">Loading...</ui.Text>
</Surface>
)
}
return (
<Surface id="slot.header">
<ui.Stack direction="column" gap="1">
<ui.Text className="text-xs font-semibold">{customerEmail}</ui.Text>
<ui.Text className="text-xs text-muted-foreground">ID: {customerId}</ui.Text>
</ui.Stack>
</Surface>
)
}
data.query — Platform-Mediated Requests
Send structured requests to the platform. The framework handles the API call and returns the result — no allowedDomains needed.
Permission: data:query
import { useCapabilities, Surface, ui } from '@stackable-labs/sdk-extension-react'
import { useState } from 'react'
import type { ApiRequest } from '@stackable-labs/sdk-extension-contracts'
export function Content(): React.ReactElement {
const capabilities = useCapabilities()
const [data, setData] = useState<unknown>(null)
const handleQuery = async () => {
const payload: ApiRequest = {
action: 'getCustomer',
customerId: '123',
}
const result = await capabilities.data.query<{ name: string }>(payload)
setData(result)
}
return (
<Surface id="slot.content">
<ui.Button onClick={handleQuery}>Fetch Data</ui.Button>
</Surface>
)
}
data.fetch — Direct HTTP Requests
Make HTTP requests directly from the extension sandbox. The domain must be listed in allowedDomains in your manifest — exact hostnames or *.<suffix> wildcards. See External APIs > Wildcards for the full rules.
Permission: data:fetch
import { useCapabilities, Surface, ui } from '@stackable-labs/sdk-extension-react'
import { useState } from 'react'
import type { FetchResponse } from '@stackable-labs/sdk-extension-contracts'
export function Content(): React.ReactElement {
const capabilities = useCapabilities()
const [data, setData] = useState<unknown>(null)
const handleGet = async () => {
const result: FetchResponse = await capabilities.data.fetch('https://api.myservice.com/orders')
if (result.ok) setData(result.data)
}
const handlePost = async () => {
const result: FetchResponse = await capabilities.data.fetch(
'https://api.myservice.com/orders',
{ method: 'POST', body: { limit: 10 } }
)
if (result.ok) setData(result.data)
}
return (
<Surface id="slot.content">
<ui.Stack gap="2">
<ui.Button onClick={handleGet}>GET Orders</ui.Button>
<ui.Button onClick={handlePost}>POST Orders</ui.Button>
</ui.Stack>
</Surface>
)
}
actions.toast — Toast Notifications
Display toast notifications in the host widget's UI to provide feedback to users.
Permission: actions:toast
import { useCapabilities, Surface, ui } from '@stackable-labs/sdk-extension-react'
export function Content(): React.ReactElement {
const capabilities = useCapabilities()
const showToast = async () => {
await capabilities.actions.toast({ type: 'success', message: 'Done!' })
}
return (
<Surface id="slot.content">
<ui.Button onClick={showToast}>Show Toast</ui.Button>
</Surface>
)
}
actions.invoke — Host Actions
Trigger host-defined actions like starting conversations, setting tags, or updating custom fields.
Permission: actions:invoke
import { useCapabilities, Surface, ui } from '@stackable-labs/sdk-extension-react'
export function Content(): React.ReactElement {
const capabilities = useCapabilities()
const newConversation = async () => {
await capabilities.actions.invoke('newConversation', {
tags: ['stackable', 'order-lookup'],
fields: [{ id: 'stackable_action', value: 'order_status' }],
metadata: { orderId: '12345' },
})
}
const setTags = async () => {
await capabilities.actions.invoke('setConversationTags', ['escalated', 'order-issue'])
}
return (
<Surface id="slot.content">
<ui.Stack gap="2">
<ui.Button onClick={newConversation}>New Conversation</ui.Button>
<ui.Button onClick={setTags}>Set Tags</ui.Button>
</ui.Stack>
</Surface>
)
}