En esta publicación veremos cómo responder de forma sistemática a mensajes de WhatsApp usando la API de OpenAI. En la entrega anterior aprendimos a recibir y enviar mensajes con WhatsApp. Ahora ajustaremos ligeramente ese código para incorporar respuestas de OpenAI, con y sin contexto. Por simplicidad, comenzaremos por la variante sin contexto.
Enviando mensajes sin contexto…
En la Parte 2 de esta serie, expusimos el siguiente bloque de código:
foreach ($payload['entry'] as $entry) {
foreach ($entry['changes'] ?? [] as $change) {
if (($change['field'] ?? '') !== 'messages') continue;
$v = $change['value'] ?? [];
foreach ($v['messages'] ?? [] as $msg) {
$mid = $msg['id'] ?? null; // message id (wamid-...)
$from = $msg['from'] ?? null; // wa_id del cliente (E.164 sin '+')
$type = $msg['type'] ?? 'text';
// mensaje del cliente
$text = ($type === 'text') ? (string)($msg['text']['body'] ?? '') : '';
}
}
}
Como explicamos en dicha publicación, el mensaje del usuario estará almacenado en la variable $text. Puesto que el payload que envía Meta puede contener múltiples mensajes, es conveniente insertar nuestra respuesta dentro de los ciclos. Por este motivo, incluiremos las siguientes líneas justo bajo la variable $text:
$payload = [
'model' => 'gpt-5-mini',
'text' => $text,
'reasoning' => ['effort' => 'low'],
'instructions' => $instructions,
'max_output_tokens' => 1000,
];
En este payload, que es el que enviaremos a OpenAI, incluimos el texto que el usuario nos envió. Además, explicitamos la clave ‘instructions’, que contiene una variable tipo string que define cómo se comportará nuestro Asistente con el cliente. En la práctica, hay libertad absoluta para elegir este texto. No obstante, para efectos de esta publicación, será: $instructions = «Ten una conversación amable y amena con el usuario.» Luego, enviamos el siguiente request a responses de OpenAI:
$ch = curl_init('https://api.openai.com/v1/responses');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $openaiApiKey,
],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 120,
]);
$res = curl_exec($ch);
Donde $openaiApiKey es un string que contiene la API key de OpenAI y $res es el resultado de nuestra consulta. En dicha variable, el texto generado por OpenAI puede ser obtenido accediendo a la siguiente ruta:
$texto = $res['output'][0]['content'][0]['text'];
Finalmente, utilizando la función callWhatsApp de la Parte 2, llamamos la API de Meta con el texto extraído para devolverlo al usuario.
Enviando mensajes con contexto…
Si queremos añadir contexto, tenemos dos opciones: la primera es utilizar conversations de OpenAI y la segunda es enviar el chat completo a la API antes de generar una respuesta nueva. En esta entrada expondremos la primera alternativa, debido a que la segunda es trivial. Dicho esto, primero debemos crear una conversación:
$ch = curl_init('https://api.openai.com/v1/conversations');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $openaiApiKey,
],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
]);
$res = curl_exec($ch);
$http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($res && $http >= 200 && $http < 300) {
$j = json_decode($res, true);
if (is_array($j) && !empty($j['id'])) {
$convId = (string)$j['id'];
}
}
Este bloque debe ejecutarse en un archivo separado —no en webhook.php—. Si lo dejáramos en el webhook, cada llamada de Meta crearía una conversación nueva y, por tanto, el chatbot no tendría memoria. Por ello, crea la conversación en un script independiente, toma el identificador $convid y guárdalo en webhook.php. Finalmente, incluye el conversation_id en el payload que enviarás a OpenAI:
$payload = [
'model' => 'gpt-5-mini',
'conversation' => $convId,
'text' => $text,
'reasoning' => ['effort' => 'low'],
'instructions' => $instructions,
'max_output_tokens' => 1000,
];
Luego, debemos llevar a cabo el mismo request que en el apartado anterior; así obtendremos una respuesta con memoria.
José Miguel Muñoz Urra – jmunozu@pulki.es