<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Rendezo blog</title>
    <link>https://rendezo.app/blog/</link>
    <description>Practical guides on WhatsApp appointment booking and the Meta Cloud API.</description>
    <language>en</language>
    <lastBuildDate>Wed, 12 Aug 2026 09:00:00 +0000</lastBuildDate>
    <atom:link href="https://rendezo.app/feed.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>What WhatsApp appointment booking actually costs in 2026</title>
      <link>https://rendezo.app/blog/whatsapp-cloud-api-appointment-booking-cost/</link>
      <guid isPermaLink="true">https://rendezo.app/blog/whatsapp-cloud-api-appointment-booking-cost/</guid>
      <pubDate>Wed, 12 Aug 2026 09:00:00 +0000</pubDate>
      <description>A plain breakdown of Meta Cloud API pricing for appointment booking: the free 24 hour service window, which reminders are billed, and where the real cost sits.</description>
      <content:encoded><![CDATA[<p>Most businesses looking at WhatsApp booking assume the worst: that every message
their customers send will show up on a bill. That is not how the WhatsApp Cloud
API works, and the difference matters. For a typical appointment business, the
entire booking conversation is free, and the only line item is a handful of
reminders sent outside a specific window.</p>
<p>Here is the model, the parts that get billed, and how to estimate your own
number before you commit to anything.</p>
<h2>Meta bills per message, not per conversation</h2>
<p>Until mid 2025, WhatsApp Business pricing was built around 24 hour
conversations. That model is gone. Since 1 July 2025 Meta charges <strong>per
template message delivered</strong>, and templates fall into three categories:
marketing, utility and authentication.</p>
<p>Ordinary replies are not templates and are not in that list. They are service
messages, and they are governed by the rule below, which is the one that
matters most for booking.</p>
<h2>The 24 hour customer service window is free</h2>
<p>When a customer sends a message to your business number, a <strong>24 hour service
window</strong> opens. Inside that window you can reply freely, as many times as the
conversation needs, at no per message charge.</p>
<p>Appointment booking lives almost entirely inside this window. Think about how
the conversation actually starts:</p>
<blockquote>
<p><strong>Customer:</strong> hi, do you have anything friday afternoon for a cut and colour?</p>
<p><strong>Business:</strong> Friday I have 2:00 pm and 4:30 pm free. Which one works?</p>
<p><strong>Customer:</strong> 4:30 please</p>
<p><strong>Business:</strong> Booked, Friday 4:30 pm, cut and colour. See you then.</p>
</blockquote>
<p>The customer opened the conversation. Every reply after that is a service
message inside the free window. A ten message back and forth about
availability, a reschedule request two days later, a cancellation, a question
about parking: all of it starts with the customer writing to you, and all of it
is free.</p>
<p>This is the single most important fact about WhatsApp booking economics, and it
is the one most comparison articles bury.</p>
<h2>What you do pay for: messages you start</h2>
<p>You pay when <strong>your business</strong> starts the conversation outside that window.
For an appointment business, that is essentially one thing: the reminder you
send the day before, when the customer has not written to you in the last 24
hours.</p>
<p>Those are template messages, and they must be pre-approved by Meta. For
reminders, the correct category is <strong>utility</strong>. Utility rates are the cheapest
of the billable categories and vary by country. In many markets they land
around one to a few US cents per message, but the spread between countries is
wide enough that you should not plan on a single global figure. Meta publishes
current per country rates on its
<a href="https://developers.facebook.com/docs/whatsapp/pricing/">WhatsApp pricing page</a>,
and it is the only number worth quoting.</p>
<p>One more rule works in your favour: under Meta's current pricing, a utility
template sent while the 24 hour service window is still open is not charged at
all. So a reminder for an appointment the customer messaged you about this
morning usually costs nothing, and only the ones sent into silence are billed.</p>
<h3>Do the arithmetic on your own volume</h3>
<p>Take your monthly appointment count, and assume the worst case: every single
one needs a reminder sent from a cold start. A clinic with 400 appointments a
month, at a utility rate of roughly one to two cents, is looking at a few
dollars a month in Meta fees. Not a few hundred. A few.</p>
<p>The costs that dwarf that line are the ones people forget to count: the AI
model you use to understand the messages, and the hosting the software runs on.</p>
<h2>Keep reminders in the utility category</h2>
<p>One expensive mistake is worth naming, because it is easy to make and it does
not announce itself.</p>
<p>If you write a reminder template that also contains a promotion, &quot;Reminder:
your appointment is tomorrow at 3 pm. Also, 20% off all treatments this week&quot;,
Meta can reclassify that template as <strong>marketing</strong>. Marketing rates are
substantially higher than utility rates, and since 2025 the reclassification is
automatic. You do not get a warning email. You get a bigger invoice.</p>
<p>The discipline is simple: reminder templates say only what the reminder needs
to say. Promotions get their own template, sent deliberately, with the
marketing cost understood up front.</p>
<h2>The costs that actually add up</h2>
<p>Once you accept that the Meta bill for booking is small, three real costs are
left:</p>
<table>
<thead>
<tr>
<th>Cost</th>
<th>Typical shape</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>AI model usage</td>
<td>Per message understood and answered</td>
<td>Depends on your provider and model. A short booking conversation is a small number of tokens.</td>
</tr>
<tr>
<td>Hosting</td>
<td>Monthly</td>
<td>Shared hosting is enough for a single location business.</td>
</tr>
<tr>
<td>The software itself</td>
<td>One time or subscription</td>
<td>This is where the range is enormous.</td>
</tr>
</tbody>
</table>
<p>That last row is where most of the money is decided, and it is the row you have
to check yourself rather than take from an article. Hosted booking services
price in very different ways: some charge a flat monthly fee, some charge per
location, and some add a margin on top of Meta's rates or on the AI usage they
run for you. Over a few years that recurring line is often larger than every
other cost combined, but the only way to know is to read the specific pricing
page in front of you.</p>
<p>A self-hosted script inverts the shape: you pay once, you run it on your own
hosting, you use your own API keys, and the per message costs you pay are the
raw ones, with no markup in between.</p>
<h2>How to sanity check any vendor's pricing</h2>
<p>If you are comparing options, three questions separate a straight answer from a
marked up one:</p>
<ol>
<li><strong>Do you charge per conversation, on top of Meta's fees?</strong> If yes, ask what
the markup is. If the answer is vague, that is the answer.</li>
<li><strong>Whose AI API key is used?</strong> If it is theirs, you are paying their margin on
every message, and you cannot see the underlying usage.</li>
<li><strong>Who holds the customer data?</strong> Appointment histories and phone numbers are
your business's asset. Hosted platforms hold them on your behalf.</li>
</ol>
<p>None of those questions are hostile. They just move the real cost from the
pricing page to something you can calculate.</p>
<h2>In short</h2>
<ul>
<li>The booking conversation itself is free, because the customer starts it.</li>
<li>You pay for reminders sent outside the 24 hour window, at utility rates,
usually cents.</li>
<li>Never mix promotions into a reminder template, it gets reclassified as
marketing and costs more.</li>
<li>Meta's fees are rarely the number that matters. The software's pricing model
usually is.</li>
</ul>
<p>Rates change and differ by country, so treat every figure here as a shape
rather than a quote, and check
<a href="https://developers.facebook.com/docs/whatsapp/pricing/">Meta's current pricing</a>
for your own market before you budget.</p>

<hr>
<p><em>Originally published on <a href="https://rendezo.app/blog/whatsapp-cloud-api-appointment-booking-cost/">the Rendezo blog</a>. Rendezo is a self-hosted script that turns a business WhatsApp number into an AI appointment assistant: <a href="https://rendezo.app/?utm_source=syndication">how it works</a> or <a href="https://demo.rendezo.app/admin">try the live demo</a>.</em></p>]]></content:encoded>
    </item>
    <item>
      <title>Connecting a business number to the WhatsApp Cloud API</title>
      <link>https://rendezo.app/blog/connect-business-number-whatsapp-cloud-api/</link>
      <guid isPermaLink="true">https://rendezo.app/blog/connect-business-number-whatsapp-cloud-api/</guid>
      <pubDate>Wed, 12 Aug 2026 09:00:00 +0000</pubDate>
      <description>The real steps to put a business number on the official WhatsApp Cloud API, plus the traps that silently break webhooks and cost hours.</description>
      <content:encoded><![CDATA[<p>Every WhatsApp booking tool, ours included, sits on top of the same thing: the
official Meta WhatsApp Cloud API. Before any assistant can answer a customer,
someone has to connect a number to it. The Meta documentation covers each
screen, but it does not tell you which parts quietly fail and leave you staring
at a webhook that never fires.</p>
<p>This is the honest version, written after doing it and getting stuck.</p>
<h2>What you need before you start</h2>
<ul>
<li>A <strong>Facebook Business account</strong>, or the willingness to create one.</li>
<li>A <strong>phone number that is not currently active on the WhatsApp app</strong>. This is
the step most people get wrong. A number already registered on regular
WhatsApp or WhatsApp Business app has to be deleted from that app first, and
you lose that chat history. Use a fresh number if you can.</li>
<li>A server with <strong>HTTPS</strong>. Meta will not send webhooks to plain HTTP, and
self-signed certificates are rejected.</li>
</ul>
<p>Nothing here needs a big server. Shared hosting with a valid certificate is
enough for a single location business.</p>
<h2>The shape of the setup</h2>
<p>Meta renames its buttons regularly, so treat the labels below as landmarks
rather than exact text. The shape has been stable for years even as the wording
moved around.</p>
<ol>
<li><strong>Create a Meta app</strong> at developers.facebook.com, of the Business type, and
add the WhatsApp product to it.</li>
<li>Meta gives you a <strong>test number</strong> immediately. It can only message up to five
recipients that you add and verify by hand, but that is enough to build and
check everything before your real number is involved.</li>
<li>Note two identifiers from that screen, you will need both: the
<strong>Phone number ID</strong> and the <strong>WhatsApp Business Account (WABA) ID</strong>. They
are different numbers and mixing them up produces confusing API errors.</li>
<li>Generate an <strong>access token</strong>. The one on this screen is temporary, see the
warning below.</li>
<li>Configure the <strong>webhook</strong>: a callback URL on your server and a verify token
you invent yourself. Meta immediately calls your URL with that token and
expects it echoed back. If your endpoint is not live yet, this step fails.</li>
<li><strong>Subscribe to the messages field.</strong> An unsubscribed webhook is a webhook
that never fires.</li>
<li>When everything works on the test number, <strong>add your real business number</strong>
and verify it.</li>
</ol>
<h2>The traps</h2>
<p>These are the ones that cost real time. None of them produce a useful error
message.</p>
<h3>The test token expires in 24 hours</h3>
<p>The access token on the setup screen is a short lived one. It works perfectly,
you build your whole integration on it, and the next morning every call fails
with an authentication error that reads like your credentials are wrong.</p>
<p>They are not wrong. They are expired. For anything permanent you need a token
generated from a <strong>System User</strong> in Business Settings, which does not expire on
a timer. Do the test with the temporary token, then switch before you rely on
it.</p>
<h3>The app can be subscribed to the webhook and still receive nothing</h3>
<p>This is the worst one, because every screen in the Meta panel looks correct.
The webhook is configured, the messages field shows as subscribed, the verify
handshake passed, and messages still never arrive.</p>
<p>The subscription on the app configuration screen is not the same thing as the
app being subscribed to your WhatsApp Business Account. Check it directly:</p>
<pre><code>GET https://graph.facebook.com/v23.0/{WABA_ID}/subscribed_apps
</code></pre>
<p>If your app is not in that list, subscribe it:</p>
<pre><code>POST https://graph.facebook.com/v23.0/{WABA_ID}/subscribed_apps
</code></pre>
<p>(Use whichever Graph API version is current when you read this. The endpoint
has been stable across versions; only the prefix moves.)</p>
<p>Messages start arriving immediately. There is no error state anywhere in the
interface telling you this was missing.</p>
<h3>Verify the webhook signature, and fail closed</h3>
<p>Your callback URL is a public address that anyone can find and post JSON to.
Meta signs every request with an <code>X-Hub-Signature-256</code> header computed from
your app secret. Check it, and reject anything that does not match.</p>
<p>Fail closed, meaning if the signature is missing or malformed, reject. It is
tempting to be lenient during setup while you are debugging. Leniency that
ships is an open endpoint that lets a stranger inject fake customer messages
into your booking system.</p>
<h3>Meta will retry, so handle duplicates</h3>
<p>If your endpoint is slow or returns an error, Meta retries the delivery:
immediately, then with decreasing frequency over the next 36 hours. Store the
WhatsApp message ID and ignore an ID you have already processed. Without that,
one delivery hiccup turns into a customer being answered twice, or worse,
booked twice.</p>
<h3>Templates are the only way to message first</h3>
<p>Inside the 24 hour window after a customer writes to you, you can reply freely.
Outside it, you can only send <strong>pre-approved template messages</strong>. Appointment
reminders are therefore templates, and they must be submitted and approved
before your first reminder can go out. Utility templates are usually approved
quickly, often within minutes, but do not discover this requirement on the
evening you planned to go live.</p>
<p>Keep promotions out of reminder templates. Mixed content gets reclassified into
the more expensive marketing category automatically. We covered that in
<a href="https://rendezo.app/blog/whatsapp-cloud-api-appointment-booking-cost/">what WhatsApp appointment booking actually costs</a>.</p>
<h2>How to test without a real customer</h2>
<p>Two things make this far less painful:</p>
<ul>
<li><strong>Use the test number</strong> for everything until the integration is solid. Add
your own phone as an allowed recipient and message yourself.</li>
<li><strong>Use a tunnel</strong> such as ngrok while developing, so Meta can reach your local
machine over HTTPS. Remember that a free tunnel gets a new URL each restart,
and Meta keeps calling the old one until you update the callback URL. A
webhook that worked yesterday and is silent today is usually this.</li>
</ul>
<h2>Is it worth doing yourself?</h2>
<p>For a developer, the Cloud API is genuinely well built. It is a normal REST
API with normal webhooks, and once the traps above are behind you it is
dependable.</p>
<p>What takes the time is not the connection, it is everything after it: deciding
what to say, checking real availability before confirming anything, preventing
two customers from taking the same slot, storing the conversation, and sending
reminders on schedule.</p>
<p>That gap is exactly what <a href="https://rendezo.app/">Rendezo</a> is. It is a self-hosted script that
handles the Cloud API plumbing, the booking logic and the AI conversation, on
your own server with your own keys. You still do the Meta setup above once,
because that connection belongs to your business and not to a vendor. After
that, the rest is configuration rather than code.</p>

<hr>
<p><em>Originally published on <a href="https://rendezo.app/blog/connect-business-number-whatsapp-cloud-api/">the Rendezo blog</a>. Rendezo is a self-hosted script that turns a business WhatsApp number into an AI appointment assistant: <a href="https://rendezo.app/?utm_source=syndication">how it works</a> or <a href="https://demo.rendezo.app/admin">try the live demo</a>.</em></p>]]></content:encoded>
    </item>
    <item>
      <title>Can you trust an AI to book appointments?</title>
      <link>https://rendezo.app/blog/can-you-trust-an-ai-to-book-appointments/</link>
      <guid isPermaLink="true">https://rendezo.app/blog/can-you-trust-an-ai-to-book-appointments/</guid>
      <pubDate>Wed, 12 Aug 2026 09:00:00 +0000</pubDate>
      <description>Three ways an AI booking assistant fails in production, what we measured when we hit them, and which of the fixes are guarantees rather than good intentions.</description>
      <content:encoded><![CDATA[<p>An AI that answers WhatsApp messages is easy to demo and hard to trust. The
demo always works, because you ask it the question you had in mind when you
built it. Trust is what happens on the message you did not anticipate, on a
Friday evening, when a real customer's real appointment is on the line.</p>
<p>We build a WhatsApp booking assistant, so we have hit these failures on live
numbers rather than in theory. Here are three that matter, with what we
measured, and the design rules that came out of them.</p>
<h2>Failure 1: it says it did something it never did</h2>
<p>The customer asks to cancel. The assistant asks for confirmation. The customer
confirms. The assistant replies, warmly and clearly, that the appointment has
been cancelled.</p>
<p>It never called the cancel function. Nothing was cancelled. The appointment is
still there, the customer believes it is gone, and nobody finds out until
someone does not show up.</p>
<p>We saw this three separate times in one evening's live testing in July 2026,
with a smaller and cheaper model tier. The same four scenarios were then run
against a mid tier model, which passed all four on the first attempt. Model
versions change quickly and any specific comparison ages, so the lesson worth
keeping is not the name of a model. It is this: <strong>for an assistant that takes
actions, instruction following is worth more than a lower price per message.</strong>
The cheap model is not cheaper if one missed cancellation costs an appointment
slot and a customer's trust.</p>
<p>The structural defence matters more than the model choice:</p>
<blockquote>
<p>The AI never writes to the database. It can only call a small set of
functions, and deterministic code decides whether each one is allowed to
proceed.</p>
</blockquote>
<p>If the model does not call the function, nothing happens in the system, which
is bad. But the reverse, a model quietly writing a wrong record, is worse and
is ruled out entirely. Every state change in the system has a log line with the
tool call that produced it. &quot;It said it cancelled&quot; is never evidence, the log
is.</p>
<h2>Failure 2: it forgets what it just did</h2>
<p>This one is subtle and it nearly stopped our launch.</p>
<p>A customer books an appointment. The assistant confirms it correctly. Then the
customer sends the most common last message in any booking conversation:
&quot;thanks&quot;. The assistant re-checks availability out of reflex, sees that the
slot the customer just took is no longer free, concludes it is unavailable, and
tells the customer their appointment did not go through.</p>
<p>A perfect booking, undone by a thank you.</p>
<p>The cause was not the prompt. We tested that. The model was not being told to
persist proof of what it had done: the machine readable result of the booking
call was held only in memory during that one exchange, and never carried into
the next turn. On the next message, the model saw its own earlier sentence
claiming a booking, but no evidence, and it went to verify.</p>
<p>We ran an isolation experiment, one variable at a time, twenty runs per
condition:</p>
<table>
<thead>
<tr>
<th>Condition</th>
<th>Wrong &quot;your booking is gone&quot; replies</th>
</tr>
</thead>
<tbody>
<tr>
<td>Baseline</td>
<td>16 of 20</td>
</tr>
<tr>
<td>Prompt rule reworded</td>
<td>15 of 20</td>
</tr>
<tr>
<td>Booking result carried into history</td>
<td><strong>0 of 20</strong></td>
</tr>
</tbody>
</table>
<p>Rewording the instruction changed nothing. Carrying the actual result of the
action into the conversation removed the failure completely.</p>
<p>The lesson generalises well beyond our product. <strong>When an AI assistant appears
to have a memory problem, look at what state you are actually giving it before
you rewrite the prompt.</strong> Prompt wording is where these bugs are usually
treated and rarely cured.</p>
<p>There is a second distinction hiding in that fix, and it is worth stating,
because getting it backwards creates a new bug. Results of questions, such as
&quot;which slots are free&quot;, are snapshots that go stale within minutes and must not
be replayed later. Results of actions, such as &quot;this appointment was booked&quot;,
are facts that stay true. Only the second kind belongs in the assistant's
long term view of the conversation.</p>
<h2>Failure 3: two customers, one slot</h2>
<p>Two people ask for Friday at 5pm within the same second. Both conversations
check availability, both see the slot free, both confirm. One customer arrives
to a chair that is taken.</p>
<p>This has nothing to do with AI. It is an ordinary race condition, and it is
solved the ordinary way: the availability check and the write happen inside a
single database transaction with the relevant rows locked, so the second
request finds the slot taken and is refused. The assistant then does what a
receptionist does, apologises and offers the nearest alternative.</p>
<p>The reason to mention it in an article about AI is that it is easy to assume
the model handles this. It cannot. A language model has no view of what another
conversation did a millisecond ago. Anything that must be true for every
customer simultaneously has to live in the database layer, not in the prompt.</p>
<h2>What &quot;trustworthy&quot; actually means here</h2>
<p>After all of the above, our working definition is narrow on purpose, and it
separates two things that are easy to blur: what the system makes impossible,
and what it merely makes unlikely.</p>
<p><strong>Guaranteed by the code, not by the model:</strong></p>
<ul>
<li>It <strong>cannot</strong> create, move or cancel an appointment that the database did not
accept. Every write goes through a function call, and deterministic code
decides whether it proceeds.</li>
<li>It <strong>cannot</strong> double book, even under simultaneous requests, because the
check and the write happen inside one locked transaction.</li>
<li>Every action is <strong>visible in a log</strong> with the exact call that caused it, so a
disputed appointment has an answer that does not depend on anyone's memory.</li>
</ul>
<p><strong>Steered, measured, but not guaranteed:</strong></p>
<ul>
<li>Services, prices and available times are handed to the model from your data,
and it is instructed to use nothing else. That makes an invented price
unlikely and it is what we test for, but no prompt makes a sentence
impossible. The guarantee is on the booking, not on every word of the chat.</li>
<li>It hands the conversation to a human when the customer asks, and when it hits
a case outside what it is allowed to do. It cannot recognise every situation
where a person would have been better.</li>
</ul>
<p>The first list is the one that matters. A trustworthy assistant is not a
cleverer model, it is a model with a smaller blast radius. Be suspicious of any
vendor whose two lists are the same list.</p>
<h2>What it still gets wrong</h2>
<p>Being honest cuts both ways, so here is the current state rather than the
marketing version.</p>
<p>Our assistant occasionally asks one confirmation question more than it needed
to, when the customer's intent was already clear. It errs toward caution. We
have chosen to leave it that way, because the failure mode of an extra question
is a slightly longer conversation, and the failure mode of less caution is a
wrong booking.</p>
<p>It also handles a customer who changes their mind mid sentence less gracefully
than a good receptionist. Real humans are better at conversation. The assistant
wins on being awake at 11pm.</p>
<h2>The takeaway</h2>
<p>Do not evaluate a booking assistant by how well it chats. Evaluate it by what
it is structurally incapable of doing wrong, and by whether the vendor can show
you a log of what it did.</p>
<p>If you want to see one running, <a href="https://rendezo.app/">Rendezo</a> is the self hosted script these
rules came out of, and there is a <a href="https://demo.rendezo.app/admin">live demo</a>
of the admin panel where you can read the conversations and the appointments
they produced. The tool calls themselves are written to the application log on
your own server, which is the point: on a self hosted install, the record of
what the AI did belongs to you and not to a vendor's dashboard.</p>

<hr>
<p><em>Originally published on <a href="https://rendezo.app/blog/can-you-trust-an-ai-to-book-appointments/">the Rendezo blog</a>. Rendezo is a self-hosted script that turns a business WhatsApp number into an AI appointment assistant: <a href="https://rendezo.app/?utm_source=syndication">how it works</a> or <a href="https://demo.rendezo.app/admin">try the live demo</a>.</em></p>]]></content:encoded>
    </item>
  </channel>
</rss>
