FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour Enviar mensajes por whatsapp?
Posts: 3358
Joined: Fri Oct 07, 2005 08:20 PM
Enviar mensajes por whatsapp?
Posted: Wed May 06, 2026 04:05 AM

Amigos foreros:

Necesito una clase para enviar mensajes por whatsapp, he buscado en el foro y parece que lo que hay aún no están totalmente maduras, me equivoco?

Alguna clase recomendada?

Saludos

SOI, s.a. de c.v.
estbucarm@gmail.com
http://www.soisa.mex.tl/
http://sqlcmd.blogspot.com/
Tel. (722) 174 44 45
Carpe diem quam minimum credula postero
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Enviar mensajes por whatsapp?
Posted: Wed May 06, 2026 04:18 AM

No equivocas. Foro samples (whatsapp.prg, whatsappw.prg) usan truco whatsapp:// + clipboard + SendKeys. Frágil:
requiere app desktop abierta, depende timing y foco ventana. No API real.

Tres caminos serios:

  1. WhatsApp Cloud API (Meta oficial) — REST HTTPS, gratis hasta cierto volumen. Requiere registrar número business +
    token. Mensajes plantilla aprobados para iniciar conversación. Maduro, soportado.

  2. Twilio / 360dialog / wati — gateways pagos sobre Cloud API. Más simple integrar, coste por mensaje.

  3. whatsapp-web.js / Baileys (no oficial, Node.js) — automatiza WhatsApp Web por websocket. Riesgo ban cuenta, no
    recomendable producción.

    Recomendación: clase TWhatsApp envolviendo Cloud API con WinHttp.WinHttpRequest.5.1 o hb_curl. Maduro, oficial, sin
    dependencia de app desktop. Tradeoff: necesitas Meta Business Account + número verificado + plantillas aprobadas para
    mensajes salientes a usuarios que no escribieron primero.

    Quieres prototipo TWhatsApp Cloud API? (construyendola...)

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Enviar mensajes por whatsapp?
Posted: Wed May 06, 2026 04:26 AM
// WhatsApp Cloud API client (Meta Business)
// Requires: Meta Business Account, App, registered phone number ID, permanent access token.
// Docs: https://developers.facebook.com/docs/whatsapp/cloud-api

#include "FiveWin.ch"
#include "hbcurl.ch"

//----------------------------------------------------------------------------//

CLASS TWhatsApp

   DATA   cToken                          // Bearer access token
   DATA   cPhoneId                        // sender phone number ID (Meta numeric ID, not the phone)
   DATA   cVersion    INIT "v18.0"        // Graph API version
   DATA   cBaseUrl    INIT "https://graph.facebook.com"
   DATA   hCurl
   DATA   cResponse
   DATA   nError      INIT 0
   DATA   nHttpCode   INIT 0

   METHOD New( cToken, cPhoneId, cVersion ) CONSTRUCTOR
   METHOD End()

   METHOD SendText( cTo, cMsg, lPreviewUrl )
   METHOD SendTemplate( cTo, cName, cLang, aBodyParams )
   METHOD SendImage( cTo, cLinkOrId, cCaption )
   METHOD SendDocument( cTo, cLinkOrId, cFilename, cCaption )
   METHOD SendAudio( cTo, cLinkOrId )
   METHOD SendVideo( cTo, cLinkOrId, cCaption )
   METHOD SendLocation( cTo, nLat, nLong, cName, cAddress )

   METHOD UploadMedia( cFileName, cMimeType )   // returns media id or nil
   METHOD DownloadMedia( cMediaId, cTargetFile )

   METHOD Post( cUrl, cJson )
   METHOD MessagesUrl()  INLINE ::cBaseUrl + "/" + ::cVersion + "/" + ::cPhoneId + "/messages"
   METHOD MediaUrl()     INLINE ::cBaseUrl + "/" + ::cVersion + "/" + ::cPhoneId + "/media"

   METHOD GetError()
   METHOD GetMessageId()

ENDCLASS

//----------------------------------------------------------------------------//

METHOD New( cToken, cPhoneId, cVersion ) CLASS TWhatsApp

   ::cToken   := cToken
   ::cPhoneId := cPhoneId
   if ! Empty( cVersion )
      ::cVersion := cVersion
   endif
   ::hCurl := curl_easy_init()

