A sidebar app is a panel in the ticket sidebar whose contents come from your backend. Gleap calls your endpoint with the context of the conversation; you return what should be shown.
It's how an agent sees the customer's subscription, their last order, or their account state from your own system, without leaving the ticket — and how they act on it without opening your admin tool.
In your project settings, Sidebar apps → Add new app. Each app needs:
App name — the panel heading your agents see.
API url — your endpoint.
Custom headers — usually an Authorization header, so you can tell it's really Gleap calling.
There's also Exclude messages from payload. Turn it on unless your endpoint genuinely needs the conversation text — it keeps message content out of a request to a third system, and makes the payload smaller.
You can add several apps; each renders as its own panel.
Your endpoint receives the ticket, contact, and message context, then returns an array of content items to show in the sidebar.
When a ticket opens, Gleap makes a POST to your API URL with your custom headers attached. The body carries the context of the conversation so you can look the customer up in your own system:
the contact — name, email, and custom data
the ticket it's attached to
the messages, unless you turned on Exclude messages from payload
{
"ticket": {
"id": "6a7c…",
"status": "OPEN",
"customData": { "plan": "pro" }
},
"contact": {
"name": "Max Smith",
"email": "[email protected]",
"customData": { "accountId": "A-1042" }
},
"messages": [
{ "type": "text", "text": "Can you extend my trial?" }
]
}Authenticate the call with the custom header you set, then key off the contact's email — or a custom ID you passed at identification — to fetch their record.
Respond 200 with a JSON list of content items. The types available:
text — a line of text.
key-value — label-and-value pairs, the workhorse for account data.
image — an image by URL.
button — an action (see below).
form — fields the agent fills in and submits back to you.
html — for anything the other types can't express.
{
"content": [
{ "type": "text", "text": "Pro plan · renews 12 Mar 2026" },
{ "type": "key-value", "label": "MRR", "value": "$49" },
{ "type": "key-value", "label": "Status", "value": "Active" },
{ "type": "image", "url": "https://acme.com/avatar/1042.png" }
]
}Prefer key-value over html where you can. It inherits Gleap's styling, so it keeps looking right when the dashboard changes.
A button either opens a URL or makes an HTTP request back to you — with a method, headers, and body you define, and success and error messages the agent sees. That's the difference between a sidebar that shows a subscription and one that can extend a trial.
{
"type": "button",
"label": "Extend trial 14 days",
"action": {
"type": "http-request",
"url": "https://acme.com/gleap/extend",
"method": "POST",
"headers": { "Authorization": "Bearer …" },
"body": { "accountId": "A-1042", "days": 14 }
},
"successMessage": "Trial extended to 26 Mar.",
"errorMessage": "Couldn't extend — do it from the admin tool."
}For a plain link, use an open-url action with a url instead.
A form takes it further: text, textarea, select, radio, checkbox, number and date fields, submitted to a URL you specify. Use it when the action needs input — a refund amount, a reason code.
{
"type": "form",
"submitUrl": "https://acme.com/gleap/refund",
"fields": [
{ "type": "number", "key": "amount", "label": "Refund amount" },
{ "type": "select", "key": "reason", "label": "Reason",
"options": ["Duplicate", "Faulty", "Goodwill"] }
]
}Keep it fast. The panel renders when the ticket opens, so a slow endpoint is felt on every single conversation — cache on your side rather than querying three systems per request.
Handle "not found" gracefully. Plenty of contacts won't exist in your system, and returning a short "no account" line reads better than an empty panel or an error.
Show what changes the answer — plan, status, last invoice — not everything you have. A sidebar with thirty fields gets scrolled past.