Before you start
- Node.js
22.22.3or newer. - Chat SDK
4.41.0, its memory state adapter, and Relay’s adapter. - An Agent Token and a webhook signing secret for your agent.
Connect
On your laptop
The CLI includes the signed local forwarder:RELAY_WEBHOOK_SECRET while the process runs.
Deployed
Create a webhook subscription whose target is your public HTTPS route, then store its signing secret asRELAY_WEBHOOK_SECRET. Relay sends the same signed request to both paths, so your handler does not need an environment-specific branch.
Install the packages from one Chat SDK major version line:
POST /webhooks/relay:
Send it a message
Open Relay on your phone and message the agent’s Handle. Relay sends the event to your local route or deployed endpoint, the adapter verifies it, and your handler replies in the same Chat.What it can do
Outgoing parts include
buttons ({ type: "buttons", items: [{ label }, { label, url }] }, 1 to 5 items) beside a text part. The Chat SDK’s message model carries text and attachments only, so a backend that wants buttons sends them through the Relay client directly, as Send buttons under a Message shows. A tap comes back as an ordinary message whose text is the label.
When it fails
- Signature verification fails: the secret does not belong to this delivery path, or the body was re-encoded.
- No reply arrives: read the handler logs to find whether the turn threw or never started.
- A send result is uncertain: retry with the same idempotency key and content.
postMessageParts with the existing idempotency strategy. Read selected_values and reply_to from message.raw.message; message.text retains the readable bullet text.
Payments. Create the request with adapter.client.createPaymentRequest, then send its checkout_url alone as a payment part through postMessageParts, with any words before it as their own Message. The part, including its status, stays in message.raw.message, and adapter.client.cancelPaymentRequest cancels a request that has not been paid.

