Inbox

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

Step 1: Backend

Create an endpoint that requests a signed JWT from Pinnacle. Control which conversations and UI appear in the inbox.

1import 'dotenv/config';
2import express from 'express';
3
4const app = express();
5app.use(express.json());
6
7app.post('/api/get-inbox-token', async (req, res) => {
8 const response = await fetch('https://api.pinnacle.sh/embed/get-token', {
9 method: 'POST',
10 headers: {
11 'Content-Type': 'application/json',
12 'pinnacle-api-key': process.env.PINNACLE_API_KEY!
13 },
14 body: JSON.stringify({
15 inboxFilter: {}, // optional — pass brandIds or senders to filter, or {} for a team-level inbox
16 scopes: [
17 'CONVERSATIONS.LIST', // required
18 'CONVERSATIONS.MESSAGES', // required
19 'CONVERSATIONS.SEND',
20 'CONVERSATIONS.CREATE',
21 'UPLOAD_FILE',
22 'REALTIME.CONNECTION'
23 ]
24 })
25 });
26
27 const { signedToken } = await response.json();
28 res.json({ signedToken });
29});
30
31app.listen(8080, () => {
32 console.log('Server running at http://localhost:8080');
33});

Schema

FieldTypeRequiredDescription
inboxFilterobjectYesFilter conversations by brandIds or senders, or pass {} for a team-level inbox
scopesstring[]YesPermissions granted to the token

FieldTypeDescription
brandIdsstring[]Only show conversations from these brands
sendersstring[]Only show conversations from these senders (agent IDs or phone numbers in E.164 format)

Filters are mutually exclusive — pass either brandIds or senders, not both. Pass an empty object to show all conversations for the team.

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.

1<!-- Other page content -->
2
3<iframe
4 id="inbox"
5 src="https://embed.pinnacle.sh/embed/v2/inbox"
6 style="width: 100%; height: 100vh; border: none;">
7</iframe>
8
9<!-- Other page content -->
10
11<script>
12 const iframe = document.getElementById('inbox');
13
14 window.addEventListener('message', async (event) => {
15 if (event.origin !== 'https://embed.pinnacle.sh') return;
16
17 if (event.data.type === 'INBOX_READY') {
18 // Fetch token from your backend
19 const res = await fetch('/api/get-inbox-token', { method: 'POST' });
20 const { signedToken } = await res.json();
21
22 // Initialize the inbox
23 iframe.contentWindow.postMessage({
24 type: 'INIT_INBOX', // required
25 signedToken: signedToken, // required
26 primaryColor: '#16a34a'
27 }, 'https://embed.pinnacle.sh');
28 }
29
30 if (event.data.type === 'INBOX_ERROR') {
31 // handle iframe init failure here
32 }
33 });
34</script>
35
36<!-- Other page content -->

Events

FieldTypeDescription
INIT_INBOXpostMessageSend signed token and configuration to initialize the inbox
INBOX_READYeventIframe has loaded and is ready to receive configuration
INBOX_ERROReventValidation or initialization error occurred

FieldTypeRequiredDescription
type"INIT_INBOX"YesMessage type identifier
signedTokenstringYesJWT token from your backend
primaryColorstringNoPrimary color for the inbox (defaults to #16a34a)

Complete Integration

1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Inbox</title>
7</head>
8<body>
9 <iframe
10 id="inbox"
11 src="https://embed.pinnacle.sh/embed/v2/inbox"
12 style="width: 100%; height: 100vh; border: none;">
13 </iframe>
14
15 <script>
16 const iframe = document.getElementById('inbox');
17
18 window.addEventListener('message', async (event) => {
19 if (event.origin !== 'https://embed.pinnacle.sh') return;
20
21 if (event.data.type === 'INBOX_READY') {
22 // Fetch token from your backend
23 const res = await fetch('/api/get-inbox-token', { method: 'POST' });
24 const { signedToken } = await res.json();
25
26 // Initialize the inbox
27 iframe.contentWindow.postMessage({
28 type: 'INIT_INBOX', // required
29 signedToken: signedToken, // required
30 primaryColor: '#16a34a'
31 }, 'https://embed.pinnacle.sh');
32 }
33
34 if (event.data.type === 'INBOX_ERROR') {
35 // handle iframe init failure here
36 }
37 });
38 </script>
39</body>
40</html>