Cookbook

Messaging Examples

Explore practical payload structures for each message kind supported by the messaging.send capability, along with the action types you can attach. Copy these examples and adapt them to 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