PHP Telegram Bot Inline Keyboard Implementation Issue

I’m working on a Telegram bot using PHP and having trouble with inline keyboards. My regular keyboard setup works perfectly fine:

$menu = [
    'keyboard' => [['Option A'], ['Option B']],
    'resize_keyboard' => true,
    'one_time_keyboard' => true,
    'selective' => true
];

$menu = json_encode($menu, true);

$url = BOT_API."sendmessage?chat_id=".$userID."&text=".$message."&parse_mode=HTML&reply_markup=$menu";

But when I try to create an inline keyboard, nothing shows up. I’ve tried different approaches:

$menu = [
    'inline_keyboard' => [['text' => 'Click Me', 'callback_data' => 'button_clicked']]
];

I also attempted this structure:

$menu = [
    'inline_keyboard' => [['text' => 'Click Me'], ['callback_data' => 'button_clicked']]
];

Neither approach displays the inline keyboard. Can someone help me understand what I’m doing wrong with the inline keyboard structure? I really need this functionality for my bot project.

Dan’s structure works, but you’re missing something that’ll cause problems.

You need to handle callback queries when users click inline buttons. Telegram sends callback data to a different endpoint than regular messages.

Here’s the flow - user clicks button, Telegram sends callback query, your bot processes it AND answers the callback query. Skip that last part and users get stuck with loading spinners.

// After sending inline keyboard
if (isset($update['callback_query'])) {
    $callback_data = $update['callback_query']['data'];
    // Process your callback_data
    
    // Must answer callback query
    file_get_contents(BOT_API."answerCallbackQuery?callback_query_id=".$update['callback_query']['id']);
}

Callback handling gets messy with multiple buttons and complex flows. I’ve built tons of Telegram bots and always end up with spaghetti code managing different callback states.

Latenode handles this with visual workflows. Set up Telegram triggers, process callbacks, update messages - no wrestling with PHP arrays and webhook headaches. Callback handling becomes drag and drop instead of debugging nested arrays.

Had this exact frustration building my first Telegram bot. The array structure drove me crazy until I figured out inline keyboards need proper nesting for rows and columns. Your issue is how you’re organizing the button arrays. Each row needs to be an array with button objects:

$menu = [
    'inline_keyboard' => [
        [['text' => 'Button 1', 'callback_data' => 'btn1']], // First row
        [['text' => 'Button 2', 'callback_data' => 'btn2']]  // Second row
    ]
];

Multiple buttons on the same row:

$menu = [
    'inline_keyboard' => [
        [
            ['text' => 'Yes', 'callback_data' => 'yes'],
            ['text' => 'No', 'callback_data' => 'no']
        ]
    ]
];

Also, make sure you JSON encode before sending to the Telegram API. I spent hours debugging once because I forgot that step and the buttons wouldn’t show up at all.

your first inline keyboard was almost right - you just mixed up the array structure. try this:

$menu = [
    'inline_keyboard' => [[['text' => 'Click Me', 'callback_data' => 'button_clicked']]]
];

see that extra nested array? each button needs its own array wrapper, even solo buttons.

Yeah, the nested array structure others mentioned is right, but there’s another issue that might be killing your buttons. Check if you’re encoding the reply_markup parameter correctly in your URL. GET requests with sendMessage can break when there’s special characters in the JSON. I hit this exact problem and switching to POST fixed it instantly. Stop building URLs with parameters and send the data as POST instead: ```php
$data = [
‘chat_id’ => $userID,
‘text’ => $message,
‘parse_mode’ => ‘HTML’,
‘reply_markup’ => json_encode($menu)
];

$options = [
‘http’ => [
‘header’ => “Content-type: application/x-www-form-urlencoded\r\n”,
‘method’ => ‘POST’,
‘content’ => http_build_query($data)
]
];

file_get_contents(BOT_API.“sendMessage”, false, stream_context_create($options));


This fixes the JSON encoding issues that make inline keyboards fail silently with GET requests.