AI Global Academy Join the waitlist

Courses / Bonus: what else you can build

Lesson 10.5 · 55 minAutomations: work that happens without you

Duration~55 min in the lesson + ~30 min homework
PrerequisitesCheckpoint lesson-8.6 (needs the scheduled job from 5.5, the Telegram bot from 4.5, the ad spend data from 7.8 and the Claude API key from 8.3).
Checkpointlesson-10.5

What you will have

Two jobs run without the owner. Every Monday a scheduled job counts last week's numbers from the database, has Claude write findings from those numbers, stores the report at /admin/reports and emails it. And the Telegram bot answers the command /today with today's lead count, for the owner's chat only.

Video

The video for this lesson is not recorded yet.

Prompts used in this lesson

Part 3 — Building the scheduled report

Prompt to Claude
Purpose: every Monday morning the CRM part of my weekly report should be
ready and in my inbox without my doing anything.

Context: read docs/reports/TEMPLATE.md and PROMPT.md (8.1), docs/crm-spec.md
(stages, business time zone), the dashboard queries from 5.6 and 7.8, the
scheduled job at /api/cron/daily-summary (5.5), the Claude API code from 8.3
and the email and Telegram code from 4.5. CRON_SECRET, ANTHROPIC_API_KEY and
LEAD_AI_MODEL are set; never print or log their values.

Before writing code: read Vercel's current Cron Jobs documentation and the
current Messages API documentation at platform.claude.com/docs. Do not rely
on memory. Tell me which pages you read.

Build:
1. Table weekly_reports: id, created_at, period_start (unique), period_end,
   numbers, findings (may be empty), decisions, status. No anonymous access.
2. One server function that counts, for a Monday-to-Sunday week in the
   business time zone: new leads; leads by utm_source and utm_campaign;
   leads that reached each stage; spend and cost per qualified lead by
   campaign; and the previous week's totals. Reuse the dashboard's
   definitions.
3. Findings: send only that table of numbers (no names, emails, phones or
   messages) to the Claude API with the PROMPT.md rules that apply to CRM
   data; at most five findings, no decisions. On any failure store the
   report with the note "Findings not available".
4. GET /api/cron/weekly-report, protected by CRON_SECRET exactly like the
   5.5 job, for the most recent complete week. If a report for that
   period_start exists it does nothing and says so. Otherwise it counts,
   gets findings, stores the report, emails it to OWNER_EMAIL and sends one
   Telegram line with the lead total and a link. It returns counts only.
5. Schedule it in vercel.json for Mondays at 06:00 UTC; keep existing jobs.
6. /admin/reports lists reports. /admin/reports/<id> shows the numbers with
   counts beside every rate, the findings under "AI findings - check against
   the numbers", a "Decisions" box I can fill and save, and a "Generate for
   last week" button that runs the same function.

Constraints: no personal data in the email, the Telegram line or the API
call. No new environment variables. Do not change the 8.1 files.

Verify before reporting: run the build and tests; call the endpoint locally
without the header (401), with it (one report) and again (no second row,
email or message); run once with ANTHROPIC_API_KEY removed and show
"Findings not available"; print the numbers so I can compare three with the
CRM. Say what you could not verify.

Part 5 — Building the command

Prompt to Claude
Purpose: I want to send my Telegram bot "/today" and get today's numbers from
my CRM. Only I may get an answer.

Context: the bot and sending code from 4.5. TELEGRAM_BOT_TOKEN,
TELEGRAM_CHAT_ID and the new TELEGRAM_WEBHOOK_SECRET are set locally and in
Vercel; never print or log them. "Today" must mean the same as on
/admin/today.

Before writing code: read the current Telegram Bot API documentation for
setWebhook (including secret_token), getWebhookInfo, deleteWebhook, the
Update and Message objects and sendMessage. Tell me what you read.

Build:
1. POST /api/telegram/webhook. If the X-Telegram-Bot-Api-Secret-Token header
   does not equal TELEGRAM_WEBHOOK_SECRET, or the variable is missing,
   return 401 and do nothing.
2. If the message's chat id is not TELEGRAM_CHAT_ID: no database query;
   reply "This bot is private." and return success.
3. For the owner, "/today" replies with new leads today by source and the
   number of tasks due and overdue, with a link to /admin/today. Counts
   only. "/help" and anything else list the commands.
4. Once the header is valid, always return success to Telegram, even if
   replying fails, and log the failure, so the message is not resent.
5. Scripts: "npm run telegram:set-webhook" registers
   https://<production domain>/api/telegram/webhook with the secret token
   for message updates only, then prints getWebhookInfo without secrets;
   "npm run telegram:delete-webhook" removes it. Ask me for the domain.

Constraints: read-only commands; the notification code stays unchanged.

Verify before reporting: run the build; post a sample message update to the
local endpoint without the header (401), with the header and another chat id
(no data), and with the header and my chat id (I should receive the reply);
tell me the counts sent. Say what you could not verify.

Do along

Work on your own project. Pause the video where a step says so.

  1. Pause after Part 3. Run the report prompt in plan mode and apply the migration. Compare three numbers from the printed table with your CRM by hand.
  2. Change the schedule's hour in vercel.json if 06:00 UTC does not suit you. Keep it weekly.
  3. Commit, push, apply the migration to the live database, and trigger the job once on production.
  4. Pause after Part 4. Save a random value of letters and digits as TELEGRAM_WEBHOOK_SECRET in .env.local and in Vercel. Do not paste it into Claude.
  5. Pause after Part 5. Run the bot prompt and check the three local results.
  6. Pause during Part 6. Deploy, run npm run telegram:set-webhook, send /today from your account, then from another.
  7. Do the "Check your work" steps, then tag with the commands under "Recap and next".

Check your work

  1. Open /api/cron/weekly-report in your browser on the live domain. Expected: unauthorized, no email.
  2. Trigger the job on production. Expected: one report in /admin/reports, one email, one Telegram line.
  3. Trigger it again. Expected: nothing new.
  4. Send /today to the bot. Expected: counts that match /admin/today and today's leads.
  5. Send /today from another Telegram account. Expected: "This bot is private."
  6. Ask Claude to post to the live Telegram webhook without the secret header. Expected: 401.

Common problems

  • The report's numbers differ from the dashboard → a second definition of a stage or of the week → "Make the weekly report and the dashboard use one function and show me the rows counted."
  • The bot does not answer on production → the webhook is not registered, or TELEGRAM_WEBHOOK_SECRET was added to Vercel after the last deployment → redeploy, run npm run telegram:set-webhook again and read the last error it prints.
  • npm run telegram:chat-id from 4.5 now fails → expected while a webhook is set → run npm run telegram:delete-webhook, use it, set the webhook again.

Homework

About 30 minutes, plus a look next Monday. No other lesson depends on it.

  1. The first report that arrives by itself. Next Monday, check that the report came without your triggering it, recount two of its numbers by hand, and type one decision into the Decisions box. Done when: one report you did not trigger carries a decision of yours.
  2. One more command. Have Claude add a read-only command for one number you look up most days, with the same three locks and the same local test. Done when: it answers you and not the second account. Commit without a tag.
  3. Your automation list. Create docs/automations.md: every automatic job your site runs, and for each what it does, when, and how you would notice it has stopped. Done when: no row has an empty "how I would notice". Commit without a tag.

Save your work

git add -A
git commit -m "Lesson 10.5: scheduled weekly report and Telegram /today command"
git tag lesson-10.5
git push
git push --tags