Small businesses rarely look at dashboards. They do look at Google Sheets. So if you build or review a WhatsApp automation for a shop, clinic or caterer, log every automated action to a sheet the owner already opens.
It is also a fair question to put to any agency you are vetting as the best AI automation agency in Mumbai for small businesses that live in spreadsheets: "Where can I read what the system did yesterday?"
The columns
| time_ist | direction | phone_masked | intent | action | approved_by | note |
|---|
Keep it to seven columns. Owners stop reading wide sheets.
Appending a row (Python, gspread)
import gspread
from datetime import datetime
from zoneinfo import ZoneInfo
gc = gspread.service_account(filename='sa.json')
ws = gc.open('WhatsApp Log').worksheet('log')
def mask(phone: str) -> str:
return phone[:3] + 'XXXXXX' + phone[-4:]
def log(direction, phone, intent, action, approved_by='', note=''):
ts = datetime.now(ZoneInfo('Asia/Kolkata')).strftime('%Y-%m-%d %H:%M')
ws.append_row([ts, direction, mask(phone), intent, action, approved_by, note],
value_input_option='USER_ENTERED')
Rules that keep it useful
- Mask phone numbers. The sheet will be shared with staff.
- One row per event, not per conversation.
- Write the approver's name whenever a person approved something.
- Never log message bodies with payment or health details. Log the intent ("payment_query") instead.
- Rotate monthly. A new tab each month keeps the sheet fast.
A daily summary from the same sheet
A small scheduled script can count yesterday's rows by action and send the owner one WhatsApp or email line: how many replies were drafted, sent, handed to a person, or failed. That one line is often the only monitoring a small team will actually read.
Why this matters
When something goes wrong, the first question is always "what happened?" A readable log turns an argument into a lookup. If you are earlier in the process, the build-or-buy chatbot comparison helps decide what to build at all.
I run NxFlowAI (Mumbai). Code is illustrative.
Top comments (1)
Masking the numbers and logging the intent instead of the message body are the right defaults. One setting in the snippet works against them.
value_input_option='USER_ENTERED'makes Sheets parse every cell as if a person typed it. A masked number like +91XXXXXX1234 starts with a plus sign, which Sheets can read as the start of a formula, so that column may fill with parse errors. Thenotecolumn carries the bigger risk: any text that begins with = is evaluated as a formula, in a sheet the owner and staff open every day. I would switch to RAW and write the timestamp as a plain ISO string. The other option is to keep USER_ENTERED and prefix the text columns with an apostrophe. Separately, the monthly rotation rule and the code disagree, becauseworksheet('log')always writes to the same tab. The fix is to build the tab name from the same IST timestamp, for example 2026-10, and create the tab when gspread raisesWorksheetNotFound. Then the rotation actually happens.