Help Desk

Custom Sidebar Widgets

Pull data from your own systems into the conversation sidebar. Sidebar widgets give agents instant context — account lookups, warranty checks, CRM records — without leaving Octocom.

Sidebar widgets let you display custom data from your own systems directly in the conversation sidebar. Instead of switching to another tool to look up a customer's account, check a warranty, or view a CRM record, your agents see it right next to the conversation.

Each widget is a Python script that fetches data and returns a structured result. The system renders it automatically in the sidebar.


How it works

  1. You create a sidebar widget — give it a name and write a Python script
  2. When an agent opens a conversation, all active widgets run automatically
  3. Each widget receives conversation and customer context, calls your APIs, and returns structured data
  4. The sidebar renders each widget's result as a collapsible card with sections and fields

You can create multiple widgets. They all run independently and display in the sidebar together.


Writing a sidebar widget

A sidebar widget implements a get_sidebar_data function. It receives the same context object as other Python features and returns structured data for the sidebar.

import requests

def get_sidebar_data(context):
    customer = context["customer"]

    if not customer or not customer.get("email"):
        return {"title": "CRM", "items": []}

    response = requests.get(
        "https://api.example.com/customers",
        params={"email": customer["email"]},
        headers={"Authorization": "Bearer API_KEY"},
        timeout=30,
    )

    if response.status_code != 200:
        return {"title": "CRM", "items": []}

    account = response.json()

    return {
        "title": "CRM",
        "items": [
            {
                "title": account["name"],
                "subtitle": account["email"],
                "badge": {
                    "text": account["tier"],
                    "color": "green" if account["tier"] == "Premium" else "gray",
                },
                "sections": [
                    {
                        "title": "Account details",
                        "fields": {
                            "Account ID": account["id"],
                            "Tier": account["tier"],
                            "Lifetime value": f"${account['ltv']}",
                            "Member since": account["createdAt"],
                        },
                    },
                ],
            },
        ],
    }

Return format

The function must return a dictionary with this structure:

{
    "title": "Widget title",  # Shown as the widget header in the sidebar
    "items": [...]             # List of items to display
}

Items

Each item is a card in the sidebar:

FieldTypeRequiredDescription
titlestrYesItem title
subtitlestrNoText shown below the title
linkstrNoURL — makes the title clickable
badgedictNoStatus badge next to the title (see below)
actionslistNoAction buttons (see below)
collapsibleboolNoWhether the item can be collapsed (default: True)
sectionslistYesList of sections with fields to display

Badge

A colored label displayed next to the item title:

FieldTypeRequiredDescription
textstrYesBadge text
colorstrNo"blue", "green", "red", "yellow", or "gray" (default)

Actions

Buttons displayed on the item:

FieldTypeRequiredDescription
labelstrYesButton label
linkstrNoURL the button opens

Sections

Each section is a labeled group of key-value fields:

FieldTypeRequiredDescription
titlestrYesSection heading
collapsibleboolNoWhether the section can be collapsed (default: False)
fieldsdictYesKey-value pairs to display

Context structure

The context object contains conversation data, customer profile, and business info. Sidebar widgets do not receive context["args"] — they rely on conversation and customer data to look up information. See Python Context for the full reference.


Examples

Wholesale account lookup

Show wholesale pricing tier and credit limit for B2B customers.

import requests

def get_sidebar_data(context):
    customer = context["customer"]

    if not customer or not customer.get("email"):
        return {"title": "Wholesale", "items": []}

    response = requests.get(
        "https://api.example.com/wholesale/accounts",
        params={"email": customer["email"]},
        headers={"Authorization": "Bearer API_KEY"},
        timeout=30,
    )

    if response.status_code != 200:
        return {"title": "Wholesale", "items": []}

    account = response.json()

    return {
        "title": "Wholesale",
        "items": [
            {
                "title": account["companyName"],
                "badge": {
                    "text": account["status"],
                    "color": "green" if account["status"] == "Active" else "red",
                },
                "actions": [
                    {"label": "View in ERP", "link": f"https://erp.example.com/accounts/{account['id']}"},
                ],
                "sections": [
                    {
                        "title": "Account",
                        "fields": {
                            "Pricing tier": account["pricingTier"],
                            "Credit limit": f"${account['creditLimit']}",
                            "Outstanding balance": f"${account['balance']}",
                            "Payment terms": account["paymentTerms"],
                        },
                    },
                    {
                        "title": "Contact",
                        "fields": {
                            "Account manager": account.get("accountManager", "—"),
                            "Phone": account.get("phone", "—"),
                        },
                    },
                ],
            },
        ],
    }

