# Draft, review, and send WhatsApp outreach from one screen

> Pick your audience from HubSpot, let a background assistant write each WhatsApp message, then approve every one before it sends through Freshchat.

- Workflow type: app
- Services: Freshchat, HubSpot
- Categories: Marketing, Customer Support
- Published: 2026-08-24

## What it does

- Build the audience: choose a group of HubSpot contacts, such as everyone in a lifecycle stage or anyone with a deal closed recently, and see each person matched to their chat history.
- See when you last spoke: every row shows the person's last chat and how long ago it happened, and anyone who has no chat record yet can be added in one click so they can be messaged.
- Draft with help, approve by hand: a background assistant reads each person's recent chat and CRM details and writes a personalized message, then you edit, reject, or approve every row before anything goes out.
- Keep the record: approved messages send over WhatsApp, a note lands on the HubSpot contact, and a sent log tab shows who was messaged, when, and who replied.

## What you'll need

- A Freshchat account with WhatsApp messaging enabled
- At least one WhatsApp message template that has already been approved, with its variables ready to fill
- A HubSpot login with access to your contacts and notes
- Phone numbers on your HubSpot contact records, so people can be reached on WhatsApp

## Prompt

Build me an app that my growth and support team opens whenever we run a WhatsApp outreach push through Freshchat. It has two tabs: an audience builder where I assemble the list, review drafts and send, and a sent log where I see what went out and who replied. Nothing runs on a schedule, a person opens the app and drives the push.

Audience builder tab. At the top I name the push and define the audience by picking a HubSpot contact search, for example customers in a given lifecycle stage, or contacts with a deal closed in the last 30 days. A handler runs HubSpot Search Contacts with those filters and pulls the fields I care about: first name, last name, email, phone, lifecycle stage, company, deal or close information, and owner. Show the results as a table, one row per contact.

Matching to Freshchat. For every contact in the audience, match to their Freshchat record with List Users, keyed on email and then phone. Important data path nuance: Freshchat has no account wide list-all-conversations operation, so any conversation data has to be assembled by walking users first. For each matched user call List User Conversations, take the most recent conversation, then call List Conversation Messages on that conversation to get the latest message and its timestamp. Each row should show: matched or unmatched, the last chat snippet, how long ago they spoke, and the status of that most recent conversation. Use the list pagination parameters page and items_per_page (message lists default to 20 and cap at 50) and only pull the newest page of messages, do not walk full history. Load the Freshchat enrichment progressively so the HubSpot rows render immediately and each row fills in as its conversation lookup completes.

Creating missing people. Rows with no Freshchat record get an "Add to Freshchat" action that calls Create User with the contact's name, email and phone so they can be messaged, plus a bulk version that does it for every unmatched row in the push. Freshchat create and update calls return 202 Accepted and are processed asynchronously, so the resource does not come back in the response: update the row optimistically, then refetch shortly after to confirm, and show a pending state in between rather than treating a 202 as a failure.

Eligibility rules, enforced in the app and not left to the user. Exclude anyone whose most recent Freshchat conversation is still open or unresolved, so we never talk over a live support thread. Exclude anyone who has already been sent to inside this same push, so nobody is messaged twice. Excluded rows stay visible, greyed out, with the exclusion reason spelled out on the row so I can see exactly who got skipped and why. Re-evaluate both rules at send time as well as at display time, since a conversation can open between building the list and pressing send.

The template. The app holds the configuration for a pre-approved WhatsApp template: its name, its language, and the ordered list of variables it expects, each with a short description of what belongs in it. Drafting fills those variables, it never produces free text, because only approved template content can go out.

"Draft messages" button and the background agent. Pressing it kicks off a background agent over the eligible rows. For each person, the agent reads their recent chat transcript (List User Conversations, then List Conversation Messages on the most recent one) plus that person's HubSpot fields from the audience search, and produces values for the configured template variables in a strict schema, along with a one line reason for the angle it chose. The agent writes each person's result back into the app as it goes, so rows populate live and I can watch progress rather than waiting on one big result at the end.

Review table. The drafts land in a review table showing the person, a rendered preview of the template with their variables substituted, the raw variable values, the agent's reason, and a status of pending, approved or rejected. I can edit any variable value inline (the preview updates as I type), reject a row, or approve it, with bulk approve and bulk reject for a filtered selection. Nothing is sendable until it is approved, and rejected rows are kept for the record rather than deleted.

Sending. A "Send approved" action sends each approved row with Send Outbound WhatsApp Message, passing the template and that person's variable values. Send sequentially with a short delay, watch the X-RateLimit-Remaining header that every Freshchat response carries, slow down as it drops and stop cleanly on a 429 so the remaining rows can be retried afterwards rather than lost. Show live progress with per-row outcome and timestamp, and mark each person as sent for this push the moment the send succeeds so the no-double-message rule holds even if I press send again.

CRM write back. After each successful send, create a note on that person's HubSpot contact with Create Note, associated to the contact id, recording which template went out, the rendered message text, and when it was sent, so the CRM shows the outreach alongside everything else on the record. Show the note status on the row and let me retry a failed note without resending the message.

Sent log tab. Build this from List Outbound Messages: one row per message showing who it went to (resolve the Freshchat user back to a name with Get User or Fetch Users by IDs, and link it to the HubSpot contact where we know it), which push and template it belonged to, when it was sent, its delivery status, and whether the person replied. Work out replies by checking that user's conversations for an inbound message after the send timestamp, again via List User Conversations and List Conversation Messages. Let me filter by push, by date range, and by replied versus no reply, and put a small summary strip at the top with sent, delivered, replied and reply rate.

Persistence. Store pushes, their audience rows, the drafts and their approval state, and the send outcomes in the app, so a push survives a reload, the sent log is accurate without refetching everything, and the never-twice rule is actually enforceable. Record which team member approved and which one sent each row, since growth and support share this app.

## How to customize

- Change the audience: swap the contact filters for any segment you push to, such as a lifecycle stage, a recent purchase, a deal owner, or a signup date range.
- Change the template: point the app at a different approved WhatsApp template and the drafts will fill in that template's variables instead.
- Change the guardrails: set how far back the assistant reads someone's chat history, or add a cooling-off window so anyone messaged in the last few weeks is skipped.

## FAQ

### Do the messages send as soon as they are drafted?

No. Every draft waits in a review table. You can rewrite the wording, reject a row, or approve it, and only approved rows are ever sent.

### Will this interrupt someone who is in the middle of a support conversation?

No. Anyone whose most recent conversation is still open and unresolved is held back automatically and shown greyed out with the reason, so you never message over a live support thread.

### Can the same person get two messages in one push?

No. Once a message goes out, that person is marked as sent for that push and cannot be sent to again, even if you rerun the send or reopen the app later.

### Can I use our own approved WhatsApp template?

Yes, and that is the intended way to use it. Rather than writing free text, the assistant fills in the variables of a template you have already had approved, so every message stays within the wording you signed off on.

### What happens to people who have never chatted with us before?

They show up in the audience with no chat record, and you can add them from the app in one click. Once added they become eligible for the push like everyone else.

### Does this work with a free HubSpot account?

Yes. Contact search and notes are part of the free HubSpot CRM, so the audience building and the write-back both work without a paid tier.

Use this prompt in General Input: https://www.generalinput.com/prompts/draft-review-and-send-whatsapp-outreach-from-one-screen