> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.pinnacle.sh/v-2/ui/components/inbox/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.pinnacle.sh/_mcp/server. # Inbox > Embed a full-featured messaging inbox into your application The inbox embed gives your users a complete RCS and SMS messaging experience — conversations, message history, rich media, and real-time updates. ![Send RCS Demo](https://pncl.to/5NVSekVskYnXtQOlMBOOMDGHx5E5cV) ## Step 1: Backend Create an endpoint that requests a signed JWT from Pinnacle. Control which conversations and UI appear in the inbox. #### server.ts ```typescript import 'dotenv/config'; import express from 'express'; const app = express(); app.use(express.json()); app.post('/api/get-inbox-token', async (req, res) => { const response = await fetch('https://api.pinnacle.sh/embed/get-token', { method: 'POST', headers: { 'Content-Type': 'application/json', 'pinnacle-api-key': process.env.PINNACLE_API_KEY! }, body: JSON.stringify({ inboxFilter: {}, // optional — pass brandIds or senders to filter, or {} for a team-level inbox scopes: [ 'CONVERSATIONS.LIST', // required 'CONVERSATIONS.MESSAGES', // required 'CONVERSATIONS.SEND', 'CONVERSATIONS.CREATE', 'UPLOAD_FILE', 'REALTIME.CONNECTION' ] }) }); const { signedToken } = await response.json(); res.json({ signedToken }); }); app.listen(8080, () => { console.log('Server running at http://localhost:8080'); }); ``` #### package.json ```json { "name": "inbox-embed", "version": "1.0.0", "type": "module", "scripts": { "dev": "tsx watch server.ts", "start": "tsx server.ts" }, "dependencies": { "dotenv": "^16.4.7", "express": "^5.2.1" }, "devDependencies": { "@types/express": "^5.0.6", "@types/node": "^20", "tsx": "^4.19.3", "typescript": "^5" } } ``` #### .env.example ```bash # https://app.pinnacle.sh/dashboard/development/api-keys PINNACLE_API_KEY=pnclk_your_api_key_here ``` ### Schema | Field | Type | Required | Description | | ------------- | ---------- | -------- | ------------------------------------------------------------------------------------ | | `inboxFilter` | `object` | Yes | Filter conversations by `brandIds` or `senders`, or pass `{}` for a team-level inbox | | `scopes` | `string[]` | Yes | Permissions granted to the token | \ #### inboxFilter | Field | Type | Description | | ---------- | ---------- | --------------------------------------------------------------------------------------- | | `brandIds` | `string[]` | Only show conversations from these brands | | `senders` | `string[]` | Only show conversations from these senders (agent IDs or phone numbers in E.164 format) | > **Info** > > Filters are mutually exclusive — pass either `brandIds` or `senders`, not both. Pass an empty object to show all conversations for the team. #### scopes | Scope | Required | Description | | ------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------- | | `CONVERSATIONS.LIST` | Yes | List and search conversations | | `CONVERSATIONS.MESSAGES` | Yes | Read message history | | `CONVERSATIONS.SEND` | No | Send RCS, SMS, and MMS messages. If omitted, the message sending UI will not appear. | | `CONVERSATIONS.CREATE` | No | Start new conversations. If omitted, the conversation creation UI will not appear. | | `UPLOAD_FILE` | No | Upload images, videos, and PDFs. If omitted, the media upload UI will not appear. | | `REALTIME.CONNECTION` | No | Receive real-time message and conversation updates. If omitted, the realtime connection will not be initialized. | ## Step 2: Frontend Add the iframe to your page. Subscribe to the `message` event to listen for `INBOX_READY`, then send `INIT_INBOX` with your signed token to initialize the inbox. ```html ``` ### Events | Field | Type | Description | | ------------- | ------------- | ----------------------------------------------------------- | | `INIT_INBOX` | `postMessage` | Send signed token and configuration to initialize the inbox | | `INBOX_READY` | `event` | Iframe has loaded and is ready to receive configuration | | `INBOX_ERROR` | `event` | Validation or initialization error occurred | \ #### INIT\_INBOX | Field | Type | Required | Description | | -------------- | -------------- | -------- | --------------------------------------------------- | | `type` | `"INIT_INBOX"` | Yes | Message type identifier | | `signedToken` | `string` | Yes | JWT token from your backend | | `primaryColor` | `string` | No | Primary color for the inbox (defaults to `#16a34a`) | #### INBOX\_READY Emitted by the iframe when it has loaded and is ready to receive configuration. Listen for this event before sending `INIT_INBOX`. #### INBOX\_ERROR Emitted by the iframe when a validation or initialization error occurs. ## Complete Integration #### index.html ```html Inbox ``` #### server.ts ```typescript import 'dotenv/config'; import express from 'express'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const app = express(); app.use(express.json()); app.use(express.static('public')); app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'index.html')); }); app.post('/api/get-inbox-token', async (req, res) => { const response = await fetch('https://api.pinnacle.sh/embed/get-token', { method: 'POST', headers: { 'Content-Type': 'application/json', 'pinnacle-api-key': process.env.PINNACLE_API_KEY! }, body: JSON.stringify({ inboxFilter: {}, // optional — pass brandIds or senders to filter, or {} for a team-level inbox scopes: [ 'CONVERSATIONS.LIST', // required 'CONVERSATIONS.MESSAGES', // required 'CONVERSATIONS.SEND', 'CONVERSATIONS.CREATE', 'UPLOAD_FILE', 'REALTIME.CONNECTION' ] }) }); const { signedToken } = await response.json(); res.json({ signedToken }); }); app.listen(8080, () => { console.log('Server running at http://localhost:8080'); }); ``` #### package.json ```json { "name": "inbox-embed", "version": "1.0.0", "type": "module", "scripts": { "dev": "tsx watch server.ts", "start": "tsx server.ts" }, "dependencies": { "dotenv": "^16.4.7", "express": "^5.2.1" }, "devDependencies": { "@types/express": "^5.0.6", "@types/node": "^20", "tsx": "^4.19.3", "typescript": "^5" } } ``` #### .env.example ```bash # https://app.pinnacle.sh/dashboard/development/api-keys PINNACLE_API_KEY=pnclk_your_api_key_here ``` > One API for RCS, iMessage, MMS, and SMS. Build, test, and scale every channel — send your first message in minutes, not weeks.