Display custom keyboard buttons in Telegram bot using Dialogflow webhook

I’m working on a Telegram bot that’s connected to Dialogflow (formerly api.ai). The bot works fine for sending text messages, but I can’t get custom keyboard buttons to appear in Telegram. When I send responses through Dialogflow, only the text part gets delivered to users.

I have a webhook that returns JSON responses when specific intents are triggered. Here’s an example of what my webhook sends back:

{
  "id": "12345678-abcd-efgh-ijkl-987654321000",
  "timestamp": "2023-08-20T10:30:45.123Z",
  "result": {
    "source": "agent",
    "resolvedQuery": "start",
    "action": "display-menu",
    "actionIncomplete": false,
    "parameters": {},
    "contexts": [],
    "metadata": {
      "intentId": "abc123-def456-ghi789",
      "webhookUsed": "true",
      "intentName": "MainMenu"
    },
    "fulfillment": {
      "speech": "Choose an option:",
      "source": "webhook.php",
      "data": {
        "chat_id": 123456789,
        "text": "Please select:",
        "parse_mode": "HTML",
        "reply_markup": {
          "keyboard": [
            "Option 1",
            "Option 2"
          ],
          "one_time_keyboard": true,
          "resize_keyboard": true
        }
      }
    },
    "score": 1
  },
  "status": {
    "code": 200,
    "errorType": "success"
  },
  "sessionId": "session-12345-abcde"
}

I’ve included the reply_markup data in my JSON response, but the keyboard buttons don’t show up in Telegram. What am I missing here? How can I make Dialogflow pass the keyboard markup to Telegram properly?

you’re using the old dialogflow v1 format - telegram integration doesn’t play nice with that. switch to fulfillmentMessages instead of the fulfillment object. had the exact same problem last month, and that fixed it for me.

Dialogflow doesn’t pass the data field straight to Telegram. You need to use Dialogflow’s platform-specific response format instead. Ditch the generic data object and use a messages array with a platform-specific payload like this: { “fulfillment”: { “speech”: “Choose an option:”, “messages”: [ { “platform”: “telegram”, “type”: 0, “speech”: “Please select:” }, { “platform”: “telegram”, “type”: 4, “payload”: { “reply_markup”: { “keyboard”: [[“Option 1”], [“Option 2”]], “one_time_keyboard”: true, “resize_keyboard”: true } } } ] } } Important: The keyboard array needs arrays of strings, not just strings. This platform-specific format tells Dialogflow exactly how to handle Telegram and gets your custom keyboards working properly.

The problem is Dialogflow treats the data field as generic metadata, not actual response content for Telegram. When you put keyboard markup in data, Dialogflow just ignores it during Telegram integration. I hit this exact issue six months ago and wasted way too much time debugging it. You need to restructure your response using Dialogflow’s native message handling instead of shoving everything into fulfillment.data. Separate your text response from the keyboard response with proper message objects. Here’s the key: Telegram features like custom keyboards must be declared as platform payloads, not generic data attachments. Also check that your webhook endpoint is registered correctly in Dialogflow’s fulfillment settings - sometimes the webhook works fine for text but fails silently on rich responses because of config issues.