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
- You create a sidebar widget — give it a name and write a Python script
- When an agent opens a conversation, all active widgets run automatically
- Each widget receives conversation and customer context, calls your APIs, and returns structured data
- 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:
| Field | Type | Required | Description |
|---|---|---|---|
title | str | Yes | Item title |
subtitle | str | No | Text shown below the title |
link | str | No | URL — makes the title clickable |
badge | dict | No | Status badge next to the title (see below) |
actions | list | No | Action buttons (see below) |
collapsible | bool | No | Whether the item can be collapsed (default: True) |
sections | list | Yes | List of sections with fields to display |
Badge
A colored label displayed next to the item title:
| Field | Type | Required | Description |
|---|---|---|---|
text | str | Yes | Badge text |
color | str | No | "blue", "green", "red", "yellow", or "gray" (default) |
Actions
Buttons displayed on the item:
| Field | Type | Required | Description |
|---|---|---|---|
label | str | Yes | Button label |
link | str | No | URL the button opens |
Sections
Each section is a labeled group of key-value fields:
| Field | Type | Required | Description |
|---|---|---|---|
title | str | Yes | Section heading |
collapsible | bool | No | Whether the section can be collapsed (default: False) |
fields | dict | Yes | Key-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.
- Open your sidebar widget in the dashboard
- Select a Business to test against
- Enter a Conversation ID — this loads real conversation and customer data as context. Use any conversation ID or public ID from your dashboard
- Click Run Test
- 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"]isNoneand 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=30on 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.