Я переписал ваш код, используя объект данных PHP вместо MySQLi. При создании любого запроса из пользовательского ввода вы должны параметризировать их, а не передавать их непосредственно в строку (в комментарии упоминается, что ваш скрипт уязвим для SQL-инъекций).
Идентификатор не возвращал значение, потому что вы выбиралиэто до того, как вы выполнили оператор вставки. Перемещение первой вставки перед вызовом для получения последнего идентификатора должно исправить эту проблему.
Цикл foreach работал только для одной записи, поскольку вы перезаписали переменную $ sql в цикле, а затем выполнили ее после.
Перемещение выполнения этого запроса в цикл будет запускать запрос каждый раз.
$connection = new PDO('mysql:host=' . $host_name . ';dbname=' . $database_name, $host_user, $host_password);
$json = json_decode(file_get_contents('php://input'), true);
$recipients = array(1000005,1000006,1000007);
$query = "INSERT INTO messages(message_content, message_subject, from_id) values(:content, :subject, :from_id)";
$params = [
'content' => $json['message_content'],
'subject' => $json['message_subject'],
'from_id' => $json['from_id']
];
$statement = $connection->prepare($query)->execute($params);
$message_id = $connection->lastInsertId();;
foreach ($recipients as $recepient){
$sql = "INSERT INTO recipients(message_id, user_id, message_content, message_subject, from_id) values(:message_id, :recepient, :message_content, :message_subject, :from_id)";
$params = [
'message_id' => $message_id,
'recepient' => $recepient,
'message_content' => $json['message_content'],
'message_subject' => $json['message_subject'],
'from_id' => $json['from_id'],
];
$sql_result = $connection->prepare($sql)->execute($params);
}