return Self

//----------------------------------------------------------------------------//

METHOD End() CLASS TWhatsApp

   if ::hCurl != nil
      curl_easy_cleanup( ::hCurl )
      ::hCurl := nil
   endif

return nil

//----------------------------------------------------------------------------//

METHOD Post( cUrl, cJson ) CLASS TWhatsApp

   local aHeaders

   curl_easy_reset( ::hCurl )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_URL, cUrl )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_POST, .T. )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_DL_BUFF_SETUP )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_SSL_VERIFYPEER, .T. )

   aHeaders := { "Authorization: Bearer " + ::cToken, ;
                 "Content-Type: application/json" }
   curl_easy_setopt( ::hCurl, HB_CURLOPT_HTTPHEADER, aHeaders )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_POSTFIELDS, cJson )

   ::nError = curl_easy_perform( ::hCurl )
   curl_easy_getinfo( ::hCurl, HB_CURLINFO_RESPONSE_CODE, @::nHttpCode )

   if ::nError == HB_CURLE_OK
      ::cResponse := curl_easy_dl_buff_get( ::hCurl )
   else
      ::cResponse := '{"error":{"message":"curl error ' + LTrim( Str( ::nError ) ) + '"}}'
   endif

return ::cResponse

//----------------------------------------------------------------------------//

METHOD SendText( cTo, cMsg, lPreviewUrl ) CLASS TWhatsApp

   local hReq := { => }, hText := { => }

   DEFAULT lPreviewUrl := .F.

   hReq[ "messaging_product" ] := "whatsapp"
   hReq[ "recipient_type" ]    := "individual"
   hReq[ "to" ]                := cTo
   hReq[ "type" ]              := "text"
   hText[ "preview_url" ]      := lPreviewUrl
   hText[ "body" ]             := cMsg
   hReq[ "text" ]              := hText

return ::Post( ::MessagesUrl(), hb_jsonEncode( hReq ) )

//----------------------------------------------------------------------------//

METHOD SendTemplate( cTo, cName, cLang, aBodyParams ) CLASS TWhatsApp

   local hReq := { => }, hTpl := { => }, hLang := { => }
   local aComponents, hComp, aParams, n

   DEFAULT cLang := "en_US"

   hReq[ "messaging_product" ] := "whatsapp"
   hReq[ "to" ]                := cTo
   hReq[ "type" ]              := "template"

   hLang[ "code" ] := cLang
   hTpl[ "name" ]     := cName
   hTpl[ "language" ] := hLang

   if HB_ISARRAY( aBodyParams ) .and. Len( aBodyParams ) > 0
      aParams := {}
      for n := 1 to Len( aBodyParams )
         AAdd( aParams, { "type" => "text", "text" => aBodyParams[ n ] } )
      next
      hComp := { "type" => "body", "parameters" => aParams }
      aComponents := { hComp }
      hTpl[ "components" ] := aComponents
   endif

   hReq[ "template" ] := hTpl

return ::Post( ::MessagesUrl(), hb_jsonEncode( hReq ) )

//----------------------------------------------------------------------------//

METHOD SendImage( cTo, cLinkOrId, cCaption ) CLASS TWhatsApp

   local hReq := { => }, hImg := { => }

   hReq[ "messaging_product" ] := "whatsapp"
   hReq[ "to" ]                := cTo
   hReq[ "type" ]              := "image"

   if Left( Lower( cLinkOrId ), 4 ) == "http"
      hImg[ "link" ] := cLinkOrId
   else
      hImg[ "id" ] := cLinkOrId
   endif
   if ! Empty( cCaption )
      hImg[ "caption" ] := cCaption
   endif
   hReq[ "image" ] := hImg

return ::Post( ::MessagesUrl(), hb_jsonEncode( hReq ) )

//----------------------------------------------------------------------------//