Warranty status check

Display warranty information for the customer's recent purchases.

import requests

def get_sidebar_data(context):
    customer = context["customer"]

    if not customer or not customer.get("email"):
        return {"title": "Warranties", "items": []}

    response = requests.get(
        "https://api.example.com/warranties",
        params={"email": customer["email"]},
        headers={"Authorization": "Bearer API_KEY"},
        timeout=30,
    )

    if response.status_code != 200:
        return {"title": "Warranties", "items": []}

    warranties = response.json()

    items = []
    for w in warranties[:5]:
        items.append({
            "title": w["productName"],
            "subtitle": f"Purchased {w['purchaseDate']}",
            "badge": {
                "text": "Active" if w["isActive"] else "Expired",
                "color": "green" if w["isActive"] else "gray",
            },
            "sections": [
                {
                    "title": "Warranty details",
                    "fields": {
                        "Type": w["warrantyType"],
                        "Expires": w["expiresAt"],
                        "Serial number": w.get("serialNumber", "—"),
                    },
                },
            ],
        })

    return {"title": "Warranties", "items": items}

Multiple items with sections

A single widget can return multiple items, each with multiple sections. For example, showing a customer's active subscriptions:

import requests

def get_sidebar_data(context):
    customer = context["customer"]

    if not customer or not customer.get("email"):
        return {"title": "Subscriptions", "items": []}

    response = requests.get(
        "https://api.example.com/subscriptions",
        params={"email": customer["email"]},
        timeout=30,
    )

    if response.status_code != 200:
        return {"title": "Subscriptions", "items": []}

    subs = response.json()

    items = []
    for sub in subs:
        items.append({
            "title": sub["planName"],
            "badge": {
                "text": sub["status"].capitalize(),
                "color": "green" if sub["status"] == "active" else "yellow",
            },
            "sections": [
                {
                    "title": "Plan",
                    "fields": {
                        "Price": f"${sub['price']}/mo",
                        "Next billing": sub["nextBillingDate"],
                        "Started": sub["startDate"],
                    },
                },
                {
                    "title": "Usage",
                    "collapsible": True,
                    "fields": {
                        "API calls": f"{sub['usage']['apiCalls']} / {sub['usage']['limit']}",
                        "Storage": sub['usage']['storage'],
                    },
                },
            ],
        })

    return {"title": "Subscriptions", "items": items}

Available helper functions

Sidebar widgets have access to all the same built-in helper functions as custom actions, condition providers, and event handlers.

See Python Helpers for the full list with documentation links.


Testing

Sidebar widgets can be tested from the dashboard before they go live.

  1. Open your sidebar widget in the dashboard
  2. Select a Business to test against
  3. Enter a Conversation ID — this loads real conversation and customer data as context. Use any conversation ID or public ID from your dashboard
  4. Click Run Test
  5. The dashboard executes your code and shows the structured result

Tip: Test with conversations from different customer types — known vs. unknown customers, different channels — to make sure your widget handles all cases gracefully.


Best practices

  • Handle missing data. Always check if context["customer"] is None and if the customer has the fields you need (email, phone, etc.). Return an empty items list when there's nothing to show.
  • Return empty, don't crash. If your API is down or returns an error, return {"title": "...", "items": []} rather than raising an exception.
  • Keep it fast. Widgets run every time an agent opens a conversation. Avoid slow API calls or heavy processing. Set timeout=30 on requests.
  • Use badges for status. Color-coded badges give agents an instant visual signal — green for active, red for problems, gray for neutral.
  • Limit items. If your API returns many results, slice to the most relevant (e.g., [:5]). Too many items clutter the sidebar.
  • Title clearly. The widget title should describe what system the data comes from — "CRM", "Wholesale", "Warranties" — not what the data contains.

On this page