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>
  )
}

Auto-generated from Stackable Extension SDK. Questions/Issues? developers@stackablelabs.com

Previous
Structural Patterns
Capability Examples | Stackable Labs :. Dev Documentation