> For the complete documentation index, see [llms.txt](https://docs.gallabox.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.gallabox.com/integration/other-integrations/conversation-panels.md).

# Conversation Panels

Conversation panels are integration sidebars that appear on the right side of the conversation screen. When an agent opens a conversation, the panel fetches live data from your external system using the contact's context — no tab switching needed.

### How It Works

When a user opens a conversation and clicks a panel icon, Gallabox calls your external endpoint with the conversation's context (contact ID, phone number, etc.). Your endpoint returns structured data, which the panel renders as a read-only form.

```
User opens conversation
        │
        ▼
Panel icon appears in conversation sidebar
        │
User clicks panel icon
        ▼
Gallabox calls YOUR endpoint with contact context
        │
        ▼
Your endpoint returns JSON
        │
        ▼
Panel renders the data
```

***

### Step 1 — Build Your Render Endpoint

This is the only backend code you write. Gallabox sends a `POST` request to your endpoint every time a user opens the panel.

**Request body Gallabox sends to your endpoint:**

```json
{
  "contactID": "64abc123",
  "phoneNumber": "+919876543210",
  "conversationId": "64xyz789",
  "assigneeId": "64agent01",
  "status": "open",
  "credential": { }
}
```

The `credential` object contains whatever auth data (API key, OAuth token, etc.) you configured during setup.

**Your endpoint must return a flat JSON object:**

```json
{
  "Full Name": "Priya Sharma",
  "Email": "priya@example.com",
  "Plan": "Pro",
  "MRR": { "code": "INR", "value": 4999 },
  "Tags": ["VIP", "Early Adopter"],
  "Renewal Date": "2026-08-01",
  "Active": true
}
```

The field names in your response **must exactly match** the property keys defined in your schema (Step 2).

{% hint style="info" %}
Keep your endpoint fast — it is called every time a user opens the panel. Aim for a response time under 2 seconds.
{% endhint %}

**Example — Node.js Express:**

```typescript
router.post("/panel/render", async (req, res) => {
  const { phoneNumber, credential } = req.body;

  const contact = await mycrm.findByPhone(phoneNumber, {
    apiKey: credential.apiKey,
  });

  if (!contact) {
    return res.json({});  // Empty object shows a blank state in the panel
  }

  return res.json({
    "Full Name": contact.name,
    "Email": contact.email,
    "Plan": contact.subscription?.plan ?? "Free",
    "MRR": { code: "INR", value: contact.mrr ?? 0 },
    "Tags": contact.tags ?? [],
    "Renewal Date": contact.renewalDate,
    "Active": contact.isActive,
  });
});
```

***

### Step 2 — Define the Schema

The schema tells the panel which fields to display and how to render them. It follows [JSON Schema](https://json-schema.org/) with field type hints.

#### Supported Field Types

| Field Type    | Schema definition                                                                                      | Renders as     |
| ------------- | ------------------------------------------------------------------------------------------------------ | -------------- |
| Text          | `{ "type": "string" }`                                                                                 | Plain text     |
| Number        | `{ "type": "number" }`                                                                                 | Number         |
| Date          | `{ "type": "string", "format": "date" }`                                                               | Formatted date |
| URL / Link    | `{ "type": "string", "format": "uri" }`                                                                | Clickable link |
| Phone         | `{ "type": "string", "pattern": "^[+]?[0-9]{1,15}$" }`                                                 | Phone number   |
| Boolean       | `{ "type": "boolean" }`                                                                                | Yes / No badge |
| Currency      | `{ "type": "object", "properties": { "code": { "type": "string" }, "value": { "type": "number" } } }`  | ₹4,999         |
| Label / Badge | `{ "type": "object", "properties": { "color": { "type": "string" }, "label": { "type": "string" } } }` | Colored badge  |
| Tags          | `{ "type": "array", "items": { "type": "string" } }`                                                   | Tag chips      |

#### Example Schema

```json
{
  "type": "object",
  "properties": {
    "Full Name": { "type": "string" },
    "Email": { "type": "string" },
    "Plan": { "type": "string" },
    "MRR": {
      "type": "object",
      "properties": {
        "code": { "type": "string" },
        "value": { "type": "number" }
      }
    },
    "Tags": {
      "type": "array",
      "items": { "type": "string" }
    },
    "Renewal Date": { "type": "string", "format": "date" },
    "Active": { "type": "boolean" }
  }
}
```

***

### Step 3 — Create the Database Records

Three records are needed to make a panel work:

| Record                       | Scope       | What it stores                                      |
| ---------------------------- | ----------- | --------------------------------------------------- |
| `IntegrationApp`             | Global      | Name, icon, description, `type: "panel"`            |
| `APPConfiguration` (version) | Global      | Your endpoint URL, JSON schema, auth config         |
| `IntegrationAppInstall`      | Per-account | Which account + channels, credentials, field config |

#### 3a. Create an `IntegrationApp`

```typescript
await IntegrationAppModel.create({
  name: "My CRM Panel",
  description: "Shows CRM contact data in the conversation sidebar",
  icon: "https://your-cdn.com/crm-icon.png",  // 32×32 PNG recommended
  type: "panel",
  liveVersionId: null,  // fill in after Step 3b
  accountId: null,      // null = global; set an accountId to make it private
});
```

Note the `_id` — you need it for the next step.

#### 3b. Create an `APPConfiguration` (version)

```typescript
const version = await APPConfigurationModel.create({
  appId: "<IntegrationApp._id from above>",
  endpoint: "https://your-service.com/panel/render",
  status: "live",
  schema: JSON.stringify({
    type: "object",
    properties: {
      "Full Name": { type: "string" },
      "Email": { type: "string" },
      "Plan": { type: "string" },
      "MRR": {
        type: "object",
        properties: {
          code: { type: "string" },
          value: { type: "number" },
        },
      },
      "Tags": { type: "array", items: { type: "string" } },
      "Renewal Date": { type: "string", format: "date" },
      "Active": { type: "boolean" },
    },
  }),
});

// Set the liveVersionId on the app
await IntegrationAppModel.findByIdAndUpdate(appId, {
  liveVersionId: version._id,
});
```

***

### Step 4 — Enable for an Account

Each account that should see the panel needs an install record. Use the API to create one:

```bash
POST /api/accounts/:accountId/integration-app/:appId/version/:versionId/install/

{
  "channelIds": ["<whatsapp-channel-id>"],
  "credential": "<encrypted-credential>",
  "status": "active"
}
```

**`channelIds`** controls where the panel tab appears — it only shows up in conversations on the listed channels.

**`credential`** is the auth data your render endpoint receives (API key, tokens, etc.). Always encrypt it before storing.

***

### Step 5 — Test the Panel

1. Open any conversation on one of the configured channels.
2. Check the right sidebar — your panel icon should appear.
3. Click it — the panel calls your render endpoint and displays the returned data.
4. In your browser's Network tab, filter by `/render` to inspect the request and response.

#### Troubleshooting

| Symptom                | Likely cause                                                                                           |
| ---------------------- | ------------------------------------------------------------------------------------------------------ |
| Panel icon not visible | `channelIds` doesn't include this conversation's channel                                               |
| Panel shows blank      | Your endpoint returned `{}`, or a field name in the response doesn't match the schema                  |
| Panel shows error      | Your endpoint returned a non-2xx status, or the install status is not `active`                         |
| Data appears stale     | The panel caches by `appId + conversationId + contactID` — if these haven't changed, it won't re-fetch |

***

### Authentication

If your external system uses OAuth2, Gallabox can handle the token flow for you. Configure it in the app version:

```json
{
  "auth": {
    "type": "oauth2",
    "oauth2": {
      "grantType": "AuthorizationCode",
      "authURL": "https://accounts.mycrm.com/oauth/authorize",
      "accessTokenURL": "https://accounts.mycrm.com/oauth/token",
      "clientId": "YOUR_CLIENT_ID",
      "clientSecret": "YOUR_CLIENT_SECRET",
      "scope": ["contacts.read"]
    }
  }
}
```

Gallabox stores, encrypts, and auto-refreshes the token. The decrypted token is passed as `credential` to your endpoint — no token refresh logic needed on your side.

***

### Field Ordering and Visibility

Users can reorder fields and hide ones they don't need by dragging and dropping in the panel settings. Their preferences are stored per install and applied automatically — no extra code needed on your end.

To set a default field order when creating an install, populate the `config` field:

```json
{
  "Full Name": { "order": 1, "visible": true },
  "Email": { "order": 2, "visible": true },
  "MRR": { "order": 3, "visible": true },
  "Tags": { "order": 4, "visible": false },
  "Plan": { "order": 5, "visible": true }
}
```

***

### API Reference

| Method  | Path                                                                                    | Description                                    |
| ------- | --------------------------------------------------------------------------------------- | ---------------------------------------------- |
| `GET`   | `/api/accounts/:accountId/integration-app/install`                                      | List all installed panels for account          |
| `POST`  | `/api/accounts/:accountId/integration-app/:appId/render`                                | Render a panel (called by frontend)            |
| `POST`  | `/api/accounts/:accountId/integration-app/:appId/version/:versionId/install/`           | Create a new install                           |
| `PATCH` | `/api/accounts/:accountId/integration-app/:appId/version/:versionId/install/:installId` | Update install (channels, credentials, config) |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.gallabox.com/integration/other-integrations/conversation-panels.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
