Receiving messages
TTMSFNCWhatsAppReceiver brings the messages people send to your WhatsApp
Business number into a Delphi application. It is a secure WebSocket client that
connects to a TTMSFNCWhatsAppServer — the
host of your webhook — receives the payloads the server forwards, and parses
each incoming WhatsApp message into a
TTMSFNCWhatsAppReceiverMessage object with the sender, the message type, and
the text, media, location, or contact details. Use it for a support desk that
shows incoming chats, for order intake over WhatsApp, or for any workflow that
should react to a customer's message. It runs in VCL and FMX applications and
in TMS WEB Core browser applications. This chapter covers connecting to the
server, how messages flow through the receiver's events, reading each message
type, and handling messages safely.
Connecting to the server
Set HostName,
Port, and — when the server uses
one — PathName to the address
of the TTMSFNCWhatsAppServer, then set Active to True or call Connect.
Because WhatsApp requires the server to run over TLS, the receiver always
connects securely; UseSSL is enabled and cannot be changed. In VCL and FMX
applications the OpenSSL libraries must therefore be deployed with the
application (see Secure connections).
procedure TForm1.ConnectBtnClick(Sender: TObject);
begin
// The receiver always connects over TLS (wss://).
TMSFNCWhatsAppReceiver1.HostName := 'hooks.example.com';
TMSFNCWhatsAppReceiver1.Port := 443;
TMSFNCWhatsAppReceiver1.PathName := 'whatsapp'; // the server's PathName, if any
TMSFNCWhatsAppReceiver1.Connect;
end;
procedure TForm1.TMSFNCWhatsAppReceiver1Connect(Sender: TObject;
AConnection: TTMSFNCWebSocketConnection);
begin
StatusLabel.Text := 'Receiving WhatsApp messages';
end;
procedure TForm1.TMSFNCWhatsAppReceiver1Disconnect(Sender: TObject;
AConnection: TTMSFNCWebSocketConnection);
begin
StatusLabel.Text := 'Disconnected';
end;
How incoming messages flow
Every message the server sends raises the generic event first, then the WhatsApp-specific one:
OnMessageReceivedfires for every text message, orOnBinaryDataReceivedfor every binary message. This includes both the webhook payloads the server forwards and messages the server sends itself withBroadcastMessageorSendMessageTo.- A text message is then parsed. When it is a WhatsApp payload of the
messagesfield that contains an incoming message,OnWhatsAppMessageReceivedfires with the parsed message inAMessage.
Anything else — status updates, payloads of other webhook fields, or your own server messages — raises only the generic event and is otherwise discarded.
Reading the message
TTMSFNCWhatsAppReceiverMessage
describes one incoming message. The common fields are always filled:
| Field | Contents |
|---|---|
From.Name |
The sender's WhatsApp profile name. |
From.Phone |
The sender's phone number. |
From.WhatsAppID |
The sender's WhatsApp ID. |
ID |
The message ID assigned by WhatsApp. |
TimeStamp |
The time the message was sent, as the Unix timestamp string WhatsApp delivers. |
IsReply |
True when the message replies to an earlier message. |
MessageType |
Which of the detail objects below carries the content. |
MessageType selects the detail object to read:
MessageType |
Details |
|---|---|
wamtText |
Text.Body |
wamtImage, wamtVideo |
Media.ID, Media.MimeType, Media.Checksum, Media.Caption |
wamtDocument |
Media.ID, Media.Filename, Media.MimeType, Media.Checksum |
wamtAudio, wamtSticker |
Media.ID, Media.MimeType, Media.Checksum |
wamtLocation |
Location.Latitude, Location.Longitude, Location.LocationName, Location.Address |
wamtContacts |
Contacts, a list of contact cards |
Media messages carry a media ID, not the file itself. Download the file through
the WhatsApp Cloud API media endpoint with that ID and your access token; the
SHA-256 value in Checksum lets you verify the download.
function TForm1.DescribeMessage(AMessage: TTMSFNCWhatsAppReceiverMessage): string;
begin
case AMessage.MessageType of
wamtText:
Result := AMessage.Text.Body;
wamtImage, wamtVideo:
Result := Format('%s (%s) caption: %s',
[AMessage.Media.ID, AMessage.Media.MimeType, AMessage.Media.Caption]);
wamtDocument:
Result := Format('Document %s, media ID %s', [AMessage.Media.Filename, AMessage.Media.ID]);
wamtAudio, wamtSticker:
Result := Format('Media ID %s (%s)', [AMessage.Media.ID, AMessage.Media.MimeType]);
wamtLocation:
Result := Format('%s, %s at %.5f, %.5f',
[AMessage.Location.LocationName, AMessage.Location.Address,
AMessage.Location.Latitude, AMessage.Location.Longitude]);
wamtContacts:
Result := Format('%d shared contact(s)', [AMessage.Contacts.Count]);
end;
if AMessage.IsReply then
Result := '[reply] ' + Result;
end;
Reading shared contacts
A contacts message holds one or more
TTMSFNCWhatsAppReceiverContact
cards. Each card has a ContactName (formatted, first, middle, and last name,
prefix, and suffix), a Birthday, an Organization (company, department, and
title), and lists of Phones, Emails, Addresses, and URLs. Phone entries
carry a PhoneType and, when the number uses WhatsApp, a WhatsAppID;
addresses, e-mail addresses, and URLs carry a home or work type.
procedure TForm1.ListContacts(AMessage: TTMSFNCWhatsAppReceiverMessage; ALines: TStrings);
var
Contact: TTMSFNCWhatsAppReceiverContact;
Phone: TTMSFNCWhatsAppReceiverContactPhone;
Email: TTMSFNCWhatsAppReceiverContactEmail;
begin
if AMessage.MessageType <> wamtContacts then
Exit;
for Contact in AMessage.Contacts do
begin
ALines.Add(Contact.ContactName.FormattedName);
if Contact.Organization.Company <> '' then
ALines.Add(' ' + Contact.Organization.Title + ', ' + Contact.Organization.Company);
for Phone in Contact.Phones do
// WhatsAppID is set when the number has a WhatsApp account.
ALines.Add(' Phone: ' + Phone.Phone);
for Email in Contact.Emails do
ALines.Add(' E-mail: ' + Email.Email);
end;
end;
Handling messages safely
Two rules apply to OnWhatsAppMessageReceived in VCL and FMX applications:
- The event runs on a background thread. Parsing happens on the thread that
reads the connection, so queue every user interface update with
TThread.QueueorTThread.Synchronize. The genericOnMessageReceived,OnConnect, andOnDisconnectevents follow the client'sAutoSyncEventssetting. AMessageis freed when the handler returns. Read the fields you need inside the handler, or keep a copy: create aTTMSFNCWhatsAppReceiverMessageand callAssign.ToJSONturns a message into JSON for storage.
Combining subscriptions, the message model, and safe handling
The example announces the business number the receiver is interested in as soon as it connects (a protocol handled by the server in Forwarding messages), copies every parsed message, and displays and stores the copy on the main thread.
procedure TForm1.TMSFNCWhatsAppReceiver1Connect(Sender: TObject;
AConnection: TTMSFNCWebSocketConnection);
begin
// A server-defined protocol message; see the server's forwarding guide.
TMSFNCWhatsAppReceiver1.Send('subscribe:' + FPhoneNumberID);
end;
procedure TForm1.TMSFNCWhatsAppReceiver1WhatsAppMessageReceived(Sender: TObject;
AMessage: TTMSFNCWhatsAppReceiverMessage);
var
Kept: TTMSFNCWhatsAppReceiverMessage;
begin
// AMessage is freed after this handler: keep a copy of the whole message.
Kept := TTMSFNCWhatsAppReceiverMessage.Create;
Kept.Assign(AMessage);
// This event runs on a background thread in VCL and FMX applications.
TThread.Queue(nil,
procedure
begin
try
Memo1.Lines.Add(Kept.From.Name + ': ' + DescribeMessage(Kept));
FInbox.Add(Kept.ID, Kept.ToJSON); // store the message as JSON
finally
Kept.Free;
end;
end);
end;
procedure TForm1.TMSFNCWhatsAppReceiver1MessageReceived(Sender: TObject;
AConnection: TTMSFNCWebSocketConnection; const AMessage: string);
begin
// Raised for every text message from the server, including the WhatsApp
// payloads above and messages the server sends itself.
if not AMessage.StartsWith('{') then
StatusLabel.Text := AMessage;
end;
Browser applications
The receiver is the one component of this product available in TMS WEB Core,
through the WEBLib.TMSFNCWhatsAppReceiver unit. It uses the browser's
WebSocket support, and the browser performs TLS, so no OpenSSL deployment is
needed. The differences from VCL and FMX:
| Area | VCL and FMX | TMS WEB Core |
|---|---|---|
| Lost connection | OnDisconnect does not fire when the connection drops abruptly; ping the server to detect it. |
OnDisconnect fires whenever the connection is lost. |
Ping, OnPing, OnPong |
Available (public). | Not available. |
Disconnect(False) |
Aborts without a close frame. | Not available; Disconnect always closes normally. |
AutoSyncEvents, ConnectTimeout |
Available (public). | Not available. |
| Event thread | OnWhatsAppMessageReceived runs on a background thread. |
All events run on the browser's single thread. |
Pitfalls
- Connect to the WhatsApp server, not to Meta. The receiver only talks to a
TTMSFNCWhatsAppServer; WhatsApp itself never opens WebSocket connections. - Deploy OpenSSL on the desktop. The connection is always secure, so VCL and FMX applications fail to connect without the OpenSSL libraries.
- Copy before you queue. A queued procedure that reads
AMessageafter the handler returned reads a freed object. - Not every payload becomes a message. Delivery and read receipts arrive
through
OnMessageReceivedonly. - Detect lost connections on the desktop. Use
PingandOnPong, as described in Connection health.
Related API
TTMSFNCWhatsAppReceiver,OnWhatsAppMessageReceived,TTMSFNCCustomWhatsAppMessageReceivedEventTTMSFNCWhatsAppReceiverMessage,TTMSFNCWhatsAppReceiverMessageFrom,TTMSFNCWhatsAppReceiverText,TTMSFNCWhatsAppReceiverMedia,TTMSFNCWhatsAppReceiverLocationTTMSFNCWhatsAppReceiverContact,TTMSFNCWhatsAppReceiverContactPhone,TTMSFNCWhatsAppReceiverContactEmail,TTMSFNCWhatsAppReceiverContactAddressHostName,Port,PathName,OnMessageReceived,OnBinaryDataReceived
See also
- Webhook setup — register the webhook the server hosts.
- Forwarding messages — what the server sends to receivers.
- Connecting — the client features the receiver builds on.