METHOD SendDocument( cTo, cLinkOrId, cFilename, cCaption ) CLASS TWhatsApp

   local hReq := { => }, hDoc := { => }

   hReq[ "messaging_product" ] := "whatsapp"
   hReq[ "to" ]                := cTo
   hReq[ "type" ]              := "document"

   if Left( Lower( cLinkOrId ), 4 ) == "http"
      hDoc[ "link" ] := cLinkOrId
   else
      hDoc[ "id" ] := cLinkOrId
   endif
   if ! Empty( cFilename )
      hDoc[ "filename" ] := cFilename
   endif
   if ! Empty( cCaption )
      hDoc[ "caption" ] := cCaption
   endif
   hReq[ "document" ] := hDoc

return ::Post( ::MessagesUrl(), hb_jsonEncode( hReq ) )

//----------------------------------------------------------------------------//

METHOD SendAudio( cTo, cLinkOrId ) CLASS TWhatsApp

   local hReq := { => }, hAudio := { => }

   hReq[ "messaging_product" ] := "whatsapp"
   hReq[ "to" ]                := cTo
   hReq[ "type" ]              := "audio"

   if Left( Lower( cLinkOrId ), 4 ) == "http"
      hAudio[ "link" ] := cLinkOrId
   else
      hAudio[ "id" ] := cLinkOrId
   endif
   hReq[ "audio" ] := hAudio

return ::Post( ::MessagesUrl(), hb_jsonEncode( hReq ) )

//----------------------------------------------------------------------------//

METHOD SendVideo( cTo, cLinkOrId, cCaption ) CLASS TWhatsApp

   local hReq := { => }, hVideo := { => }

   hReq[ "messaging_product" ] := "whatsapp"
   hReq[ "to" ]                := cTo
   hReq[ "type" ]              := "video"

   if Left( Lower( cLinkOrId ), 4 ) == "http"
      hVideo[ "link" ] := cLinkOrId
   else
      hVideo[ "id" ] := cLinkOrId
   endif
   if ! Empty( cCaption )
      hVideo[ "caption" ] := cCaption
   endif
   hReq[ "video" ] := hVideo

return ::Post( ::MessagesUrl(), hb_jsonEncode( hReq ) )

//----------------------------------------------------------------------------//

METHOD SendLocation( cTo, nLat, nLong, cName, cAddress ) CLASS TWhatsApp

   local hReq := { => }, hLoc := { => }

   hReq[ "messaging_product" ] := "whatsapp"
   hReq[ "to" ]                := cTo
   hReq[ "type" ]              := "location"

   hLoc[ "latitude" ]  := nLat
   hLoc[ "longitude" ] := nLong
   if ! Empty( cName )
      hLoc[ "name" ] := cName
   endif
   if ! Empty( cAddress )
      hLoc[ "address" ] := cAddress
   endif
   hReq[ "location" ] := hLoc

return ::Post( ::MessagesUrl(), hb_jsonEncode( hReq ) )

//----------------------------------------------------------------------------//

METHOD UploadMedia( cFileName, cMimeType ) CLASS TWhatsApp

   local aHeaders, hResp, cMediaId

   if ! File( cFileName )
      ::cResponse := '{"error":{"message":"file not found"}}'
      return nil
   endif

   DEFAULT cMimeType := "application/octet-stream"

   curl_easy_reset( ::hCurl )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_URL, ::MediaUrl() )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_DL_BUFF_SETUP )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_SSL_VERIFYPEER, .T. )

   aHeaders := { "Authorization: Bearer " + ::cToken }
   curl_easy_setopt( ::hCurl, HB_CURLOPT_HTTPHEADER, aHeaders )

   curl_easy_setopt( ::hCurl, HB_CURLOPT_HTTPPOST, { ;
      { "name" => "messaging_product", "contents" => "whatsapp" }, ;
      { "name" => "type",              "contents" => cMimeType }, ;
      { "name" => "file",              "file" => cFileName, "type" => cMimeType } } )

   ::nError = curl_easy_perform( ::hCurl )
   curl_easy_getinfo( ::hCurl, HB_CURLINFO_RESPONSE_CODE, @::nHttpCode )

   if ::nError == HB_CURLE_OK
      ::cResponse := curl_easy_dl_buff_get( ::hCurl )
      hb_jsonDecode( ::cResponse, @hResp )
      if HB_ISHASH( hResp ) .and. hb_HHasKey( hResp, "id" )
         cMediaId := hResp[ "id" ]
      endif
   else
      ::cResponse := '{"error":{"message":"curl error ' + LTrim( Str( ::nError ) ) + '"}}'
   endif

