Table of Contents

TMS FNC Cloud WhatsApp Guides

TTMSFNCCloudWhatsApp sends and receives messages through the WhatsApp Business Cloud API. Every send goes through the same shape: a TTMSFNCCloudWhatsAppMessage with a SendTo number, a MessageType, and the payload object that matches that type — Text, Media, Location, Contacts or Template. Convenience methods (SendTextMessage, SendImage, SendDocument, SendLocation, SendContacts, SendTemplate) build that message for you; SendMessage takes one you built yourself when you need full control. This page covers authentication, sending a message, template messages, and combining the two.

Authentication

Set Authentication.Key to the permanent access token of your Meta app, and PhoneNumberID to the ID of the business phone number that sends the messages — both come from the WhatsApp Business account in the Meta developer console. Every request carries them, so set both before the first send. Outcomes are reported through OnSendSuccess (the sent message, with the service-assigned ID filled in) and OnError (the provider's error text); media uploads report through OnUploadSuccess and downloads through OnDownloadFileSuccess.

Sending a message

SendWhatsAppMessage is an alias for SendMessage — the same method under a second, unambiguous name, which matters because forms and data modules frequently already have their own SendMessage. Either name accepts a message you built yourself, which is the route to take when you need a property the convenience methods do not expose, such as ReplyToID for threading a reply onto an inbound message.

procedure TForm1.SendGreeting(const ANumber: string);
var
  Msg: TTMSFNCCloudWhatsAppMessage;
begin
  TMSFNCCloudWhatsApp1.Authentication.Key := 'your-permanent-access-token';
  TMSFNCCloudWhatsApp1.PhoneNumberID := 'your-phone-number-id';
  TMSFNCCloudWhatsApp1.OnSendSuccess := WhatsAppSent;
  TMSFNCCloudWhatsApp1.OnError := WhatsAppError;

  Msg := TTMSFNCCloudWhatsAppMessage.Create;
  Msg.SendTo := ANumber;                 // Recipient in international format.
  Msg.MessageType := mtText;
  Msg.Text.Body := 'Your order has shipped.';
  Msg.Text.PreviewURL := False;          // True renders a link preview.

  // SendWhatsAppMessage is an alias for SendMessage - identical behaviour,
  // but an unambiguous name in a form that also has its own SendMessage.
  // Do NOT free Msg: the component takes ownership of it from here on.
  TMSFNCCloudWhatsApp1.SendWhatsAppMessage(Msg);
end;

procedure TForm1.WhatsAppSent(Sender: TObject; AMessage: TTMSFNCCloudWhatsAppMessage);
begin
  // ID is filled in from the service response and the message is kept in the
  // component's Messages collection, so it can be replied to later.
  ShowMessage('Sent, message id ' + AMessage.ID);
end;

procedure TForm1.WhatsAppError(Sender: TObject; AErrorMessage: string);
begin
  ShowMessage('WhatsApp error: ' + AErrorMessage);
end;
Important

The component takes ownership of a message passed to SendMessage or SendWhatsAppMessage: on success it is added to the Messages collection (which frees its items), and on failure it is freed immediately. Never free a message you handed to either method, and do not keep the reference after the call.

Template messages

Outside a 24-hour customer service window, WhatsApp only accepts a template message — a layout pre-registered and approved in your WhatsApp Business account, with placeholders your code fills in. This is the mechanism to use for order confirmations, appointment reminders and one-time codes.

Three pieces make one up:

Type Purpose
TTMSFNCCloudWhatsAppTemplate Name and LanguageCode identify the approved template; Components holds its parts
TTMSFNCCloudWhatsAppTemplateComponent One part of the template — ComponentType is tctHeader, tctBody or tctButton; a button also needs its zero-based ButtonIndex
TTMSFNCCloudWhatsAppTemplateParameter One placeholder value, added through the Parameters helpers

Add parameters in the order the template's placeholders ({{1}}, {{2}}, …) appear, using the helper that matches the type the template expects:

Helper Parameter type Use for
AddText tptText a plain text substitution
AddMedia tptImage / tptDocument / tptVideo a header image, document or video, by media ID or public link
AddCurrency tptCurrency an amount — the code, the value in thousandths (49900 for 49.90), and a fallback string
AddDateTime tptDateTime a date, with a fallback string
AddPayload tptPayload the payload a quick-reply button posts back

SendTemplate is the short route: it builds the message, sets MessageType to mtTemplate, copies your template into it and sends it. Because it copies, the template you pass stays yours to free.

procedure TForm1.SendOrderConfirmation(const ANumber, AOrderID: string);
var
  Template: TTMSFNCCloudWhatsAppTemplate;
  Header, Body, Button: TTMSFNCCloudWhatsAppTemplateComponent;
begin
  TMSFNCCloudWhatsApp1.OnSendSuccess := WhatsAppSent;
  TMSFNCCloudWhatsApp1.OnError := WhatsAppError;

  Template := TTMSFNCCloudWhatsAppTemplate.Create;
  try
    // Name and LanguageCode must match a template approved in the WhatsApp
    // Business account; the placeholders are filled in below, in order.
    Template.Name := 'order_confirmation';
    Template.LanguageCode := 'en_US';

    // A header component carries at most one parameter, often an image.
    Header := Template.Components.AddComponent(tctHeader);
    Header.Parameters.AddMedia(tptImage, '', 'https://example.com/logo.png');

    // Body parameters are substituted for {{1}}, {{2}}, ... in order.
    Body := Template.Components.AddComponent(tctBody);
    Body.Parameters.AddText(AOrderID);
    Body.Parameters.AddCurrency('EUR', 49900, 'EUR 49.90');
    Body.Parameters.AddDateTime('March 3, 2026');

    // A button component needs the zero-based index of the button it fills.
    Button := Template.Components.AddComponent(tctButton, 0);
    Button.Parameters.AddPayload('TRACK_' + AOrderID);

    // SendTemplate builds the mtTemplate message itself and copies the
    // template into it, so the local Template stays yours to free.
    TMSFNCCloudWhatsApp1.SendTemplate(ANumber, Template);
  finally
    Template.Free;
  end;
end;

Combining a template with a threaded follow-up

SendTemplate cannot set every message property, so when you need one it does not cover — ReplyToID, for instance — set MessageType := mtTemplate on a message you build yourself and fill its own Template object. Delivering a template also opens the conversation window, so a free-form text message chained from OnSendSuccess is then accepted:

{ FTemplateReplyNumber is a private string field on the form, carrying the
  recipient from the template send to the follow-up. }
procedure TForm1.ReplyWithTemplate(const ANumber, AInboundMessageID: string);
var
  Msg: TTMSFNCCloudWhatsAppMessage;
begin
  TMSFNCCloudWhatsApp1.OnSendSuccess := TemplateReplySent;
  TMSFNCCloudWhatsApp1.OnError := WhatsAppError;

  // SendTemplate cannot set ReplyToID, so build the message by hand: set
  // MessageType to mtTemplate and fill the message's own Template object.
  Msg := TTMSFNCCloudWhatsAppMessage.Create;
  Msg.SendTo := ANumber;
  Msg.MessageType := mtTemplate;
  Msg.ReplyToID := AInboundMessageID;
  Msg.Template.Name := 'ticket_received';
  Msg.Template.LanguageCode := 'en_US';
  Msg.Template.Components.AddComponent(tctBody).Parameters.AddText('24 hours');

  FTemplateReplyNumber := ANumber;
  TMSFNCCloudWhatsApp1.SendWhatsAppMessage(Msg);
end;

procedure TForm1.TemplateReplySent(Sender: TObject; AMessage: TTMSFNCCloudWhatsAppMessage);
var
  FollowUp: TTMSFNCCloudWhatsAppMessage;
begin
  // Only chain after the template landed, and only once - clear the handler
  // so the follow-up's own success does not start another round.
  if AMessage.MessageType <> mtTemplate then
    Exit;

  TMSFNCCloudWhatsApp1.OnSendSuccess := WhatsAppSent;

  // The template opened the conversation window, so a free-form text message
  // is now allowed - sent as a reply to the template we just delivered.
  FollowUp := TTMSFNCCloudWhatsAppMessage.Create;
  FollowUp.SendTo := FTemplateReplyNumber;
  FollowUp.MessageType := mtText;
  FollowUp.ReplyToID := AMessage.ID;
  FollowUp.Text.Body := 'Reply to this message to add details to your ticket.';
  TMSFNCCloudWhatsApp1.SendWhatsAppMessage(FollowUp);
end;

Common mistakes

  • Freeing a sent message. The component owns it after SendMessage / SendWhatsAppMessage. A template passed to SendTemplate is the opposite — that one is copied, so you must free it.
  • Sending free-form text outside the 24-hour window. It is rejected; send an approved template first.
  • Mismatched template parameters. The name, language code, component set and parameter order and types must match the approved template exactly, or the service rejects the message — the failure arrives in OnError, not as an exception.
  • Setting a payload amount in the wrong unit. AddCurrency takes thousandths of the currency unit, not the amount itself.
  • Forgetting PhoneNumberID. Without it the request path is incomplete and every send fails, even with a valid token.
  • Re-entering OnSendSuccess. Sending from that handler makes it fire again for the new message; guard on MessageType or ID, or swap the handler, to avoid a loop.

See also