SMS and RCS Webhooks: Real-Time Delivery Receipts and Inbound Message Handling
Stop polling for message status. Pinnacle's webhook system delivers real-time events for every message delivery update, inbound reply, and RCS typing indicator — attached to any phone number or RCS agent in seconds, manageable from the dashboard or API.
Ivan

The Polling Tax
Before webhooks became the norm, developers handled real-time events the hard way: polling. Send a message, then ping the status endpoint every few seconds until you get a final state. It works — in the same way a broken clock is right twice a day. It wastes API calls, it introduces latency, it doesn't scale, and it costs you money for the privilege of getting worse results.
Webhooks are the better answer. Instead of you asking "what happened?", the system tells you the moment something does. A message is delivered — your endpoint gets a POST. A customer replies — your endpoint gets a POST. An RCS user starts typing — your endpoint gets a POST. No polling, no lag, no waste.
Pinnacle's webhook system is built on this model, with a simple attach/detach design that works across SMS, MMS, and RCS with no configuration overhead.
How Pinnacle Webhooks Work
Every webhook in Pinnacle is an HTTPS endpoint that receives POST requests when specific events occur. You register your endpoint, attach it to one or more senders (phone numbers or RCS agents), and Pinnacle begins delivering events to it in real time.
The lifecycle looks like this:
- A carrier event occurs — message delivered, message received, user starts typing
- Pinnacle's gateway receives the event from the carrier via its own webhook infrastructure
- Pinnacle acknowledges immediately and processes the event asynchronously — no carrier timeout risk
- Your webhook endpoint receives a POST with the event payload, typically within milliseconds
This fire-and-forget acknowledgment pattern is critical. Carriers expect fast responses to their webhook deliveries. By acknowledging immediately and processing asynchronously, Pinnacle ensures no events are dropped due to slow downstream processing on your end.
Three Event Types
Pinnacle webhooks support three distinct event types. You can subscribe to all events on a single webhook, or create separate webhooks filtered to specific event types.
MESSAGE.STATUS — Delivery Receipts
The most common webhook use case: knowing whether a message you sent actually reached the recipient.
Every outbound message transitions through statuses: QUEUED → SENT → DELIVERED → READ (for RCS). Failed messages surface SEND_FAILED or DELIVERY_FAILED with an error code. Each status transition triggers a MESSAGE.STATUS event delivered to your webhook.
Use cases:
- Update your internal order management system when a shipping notification is confirmed delivered
- Retry or escalate when a message fails with a
SEND_FAILEDevent - Log read receipts for RCS messages to measure campaign engagement
- Trigger downstream workflows when a critical notification reaches its recipient
MESSAGE.RECEIVED — Inbound Messages
When a customer replies to your number, or sends you a message unprompted, Pinnacle delivers an MESSAGE.RECEIVED event to your webhook.
The payload includes the sender's phone number, message content (text, media URLs for MMS, button click data for RCS), timestamp, and the phone number or RCS agent that received the message.
Use cases:
- Route customer replies into your CRM or support ticketing system
- Trigger automated response flows (confirm opt-in, send requested information, process STOP/HELP commands)
- Feed inbound messages into a chatbot or AI agent for automated handling
- Log conversations for compliance and audit purposes
USER.TYPING — RCS Typing Indicators
Exclusive to RCS agents: when a user begins typing a reply in their messaging app, Pinnacle delivers a USER.TYPING event. This is analogous to the typing indicator in consumer messaging apps like iMessage or WhatsApp — but available to your server in real time.
Use cases:
- Show a "customer is typing" indicator in your internal support dashboard
- Prepare a human agent for an incoming reply
- Track engagement signals beyond just reads and button clicks
Manage all your webhooks from one place — create, enable, disable, and attach to senders without touching code.
Attaching a Webhook
Creating a webhook and attaching it to a sender takes a single API call. You can attach an existing webhook by ID, or create a new one inline:
import { PinnacleClient } from "rcs-js";
const client = new PinnacleClient({ apiKey: process.env.PINNACLE_API_KEY });
// Create and attach a new webhook to a phone number
await client.webhooks.attach({
senders: ["+18005550001"],
name: "Production Delivery Events",
url: "https://api.yourapp.com/webhooks/pinnacle",
event: "MESSAGE.STATUS", // or "MESSAGE.RECEIVED", "USER.TYPING", or null for all
});// Attach an existing webhook to an RCS agent
await client.webhooks.attach({
senders: ["agent_your_brand"],
webhookId: "wh_abc123",
event: null, // null = all events
});A few important design details:
- Idempotent: Re-attaching a webhook to a sender that already has it updates the event filter without creating duplicates
- Additive: A sender can have multiple webhooks for different event types
- RCS-aware:
USER.TYPINGevents are only available for RCS agent senders — attaching this event type to a phone number has no effect
Detaching a Webhook
Detach a webhook from specific senders without deleting it:
await client.webhooks.detach({
webhookId: "wh_abc123",
senders: ["+18005550001"],
});The webhook itself remains intact and active for any other senders it's attached to. This makes it easy to reorganize webhook routing without disrupting other integrations.
Managing Webhooks from the Dashboard
Every webhook attached to your account is visible and manageable from app.pinnacle.sh/dashboard/development/webhooks.
From the dashboard you can:
- Create new webhooks with a name and HTTPS endpoint
- Enable or disable individual webhooks (disabled webhooks stop receiving events without losing their configuration)
- View which senders each webhook is attached to
- See recent webhook delivery logs for debugging
Debug webhook deliveries with per-event logs — see exactly what was sent to your endpoint and when.
Webhook Failure Notifications
Silent webhook failures are one of the most dangerous failure modes in messaging infrastructure. Your endpoint goes down, events pile up, and you don't notice until customers report missing messages — or worse, don't report them and just churn.
Pinnacle solves this with configurable email notifications for webhook failures. From your Email Preferences, enable Webhook Failures and set the notification cadence — for example, receive an email every 3 failures up to a maximum of 5 notifications. This ensures you're alerted early enough to act, without being spammed during a brief transient error.
Configure webhook failure notifications to catch endpoint issues before they become silent outages.
The same notification system also covers other critical events — brand submission status, campaign approval updates, compliance changes, and API key creation — so your team stays informed across every part of the platform.
Using Webhooks with the Pinnacle MCP Server
The Pinnacle MCP server exposes webhook management to your AI-powered workflows. This is particularly useful for AI agents that need to set up event-driven messaging pipelines dynamically:
"Attach a webhook at https://api.myapp.com/events to all of our 10DLC phone numbers, listening for inbound messages only."
The MCP's attach_webhook, detach_webhook, get_webhooks, and list_webhooks tools handle this without manual API calls.
Building an Inbound Message Handler
Here's what a minimal inbound message webhook handler looks like in TypeScript:
import express from "express";
const app = express();
app.use(express.json());
app.post("/webhooks/pinnacle", async (req, res) => {
// Acknowledge immediately — critical for carrier reliability
res.status(200).send();
const { event, data } = req.body;
if (event === "MESSAGE.RECEIVED") {
const { from, to, content } = data;
console.log(`Inbound from ${from} to ${to}: ${content.text}`);
// Route to CRM, support system, AI handler, etc.
}
if (event === "MESSAGE.STATUS") {
const { messageId, status, error } = data;
console.log(`Message ${messageId} is now ${status}`);
if (status === "SEND_FAILED") {
// Alert, retry, or escalate
}
}
});Notice the pattern: respond with 200 first, then process. This mirrors how Pinnacle handles carrier events — acknowledge fast, process async.
Why Webhooks Beat Polling Every Time
The case against polling isn't just philosophical — it's practical:
- Latency: Polling checks at fixed intervals. If you poll every 5 seconds and a status changes at second 1, you wait 4 more seconds to find out. Webhooks deliver within milliseconds.
- Efficiency: Polling makes an API call whether or not anything changed. With 10,000 messages in flight, that's 10,000 wasted calls every polling cycle. Webhooks only fire when there's something to report.
- Scalability: Polling load grows linearly with message volume. Webhook load grows with event volume — a much smaller number, since not every message generates a status change every second.
- Reliability: A polling loop that crashes loses state. Webhooks are delivered by Pinnacle's infrastructure; if your endpoint is temporarily down, Pinnacle retries.
The math is straightforward: webhooks deliver better results at lower cost and lower complexity.
Webhook Security
All webhook deliveries include a signature header you can use to verify the payload came from Pinnacle — not from a third party spoofing your endpoint. Verify this signature in your handler before processing any event.
For implementation details, see the receiving messages guide.
Want More Control? Enterprise Webhook Configuration
For high-volume deployments, custom retry logic, or multi-region webhook routing, the Pinnacle SDK and API give you full control over webhook configuration. Reach out to founders@pinnacle.sh for enterprise webhook requirements.
Frequently Asked Questions
What URL format does my webhook endpoint need?
Your endpoint must be a publicly accessible HTTPS URL. HTTP URLs and self-signed certificates are not accepted for production webhooks.
Can I use one webhook for all my phone numbers?
Yes. You can attach a single webhook to up to 50 senders per API call, and re-run the call for additional senders. A single webhook can receive events from as many senders as you want.
Can I filter events per sender?
Yes. Specify the event field when attaching — MESSAGE.STATUS, MESSAGE.RECEIVED, or USER.TYPING — to receive only that event type on that sender. Pass null for all events.
What happens if my endpoint is down when Pinnacle tries to deliver an event?
Pinnacle retries failed webhook deliveries with backoff. If your endpoint continues to fail, Pinnacle sends you an email notification so you're never silently missing messages. You configure the failure threshold in your Email Preferences) — for example, "email every 3 failures, max 5 total" — so you get alerted before a transient issue becomes a silent outage. Check the webhook logs in the dashboard to see delivery attempts and their status.
Is there a rate limit on webhook deliveries?
Pinnacle delivers events as they arrive — there's no artificial rate limit on your webhook endpoint. For very high-volume deployments, ensure your endpoint can handle burst traffic.
Can I test webhooks without production traffic?
Yes. Use Pinnacle's sandbox environment and sandbox numbers to send test messages and trigger webhook events in a controlled way without touching production.
Does the typing indicator (USER.TYPING) work for phone number senders?
No. USER.TYPING is exclusive to RCS agents. Phone number senders (SMS/MMS) do not support typing indicators.
Key Takeaways
- Three event types:
MESSAGE.STATUSfor delivery receipts,MESSAGE.RECEIVEDfor inbound messages,USER.TYPINGfor RCS typing indicators - Attach to any sender: Phone numbers (SMS/MMS) or RCS agents — up to 50 senders per attach call
- Idempotent and additive: Re-attaching updates the event filter; multiple webhooks per sender are supported
- Fire-and-forget processing: Pinnacle acknowledges carrier events immediately and delivers to your endpoint asynchronously
- Failure notifications: Configurable email alerts when your webhook endpoint fails — no silent message loss
- Dashboard management: Create, enable/disable, and debug webhooks from app.pinnacle.sh/dashboard/development/webhooks
- MCP-ready: Manage webhooks via the Pinnacle MCP server in AI-powered workflows
- Webhooks always beat polling: Lower latency, lower cost, higher reliability
Get Started with Webhooks
Attach your first webhook at app.pinnacle.sh/dashboard/development/webhooks or via the webhooks API. For the full event payload schema and signature verification, see the receiving messages guide.
If you're not yet on Pinnacle, sign up and subscribe to a plan to get started. Questions? Get in touch.