return cMediaId

//----------------------------------------------------------------------------//

METHOD DownloadMedia( cMediaId, cTargetFile ) CLASS TWhatsApp

   local aHeaders, hResp, cUrl, cBin, hFile

   curl_easy_reset( ::hCurl )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_URL, ::cBaseUrl + "/" + ::cVersion + "/" + cMediaId )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_DL_BUFF_SETUP )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_SSL_VERIFYPEER, .T. )
   aHeaders := { "Authorization: Bearer " + ::cToken }
   curl_easy_setopt( ::hCurl, HB_CURLOPT_HTTPHEADER, aHeaders )
   ::nError := curl_easy_perform( ::hCurl )
   if ::nError != HB_CURLE_OK
      return .F.
   endif
   ::cResponse := curl_easy_dl_buff_get( ::hCurl )
   hb_jsonDecode( ::cResponse, @hResp )
   if ! HB_ISHASH( hResp ) .or. ! hb_HHasKey( hResp, "url" )
      return .F.
   endif
   cUrl := hResp[ "url" ]

   curl_easy_reset( ::hCurl )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_URL, cUrl )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_DL_BUFF_SETUP )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_SSL_VERIFYPEER, .T. )
   curl_easy_setopt( ::hCurl, HB_CURLOPT_HTTPHEADER, { "Authorization: Bearer " + ::cToken } )
   ::nError := curl_easy_perform( ::hCurl )
   if ::nError != HB_CURLE_OK
      return .F.
   endif
   cBin := curl_easy_dl_buff_get( ::hCurl )

   hFile := FCreate( cTargetFile )
   if FError() != 0
      return .F.
   endif
   FWrite( hFile, cBin )
   FClose( hFile )

return .T.

//----------------------------------------------------------------------------//

METHOD GetError() CLASS TWhatsApp

   local hResp, cMsg := ""

   if Empty( ::cResponse )
      return ""
   endif
   hb_jsonDecode( ::cResponse, @hResp )
   if HB_ISHASH( hResp ) .and. hb_HHasKey( hResp, "error" )
      if HB_ISHASH( hResp[ "error" ] ) .and. hb_HHasKey( hResp[ "error" ], "message" )
         cMsg := hResp[ "error" ][ "message" ]
      endif
   endif

return cMsg

//----------------------------------------------------------------------------//

METHOD GetMessageId() CLASS TWhatsApp

   local hResp, cId := ""

   if Empty( ::cResponse )
      return ""
   endif
   hb_jsonDecode( ::cResponse, @hResp )
   if HB_ISHASH( hResp ) .and. hb_HHasKey( hResp, "messages" )
      if HB_ISARRAY( hResp[ "messages" ] ) .and. Len( hResp[ "messages" ] ) > 0
         if hb_HHasKey( hResp[ "messages" ][ 1 ], "id" )
            cId := hResp[ "messages" ][ 1 ][ "id" ]
         endif
      endif
   endif

return cId

//----------------------------------------------------------------------------//
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Enviar mensajes por whatsapp?
Posted: Wed May 06, 2026 04:28 AM

Pregunta: ¿es gratuito?

Cloud API tiene tier gratis pero limitado. Modelo Meta cambió 2025-07-01:

Free tier:

  • 1.000 conversaciones/mes con service messages (responder dentro de ventana 24h tras mensaje del usuario). Gratis.

  • Marketing/utility/authentication templates: NO gratis. Pago por mensaje según país.

    Tarifas template (España, ejemplo):

  • Marketing: 0,0691 €/msg

  • Utility: 0,0287 €/msg (gratis si dentro ventana servicio 24h desde 2025-11)

  • Authentication: 0,0287 €/msg

    Otros países más baratos (India 0,008€, México 0,02€). Tarifa exacta:
    https://developers.facebook.com/docs/whatsapp/pricing

    Costes infra: cero. Sin cuota mensual Meta. Solo pagas mensajes salientes iniciados por business.

    Recibir mensajes: siempre gratis.

    Resumen: desarrollo + pruebas con números allowlist = gratis. Producción: gratis para responder usuarios (24h
    ventana), pago por iniciar conversación con templates.

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Enviar mensajes por whatsapp?
Posted: Wed May 06, 2026 04:30 AM

