Cookbook

Messaging Examples

Explore complete payload structures for every supported message kind and action type—copy these examples directly into your extension and adapt them for your use case.

Permission: messaging:send in your manifest.json. The author label above outbound messages is set per-Instance by the admin via the Instance settings field messagingDisplayName; falls back to extension.manifest.name when blank. Extensions do not set or think about the author identity.

kind: 'text' — Plain text + reply actions

Plain conversational message. Optional actions array attaches quick-reply buttons, links, postbacks, or location requests.

const [send] = useMessaging()

await send({
  "kind": "text",
  "body": "Order approved ✓",
  "actions": [
    {
      "type": "reply",
      "label": "Got it",
      "payload": "ACK"
    },
    {
      "type": "reply",
      "label": "Show details",
      "payload": "DETAILS"
    }
  ]
})

kind: 'image' — Embedded image + optional caption

Product photos, screenshots, visual help. url must respond with a Content-Type: image/* header (Sunco enforces; redirecting URLs like picsum.photos will fail). altText is optional — defaults to filename. Optional body renders a caption alongside the image.

const [send] = useMessaging()

await send({
  "kind": "image",
  "url": "https://cdn.example.com/products/widget-blue.jpg",
  "altText": "Blue widget — model A24",
  "body": "Here is the product you asked about:",
  "actions": [
    {
      "type": "link",
      "label": "View on site",
      "url": "https://example.com/p/widget-a24"
    }
  ]
})

kind: 'file' — Embedded file attachment

Return labels, receipts, NDAs, warranty paperwork. Sunco's fileMessage schema does NOT support actions — file messages can only carry an optional text caption.

const [send] = useMessaging()

await send({
  "kind": "file",
  "url": "https://cdn.example.com/receipts/order-12345.pdf",
  "altText": "Receipt for order #12345",
  "body": "Your receipt is attached."
})

The conversational-commerce primitive: product recommendations, size/color pickers, search results with images, multi-option selection.

  • 1–10 items, each with required title + required actions (1–3 per item)
  • Item actions are a subset: link + postback only (no reply, no locationRequest — Sunco rejects those at the item level)
  • Carousel messages have NO message-level actions field — actions live per-card
  • Use a separate text message before the carousel if you need an intro
const [send] = useMessaging()

await send({
  "kind": "carousel",
  "items": [
    {
      "title": "Widget A24 — Blue",
      "description": "In stock — ships in 1-2 days",
      "imageUrl": "https://cdn.example.com/products/widget-blue.jpg",
      "actions": [
        {
          "type": "link",
          "label": "View",
          "url": "https://example.com/p/widget-a24-blue"
        },
        {
          "type": "postback",
          "label": "Add to cart",
          "payload": "add_to_cart:widget-a24-blue"
        }
      ]
    },
    {
      "title": "Widget A24 — Red",
      "description": "Limited stock",
      "imageUrl": "https://cdn.example.com/products/widget-red.jpg",
      "actions": [
        {
          "type": "link",
          "label": "View",
          "url": "https://example.com/p/widget-a24-red"
        },
        {
          "type": "postback",
          "label": "Add to cart",
          "payload": "add_to_cart:widget-a24-red"
        }
      ]
    }
  ],
  "displaySettings": {
    "imageAspectRatio": "horizontal"
  }
})

Action types

Attach to message-level actions (text / image) or item-level actions (carousel). All actions share label + optional metadata (primitives only, ≤4KB total).

type: 'reply'

Inserts a visible user-reply bubble carrying payload back into the conversation, as if the user typed it. Mutually exclusive — a reply action cannot share an actions[] array with any other action type; the host validates and surfaces invalid_message if mixed.

{
  "type": "reply",
  "label": "Got it",
  "payload": "ACK"
}

Opens url in a new tab/window. No bubble inserted. Freely mixable with other non-reply actions.

{
  "type": "link",
  "label": "View on site",
  "url": "https://example.com/p/widget-a24"
}

type: 'postback'

Fires a server-side conversation:postback webhook to the Stackable backend carrying the full payload + any metadata. NO visible bubble. Extensions with the events:messaging permission receive postback events via useMessagingEvent — see the Events & Identity cookbook.

{
  "type": "postback",
  "label": "Add to cart",
  "payload": "add_to_cart:widget-a24"
}

type: 'locationRequest'

Prompts the user to share device location. Response arrives as a separate location-type message inbound to the conversation. Freely mixable.

{
  "type": "locationRequest",
  "label": "Share location"
}

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

Previous
Capability Examples
Messaging Examples | Stackable Labs :. Dev Documentation