> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.pinnacle.sh/v-2/quickstart/sms/python/receive/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.pinnacle.sh/_mcp/server. # Receiving SMS Messages > Receiving your first SMS with Pinnacle's Python SDK ```python from rcs import Pinnacle from dotenv import load_dotenv import os from fastapi import FastAPI, Request from rcs.messages.sms.types.sms_send_response import SmsSendResponse from rcs.types import MessageEvent # Load environment variables load_dotenv() API_KEY = os.getenv("PINNACLE_API_KEY") SENDER_NUMBER = os.getenv("SENDER_NUMBER") SIGNING_SECRET = os.getenv("PINNACLE_SIGNING_SECRET") # Initialize the Pinnacle client client = Pinnacle( api_key=API_KEY, ) app = FastAPI() @app.get("/send-sms/{phone_number}") async def send_sms(phone_number): try: result: SmsSendResponse = client.messages.sms.send( from_=SENDER_NUMBER, text="Hello, world!", to=phone_number, ) return {"message": f"SMS message #{result.message_id} {result.status}"} except Exception as e: return {"message": "Error sending SMS", "error": str(e)} @app.post("/inbound-sms") async def inbound_sms(request: Request): # Convert FastAPI Request to the format expected by the Pinnacle SDK req_dict = {"headers": dict(request.headers), "body": await request.body()} message_event: MessageEvent = client.messages.process( req_dict, secret=SIGNING_SECRET # If omitted, will look inside of your .env for PINNACLE_SIGNING_SECRET ) if message_event.type == "MESSAGE.RECEIVED": # Check if this is an SMS message (discriminated by type field) if message_event.message.type == "SMS": text = message_event.message.text from_number = message_event.conversation.from_ print(f"Received message from {from_number}: {text}") return {"status": "received", "message_id": message_event.message.id} if message_event.type == "MESSAGE.STATUS": status = message_event.message.status print(f"Message status: {status}") return {"status": "status", "status": status} return {"status": "processed", "event_type": message_event.type} ``` ## Prerequisites Before proceeding, ensure you have obtained a phone number and API key as described in the [prerequisites](/quickstart/sms). ## Installation Create a Python virtual environment: ```bash python3 -m venv venv ``` Activate the virtual environment: ```bash source venv/bin/activate ``` Install the Pinnacle Python SDK and FastAPI: ```bash pip install rcs "fastapi[standard]" ``` > **Info** > > This guide uses version `rcs>=2.0.4`. Requires: Python \<4.0, >=3.8 ## 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:8000/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 through the phone icon on the sandbox number's card. > **Info** > > 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`](/api-reference/webhooks/attach-webhook). > The `PINNACLE-SIGNING-SECRET` header is reserved. ## Creating Your Webhook Endpoint Create a new Python file (e.g., `main.py`) and add the following snippet to the right. The code above creates a FastAPI 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 FastAPI server: ```bash fastapi dev main.py ``` Your server will start on `http://localhost:8000`. If you're using ngrok for local development, start it in a separate terminal: ```bash ngrok http 8000 ``` 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! ``` > **Warning** > > 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](/methods/process). Optionally, you can also create the `/send-sms/{phone_number}` endpoint to send an initial SMS message out. > One API for RCS, iMessage, MMS, and SMS. Build, test, and scale every channel — send your first message in minutes, not weeks.