> ## Documentation Index
> Fetch the complete documentation index at: https://meepa.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Apps

> Build interactive integrations with OAuth, slash commands, buttons, and modals

Full apps are interactive integrations that can install via OAuth, respond to slash commands, render buttons and select menus, open modals, and subscribe to channel events. They run as separate HTTP services — a Cloudflare Worker is the recommended host.

## What apps can do

* **OAuth install flow** — users install your app to a server with one click
* **Slash commands** — respond to `/command` invocations in any channel
* **Buttons and select menus** — render interactive components in messages
* **Modals** — open multi-field forms triggered by button clicks
* **Event subscriptions** — receive `message.created`, `member.joined`, and other events via webhook

## Create an app

1. Go to `/developer` on your MeepaChat instance
2. Click **New App**
3. Enter a name and description
4. Copy your **Client ID** and **Client Secret**
5. Set your **Interaction URL** — this is where MeepaChat sends slash commands, button clicks, and modal submissions
6. Set your **Redirect URI** — the OAuth callback URL for your app

<Note>
  Keep your Client Secret private. It is used to verify that interaction requests genuinely come from MeepaChat.
</Note>

## OAuth install flow

When a user installs your app, MeepaChat runs a standard OAuth 2.0 authorization code flow.

```
User clicks "Install"
  → MeepaChat shows permission screen
  → User approves
  → MeepaChat redirects to your Redirect URI with ?code=...
  → Your app exchanges code for access token
  → Store token, post confirmation message
```

### Step 1: Authorization URL

Redirect the user to:

```
https://chat.example.com/oauth/authorize
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https://your-app.workers.dev/callback
  &scope=messages:write channels:read
  &response_type=code
  &state=RANDOM_STATE
```

### Step 2: Exchange code for token

```bash theme={null}
curl -X POST https://chat.example.com/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "authorization_code",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "code": "AUTHORIZATION_CODE",
    "redirect_uri": "https://your-app.workers.dev/callback"
  }'
```

Response:

```json theme={null}
{
  "access_token": "...",
  "token_type": "Bearer",
  "scope": "messages:write channels:read",
  "server_id": "SERVER_ID"
}
```

Store the token — use it as `Authorization: Bearer TOKEN` on all subsequent API calls for that server.

## Scopes

| Scope            | Access                                          |
| ---------------- | ----------------------------------------------- |
| `messages:read`  | Read messages in channels the app has access to |
| `messages:write` | Post and edit messages                          |
| `channels:read`  | List channels and their metadata                |
| `members:read`   | List server members                             |
| `webhooks:write` | Create and manage incoming webhooks             |

## Interaction URL

Set your Interaction URL in the Developer Portal. MeepaChat POSTs to this URL whenever a user triggers a slash command, clicks a button, picks a select menu option, or submits a modal.

### Request format

```json theme={null}
{
  "type": "command",
  "customId": "deploy",
  "channelId": "CHANNEL_ID",
  "userId": "USER_ID",
  "values": []
}
```

| Field       | Description                                                                  |
| ----------- | ---------------------------------------------------------------------------- |
| `type`      | `"command"`, `"button"`, `"select_menu"`, or `"modal_submit"`                |
| `customId`  | The slash command name or the `custom_id` of the component                   |
| `channelId` | Channel where the interaction occurred                                       |
| `userId`    | User who triggered it                                                        |
| `values`    | Selected values for `select_menu`; submitted field values for `modal_submit` |

### Response format

Return a JSON response within 3 seconds:

```json theme={null}
{
  "type": "message",
  "content": "Deploying to production...",
  "embeds": [],
  "components": []
}
```

| `type`      | Behavior                                        |
| ----------- | ----------------------------------------------- |
| `"message"` | Post a new message in the channel               |
| `"update"`  | Update the original message in place            |
| `"defer"`   | Acknowledge immediately; post a follow-up later |

## Components

Attach interactive elements to messages using the `components` field.

### Button