como hago para probarla ?

● Pasos para probar (orden importa):

  1. Crear app Meta

  2. https://developers.facebook.com/apps → "Create App" → tipo "Business"

  3. Add product → WhatsApp → "Set up"

  4. Obtener credenciales

    En panel WhatsApp → "API Setup":

  5. Temporary access token (24h, suficiente para probar) — cópialo

  6. Phone number ID del número de prueba que Meta da gratis — cópialo

  7. Token permanente luego: Business Settings → System Users → genera token con permiso whatsapp_business_messaging +
    whatsapp_business_management

  8. Registrar tu móvil como destino test

    En "API Setup" → sección "To" → "Manage phone number list" → añade tu móvil con código país (ej. +34666...). Meta
    envía SMS con código verificación.

  9. Editar sample

    c:\fwteam\samples\misc\twhatsapp.prg líneas 18-20:
    static cToken := "EAAxxxxxxxxxxxxxxxx" // pega token aquí
    static cPhoneId := "123456789012345" // pega phone number ID
    static cTo := "34666555444" // tu móvil verificado, sin +

  10. Compilar

    cd /d C:\fwteam\samples\misc
    C:\fwteam\samples\build_new.bat twhatsapp hb64

    (o variante: hb32, hm64, hg64, etc.)

  11. Ejecutar

    build_new.bat lanza exe automático. Pulsa botón "Template" primero — único que funciona sin que destinatario te haya
    escrito antes (template hello_world viene preaprobado por Meta).

  12. Verificar

  13. Tu móvil recibe mensaje "Hello World" en WhatsApp

  14. MsgInfo muestra HTTP 200 + id: wamid.xxxxx

  15. Si falla: ver error: en MsgInfo (token caducado, número no en allowlist, etc.)

  16. Después de hello_world

    Una vez tu móvil responde algo al business, ventana 24h abierta → botones Text/Image/Document/Location funcionan
    (mensajes sesión, gratis).

    Errores comunes:

  17. (#131030) Recipient phone number not in allowed list → falta verificar móvil en allowlist

  18. (#190) Access token expired → regenera token (temporal dura 24h)

  19. (#132001) Template name does not exist → template no aprobado en tu idioma

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Enviar mensajes por whatsapp?
Posted: Wed May 06, 2026 04:32 AM
// Sample for TWhatsApp class — WhatsApp Cloud API (Meta Business)
//
// Setup steps (one-time):
//   1. Create Meta Business Account: https://business.facebook.com/
//   2. Create app at https://developers.facebook.com/apps/ (type: Business)
//   3. Add WhatsApp product, register a phone number, get PHONE_NUMBER_ID
//   4. Generate permanent access token (System User)
//   5. Add destination phone numbers to "Allowed list" (sandbox) or
//      get number out of sandbox by submitting business verification
//   6. For unsolicited messages create and approve a message template

#include "FiveWin.ch"

#define CRLF Chr(13)+Chr(10)

static cToken   := "EAAxxxxxxxxxxxxxxxx"      // Bearer token
static cPhoneId := "123456789012345"          // sender phone number ID
static cTo      := "34666555444"              // recipient (E.164 without +)

//----------------------------------------------------------------------------//

function Main()

   local oWnd, oWa

   oWa := TWhatsApp():New( cToken, cPhoneId )

   DEFINE WINDOW oWnd TITLE "TWhatsApp test"

   ACTIVATE WINDOW oWnd ON INIT BuildMenu( oWnd, oWa ) ;
                       VALID ( oWa:End(), .T. )

return nil

//----------------------------------------------------------------------------//

static function BuildMenu( oWnd, oWa )

   local oBar

   DEFINE BUTTONBAR oBar OF oWnd SIZE 60, 60 2007

   DEFINE BUTTON OF oBar PROMPT "Text"        ACTION TestText( oWa )
   DEFINE BUTTON OF oBar PROMPT "Template"    ACTION TestTemplate( oWa )
   DEFINE BUTTON OF oBar PROMPT "Image URL"   ACTION TestImageUrl( oWa )
   DEFINE BUTTON OF oBar PROMPT "Upload+Img"  ACTION TestUploadImage( oWa )
   DEFINE BUTTON OF oBar PROMPT "Document"    ACTION TestDocument( oWa )
   DEFINE BUTTON OF oBar PROMPT "Location"    ACTION TestLocation( oWa )

return nil

//----------------------------------------------------------------------------//

static function TestText( oWa )

   oWa:SendText( cTo, "Hello from FiveWin via Cloud API " + Time() )
   ShowResult( oWa )

return nil

//----------------------------------------------------------------------------//

static function TestTemplate( oWa )

   // hello_world is the default template enabled on every new app
   oWa:SendTemplate( cTo, "hello_world", "en_US" )
   ShowResult( oWa )

return nil

//----------------------------------------------------------------------------//

static function TestImageUrl( oWa )

   oWa:SendImage( cTo, ;
      "https://www.fivetechsoft.com/images/fivewin_logo.png", ;
      "FiveWin logo" )
   ShowResult( oWa )

return nil

//----------------------------------------------------------------------------//

static function TestUploadImage( oWa )

   local cFile := "..\bitmaps\007.bmp"
   local cId

   if ! File( cFile )
      MsgAlert( "File not found: " + cFile )
      return nil
   endif

   cId := oWa:UploadMedia( cFile, "image/bmp" )
   if Empty( cId )
      MsgAlert( oWa:GetError(), "Upload failed" )
      return nil
   endif
   oWa:SendImage( cTo, cId, "uploaded local file" )
   ShowResult( oWa )

return nil

//----------------------------------------------------------------------------//

static function TestDocument( oWa )

   oWa:SendDocument( cTo, ;
      "https://www.fivetechsoft.com/files/fwh_releases.pdf", ;
      "release_notes.pdf", "FWH release notes" )
   ShowResult( oWa )

return nil

//----------------------------------------------------------------------------//

static function TestLocation( oWa )

   oWa:SendLocation( cTo, 36.5108, -6.2693, "FiveTech HQ", "Cadiz, Spain" )
   ShowResult( oWa )

return nil

//----------------------------------------------------------------------------//

static function ShowResult( oWa )

   local cInfo

   cInfo := "HTTP " + LTrim( Str( oWa:nHttpCode ) ) + CRLF + ;
           "id: " + oWa:GetMessageId() + CRLF + ;
           "error: " + oWa:GetError() + CRLF + CRLF + ;
           "raw: " + oWa:cResponse

   MsgInfo( cInfo, "WhatsApp" )

return nil

//----------------------------------------------------------------------------//
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Enviar mensajes por whatsapp?
Posted: Wed May 06, 2026 04:33 AM

Añadido al próximo build de FWH

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Enviar mensajes por whatsapp?
Posted: Wed May 06, 2026 04:34 AM

Esto son claros ejemplos de la importancia de usar Claude Code :wink:

Invitaciones gratuitas para probar Claude Code:
https://claude.ai/referral/9MBf7GAUAQ

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 3358
Joined: Fri Oct 07, 2005 08:20 PM
Re: Enviar mensajes por whatsapp?
Posted: Thu May 07, 2026 02:36 AM

Maestro Linares:

Gracias por las respuestas.

Debí comentar que mi necesidad es para una aplicación que me solicita un médico y la idea es enviar mensajes por whatsapp cuando se haya registrado/pospuesto/cancelado alguna cita.

La cantidad de mensajes no será muy alto.

Saludos

SOI, s.a. de c.v.
estbucarm@gmail.com
http://www.soisa.mex.tl/
http://sqlcmd.blogspot.com/
Tel. (722) 174 44 45
Carpe diem quam minimum credula postero
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Enviar mensajes por whatsapp?
Posted: Thu May 07, 2026 02:42 AM

Perfecto encaje para Cloud API. Caso de uso ideal: utility templates (notificaciones transaccionales).

Por qué encaja bien:

  • Notificaciones de cita = categoría utility (no marketing). Tarifa más baja (0,0287€/msg España).

  • Desde nov 2025: utility messages gratis si paciente escribió al business en últimas 24h. Si no, pago por template.

  • Volumen bajo → cabe sobrado en límite gratuito (1.000 conversaciones/mes service).

  • Coste estimado consulta típica (300 citas/mes notificadas): 8,60€/mes worst case, mucho menos si pacientes
    confirman.

    Plantillas a aprobar (Meta):

    Crea 3 templates en Business Manager → WhatsApp Manager → Message Templates → Categoría: Utility:

    Nombre: cita_confirmada
    Idioma: es
    Body: Hola {{1}}, su cita con Dr. {{2}} queda confirmada para {{3}} a las {{4}}h.
    Responda CANCELAR para anularla.

    Nombre: cita_pospuesta
    Idioma: es
    Body: Hola {{1}}, su cita con Dr. {{2}} se ha movido del {{3}} al {{4}} a las {{5}}h.

    Nombre: cita_cancelada
    Idioma: es
    Body: Hola {{1}}, su cita del {{2}} con Dr. {{3}} ha sido cancelada.
    Para reagendar llame al {{4}}.

    Aprobación Meta: 1-24h normalmente. Plantilla utility se aprueba rápido si texto neutro/informativo.

    Uso en código:

    local oWa := TWhatsApp():New( cToken, cPhoneId )

    // confirmación
    oWa:SendTemplate( "34666555444", "cita_confirmada", "es", ;
    { "María García", "López", "lunes 12 mayo", "10:30" } )

    // pospuesta
    oWa:SendTemplate( "34666555444", "cita_pospuesta", "es", ;
    { "María García", "López", "12/05", "19/05", "10:30" } )

    // cancelada
    oWa:SendTemplate( "34666555444", "cita_cancelada", "es", ;
    { "María García", "12/05", "López", "956123456" } )

    Datos médicos / GDPR:

  • Meta Cloud API es procesador GDPR. Firma DPA en Business Settings.

  • Mensaje WhatsApp NO es canal médico seguro oficial. Mejor solo: nombre, fecha, hora, médico. Nunca diagnóstico,
    medicación, resultados.

  • Consentimiento explícito del paciente para notificaciones WhatsApp (firmar al alta).

    Webhook entrante (opcional pero útil):

    Si paciente responde "CANCELAR", recibes webhook. Necesitas endpoint HTTPS público. Para empezar puedes ignorarlo y
    usar solo salida.

    Stack mínimo para empezar:

  • App Meta + número test gratis → probar hello_world

  • Crear las 3 plantillas utility → esperar aprobación

  • Verificar negocio (Business Verification) → necesario para producción y para sacar número del sandbox

  • Comprar número WhatsApp Business (puedes usar línea fija/móvil propia, Meta hace verificación SMS/llamada)

  • Token permanente System User

  • Integrar TWhatsApp en módulo de citas: hook al guardar/posponer/cancelar registro → llama al template
    correspondiente

    Quieres que añada métodos helper tipo SendAppointment( cTo, cPatient, cDoctor, dDate, cTime, cAction ) que envuelva
    los 3 templates?

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Enviar mensajes por whatsapp?
Posted: Thu May 07, 2026 02:43 AM

Puedes usar la versión de prueba de Claude Code (enlace que he publicado) y decirle que te lo haga el todo :idea:

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 410
Joined: Sun Jan 31, 2010 03:30 PM
Re: Enviar mensajes por whatsapp?
Posted: Sun May 10, 2026 09:23 PM

Buena tarde...

Alguien ya hizo la validación y publicación de la APP como desarrollador meta o Facebook,
como uno de los requisitos para empezar a usar la API en producción ?

Compartamos ideas ...

Nota : 1 . Ya tengo interface con api meta usando un BSP - onurix, funcionando: ok

  1. Estoy medio varado en la publicación de la app con api meta directamente.

  2. CC me creo un server python, para multiples funciones: convertir informes legacy a tipo web, crear y enviar PDF via WhatsApp o email, api rest / websocket- SQL, etc..

  3. Todo usando fivewin + harbour

JONSSON RUSSI

Continue the discussion