For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
SupportDashboard
DocsAPI ReferenceWebhooksMethodsUI ComponentsMCP ServerChangelog
  • Documentation
    • Introduction
    • Authentication
    • RCS Support
  • Quickstart
    • SMS
      • Python
      • TypeScript
        • Send
        • Receive
      • Ruby
    • RCS
  • Guides
    • Purchase Phone Numbers
    • Brands
    • Campaigns
    • Messages
    • Branded Test Agents
    • Handling Expired URLs
LogoLogo
SupportDashboard
QuickstartSMSTypeScript

Receiving SMS Messages

1import express from "express";
2import { PinnacleClient, Pinnacle } from "rcs-js";
3import dotenv from "dotenv";
4dotenv.config();
5const port = 3000;
6
7const app = express();
8const client = new PinnacleClient({ apiKey: process.env.PINNACLE_API_KEY });
9
10async function sendSMS(recipient: string) {
11 try {
12 const res = await client.messages.sms.send({
13 from: process.env.SENDER_NUMBER!,
14 to: recipient, // Recipient number
15 text: "Hello, world!",
16 });
17
18 console.log("✅ Message sent:", JSON.stringify(res, null, 2));
19 } catch (err) {
20 console.error("❌ Error sending message:", err);
21 }
22}
23
24app.get("/send-sms/:phoneNumber", async (req, res) => {
25 const { phoneNumber } = req.params;
26 await sendSMS(phoneNumber);
27});
28
29app.post("/inbound-sms", express.json(), async (req, res) => {
30 try {
31 // Process and validate the webhook
32 // Returns a fully typed MessageEvent or UserEvent object
33 // UserEvent is mainly used to know when the user is typing
34 const messageEvent: Pinnacle.MessageEvent | Pinnacle.UserEvent =
35 await client.messages.process(req); // automatically detects PINNACLE_SIGNING_SECRET in your env vars
36
37 // messageEvent is now typed as Pinnacle.MessageEvent
38 // Your business logic here
39 await handleInboundMessage(messageEvent);
40
41 res.status(200).json({ status: "processed" });
42 } catch (error) {
43 throw error;
44 }
45});
46
47async function handleInboundMessage(
48 event: Pinnacle.MessageEvent | Pinnacle.UserEvent
49) {
50 console.log(event);
51 switch (event.type) {
52 case "MESSAGE.RECEIVED":
53 if (event.direction == "INBOUND") {
54 // Type guard to check if message is SMS (discriminated by type field)
55 if (event.message.type === "SMS") {
56 const messageText = event.message.text;
57 console.log("Received message:", messageText);
58
59 if (messageText === "hello") {
60 await sendSMS(event.conversation.from);
61 }
62 }
63 break;
64 }
65
66 case "MESSAGE.STATUS":
67 break;
68
69 case "USER.TYPING":
70 break;
71 }
72}
73
74app.listen(port, () => {
75 console.log(`Example app listening on port ${port}`);
76});
Was this page helpful?
Previous

Ruby SDK Quickstart

Next
Built with

Prerequisites

Before proceeding, ensure you have obtained a phone number and API key as described in the prerequisites.

Installation

Initialize a new Node.js project:

$npm init -y

Install the Pinnacle TypeScript SDK and Express:

$npm i express dotenv rcs-js
$npm i --save-dev @types/express @types/node tsx

This guide uses version rcs-js>=2.0.3. It’s compatible with the following runtimes: Node.js 18+, Vercel, Cloudflare Workers, Deno v1.25+, Bun 1.0+, and React Native.

Configuration

Create an .env file in your project root and add your Pinnacle API key and signing secret:

PINNACLE_API_KEY="your_api_key" # pnclk_
SENDER_NUMBER="your_phone_number" # +12345678910
PINNACLE_SIGNING_SECRET="your_signing_secret" # pss_

Setting Up a Webhook

To receive inbound SMS messages, you need to configure a webhook in the Pinnacle dashboard:

  1. Navigate to Development > Webhooks in the Pinnacle dashboard
  2. Click Create new webhook
  3. Give your webhook a descriptive name
  4. Enter your webhook endpoint URL
    • For local development, use an ngrok tunnel pointing to localhost:3000/inbound-sms
    • For production, use your deployed server URL
  5. After creation, copy the signing secret and add it to your .env file
  6. Attach a phone number to your webhook to receive messages. If the number is a sandbox number, ensure that you’ve whitelisted a number and verified the 4 digit PIN.

Optionally, you can configure custom HTTP headers (e.g. X-API-KEY) to be sent on every webhook delivery. Add them in the dashboard or via the headers field on POST /webhooks/attach. The PINNACLE-SIGNING-SECRET header is reserved.

Creating Your Webhook Endpoint

Create a new TypeScript file (e.g., index.ts) and add the following snippet to the right.

The code above creates an Express endpoint that:

  • Receives webhook POST requests at /inbound-sms
  • Verifies the webhook signature using your signing secret
  • Processes incoming message events
  • Handles both received messages and message status updates

Running Your Server

Start the server:

$npx tsx index.ts

Your server will start on http://localhost:3000. If you’re using ngrok for local development, start it in a separate terminal:

$ngrok http 3000

Use the ngrok URL (e.g., https://abc123.ngrok.io/inbound-sms) as your webhook endpoint in the Pinnacle dashboard.

Testing Your Webhook

Send an SMS to your Pinnacle phone number from any mobile device. You should see the message logged in your server console:

Received message from +14155551234: Hello, this is a test!

If you’re not receiving any messages, make sure you have a phone number associated with your webhook.

Your webhook should now be successfully receiving inbound SMS messages as well message status updates for outbound messages!

For more detail about processing the message payload received, please view the process method.

Optionally, you can also create the /send-sms/:phone_number endpoint to send an initial SMS message out.