```json theme={null}
{
  "components": [{
    "components": [{
      "type": "button",
      "custom_id": "approve",
      "label": "Approve",
      "style": "primary"
    }, {
      "type": "button",
      "custom_id": "reject",
      "label": "Reject",
      "style": "danger"
    }]
  }]
}
```

Button `style` values: `"primary"`, `"secondary"`, `"danger"`.

### Select menu

```json theme={null}
{
  "components": [{
    "components": [{
      "type": "select_menu",
      "custom_id": "env_select",
      "placeholder": "Choose environment",
      "options": [
        { "label": "Production", "value": "prod" },
        { "label": "Staging", "value": "staging" },
        { "label": "Development", "value": "dev" }
      ]
    }]
  }]
}
```

## Starter template

The Cloudflare Worker template in `integrations/template/` is a ready-to-deploy starting point. Clone the MeepaChat repo and copy it:

```bash theme={null}
git clone https://github.com/bogpad/meepachat
cp -r meepachat/integrations/template my-integration
cd my-integration
npm install
```

The template handles:

* `/health` — liveness check
* `/install` — OAuth entry point (stub, wire up your auth URL)
* `/webhook` — receive events from external services with HMAC verification
* `/interaction` — receive slash commands, button clicks, and modal submissions from MeepaChat

### Environment variables

Set these in `wrangler.toml` (plain) or via `wrangler secret put` (sensitive):

| Variable                  | Description                                                        |
| ------------------------- | ------------------------------------------------------------------ |
| `MEEPACHAT_URL`           | Base URL of your MeepaChat instance                                |
| `MEEPACHAT_WEBHOOK_TOKEN` | Webhook token for posting messages                                 |
| `WEBHOOK_SECRET`          | HMAC secret for verifying incoming webhooks from external services |

```bash theme={null}
npx wrangler secret put MEEPACHAT_WEBHOOK_TOKEN
npx wrangler secret put WEBHOOK_SECRET
```

### Example: GitHub integration Worker

```typescript theme={null}
// src/transform.ts
import type { MeepaChatMessage } from "./types";

export function transform(event: string, payload: unknown): MeepaChatMessage | null {
  if (event === "pull_request") {
    const pr = payload as { action: string; pull_request: { title: string; html_url: string; number: number }; sender: { login: string } };
    if (pr.action !== "opened") return null;
    return {
      embeds: [{
        title: `PR #${pr.pull_request.number}: ${pr.pull_request.title}`,
        url: pr.pull_request.html_url,
        color: "#2ea44f",
        author: { name: pr.sender.login },
        footer: { text: "GitHub" },
      }],
      thread_key: `pr-${pr.pull_request.number}`,
    };
  }
  return null;
}
```

```typescript theme={null}
// src/interact.ts
import type { Env, InteractionData, InteractionResponse } from "./types";

export function interact(type: string, data: InteractionData, env: Env): InteractionResponse {
  if (type === "button" && data.customId === "approve") {
    return { type: "message", content: "Approved. Merging PR..." };
  }
  if (type === "button" && data.customId === "reject") {
    return { type: "message", content: "Rejected." };
  }
  return { type: "message", content: `Unknown interaction: ${data.customId}` };
}
```

## Deploy to Cloudflare Workers

```bash theme={null}
# Authenticate (one time)
npx wrangler login

# Local dev
npm run dev

# Deploy
npm run deploy
```

After deploying, set your Worker URL as the **Interaction URL** in the Developer Portal:

```
https://my-integration.your-subdomain.workers.dev/interaction
```

<Warning>
  Verify interaction request signatures in production. MeepaChat signs requests with your Client Secret. The template's `verifySignature` utility handles HMAC-SHA256 verification — call it before processing any interaction.
</Warning>

## Event subscriptions

Subscribe your app to channel events via the API after OAuth install:

```bash theme={null}
curl -X POST https://chat.example.com/api/apps/subscriptions \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_id": "CHANNEL_ID",
    "events": ["message.created", "member.joined"]
  }'
```

MeepaChat will POST to your Interaction URL for each matching event with `type: "event"` and the event payload in `data`.
