Last updated 30 August 2026. Sending and receiving are separate paths in RCS, and only one of them tells you when it breaks. Written after losing several days to a blank webhook while every check we had stayed green.
Your messages send. They arrive. People reply. Nothing reaches your server, and nothing anywhere reports an error.
This is the most common way an RCS integration fails, and the hardest to notice, because every signal you have says it is working. Sending and receiving are separate paths in RCS. Outbound goes from you to Google. Inbound goes from Google to a webhook URL you registered in a console, months ago, possibly for a different deployment. If that URL is wrong, blank, or pointing at a service you have since replaced, replies are delivered somewhere that is not you and the send path never notices.
We lost several days to exactly this. Our partner webhook was blank at Google. Every inbound reply on the platform was dropped for days. Our health check was green the whole time, because it was checking that we held a token — not that anyone was using it.
Not "is the webhook configured". Not "is the token set". Has a request from Google actually arrived and verified?
Those are different questions and only the last one can go wrong in the way that hurts. A token sitting in your environment proves nothing about what is registered on Google's side, and that is the half you cannot see from your own logs.
If you have no record of a verified inbound request, you have no evidence inbound works. Silence is not evidence. It is the same reading you would get from a webhook that has never been configured at all.
Google will happily deliver every outbound message for an agent with no inbound webhook. The console does not warn you. Nothing fails. Replies are simply dropped.
Set a partner-level webhook, which covers every agent under your partner account, or an agent-level one for a single agent. Google validates the URL when you save it, so a save that succeeds is itself proof the endpoint is reachable.
If an agent has its own webhook, Google uses it and ignores the partner-level URL entirely. Worse, Google signs those pushes with that agent's verification token, not the partner token — so your partner endpoint could not verify them even if it received them.
This bites when an agent-level webhook was set once for testing and forgotten. Outbound keeps working. That one agent's replies go to a URL nobody is watching.
A webhook pointing at a service you replaced keeps validating long after it stops being right. Google has no way to know you moved. Check the registered URL against the deployment you are actually running, and remember that a service often answers on more than one hostname — a custom domain and the platform's own. Those are the same process, and a check that compares against only one of them will report a working webhook as broken.
Google validates a webhook by sending a handshake containing a clientToken, and
expects you to echo back the secret. If your token does not match, you cannot
complete the handshake.
The failure is unhelpful by default. Most implementations answer a mismatched handshake with a bare 400, and the console renders that as "URL returned an error response" — indistinguishable from a wrong address, a down service or a DNS failure. The console will not show you the token it sent, so the one fact that separates those causes is the one nobody has.
Answer the mismatch explicitly. Say that the URL is right, the service is up, and only the token disagrees. Never echo the secret for a token you cannot match.
One ordering trap catches nearly everyone: a variable saved in your hosting dashboard does not reach the running process until it restarts. Save the token, wait for the restart, then click Verify. Clicking Verify first guarantees a mismatch against the old value still in memory.
Inbound pushes are signed. If your verification is wrong — wrong token, wrong payload encoding, comparing the decoded body instead of the raw base64 — every genuine request is refused with a 403 that looks exactly like a forged one.
Log the outcome of every inbound request, including the ones you drop. A route with several legitimate silent drops — unknown event type, duplicate retry, no matching conversation — is indistinguishable from a webhook that never fires unless each one leaves a line behind.
Google retries webhook deliveries. If you do not de-duplicate, the same reply is processed more than once; if you de-duplicate on message ID alone, you will silently drop distinct events that share one — delivery receipts and read receipts arrive under the same message ID. Key your de-duplication on the event type as well as the ID.
Do not wait for a customer. Three checks, in order:
clientToken you know is incorrect. A healthy endpoint answers quickly and says the
token does not match. This proves the URL is reachable and parsing, without touching your
real token.A subtler version of this failure: replies arrive, but you cannot tell what they answer.
Reply chips travel with a postbackData value that comes back when someone taps
them. If you generate that value in one place and interpret it somewhere else, the two drift and
taps resolve to the wrong question — or to nothing at all.
It gets worse with carousels, where each card carries its own chips. Number each card's chips from one and every card's first chip gets an identical value, so a tap on Wednesday books Monday. Both sides believe it worked. The recipient sees a confirmation for a day they did not choose.
Define the chip order in exactly one place and have both the sender and the resolver read it from there. If you cannot point at that single definition, you have two, and they will diverge.
The lesson is not "check your webhook". It is that a check reading your own configuration cannot detect a misconfiguration on the other side of an integration.
A check that cannot fail is worthless. One that fails on a working system is worse: it trains you to ignore it, and takes the real signal down with it.
One API and an MCP server for RCS with automatic SMS fallback. Brand onboarding, carrier readiness and the checks in this article are built in, so the failures above are reported rather than discovered.
Get a free sandbox key — no card, nothing to verify first — or read the agent-facing reference.
Two neighbours: nine undocumented traps in Google’s RBM API that get an agent rejected before it ever sends, and why an approved agent still reaches nobody until a carrier agreement exists.