Zapier CRM Series
Zapier CRM Series · 05 / 07Live-testedRun on real accounts with sample data

Weekly Pipeline Report

Every Monday at 9 AM this Zap pulls every open deal from Zoho CRM, totals it by stage, logs a dated snapshot to Google Sheets and posts one summary to Slack, including the weighted pipeline.

TriggerSchedule, Mondays 9:00 AM (UTC+8)
Steps7
ToolsZoho API · Code (JS) · Looping · Filter · Sheets · Slack
TestedSep 15, 2026
All 7 builds
The Weekly Pipeline Report Zap in the Zapier editor
The Zap itself. Click to see all of it.

“How much of the pipeline is real?”

Every week someone opens the CRM, filters open deals, copies them to a spreadsheet, groups and totals them, then types a summary into Slack. It gets skipped on busy weeks, and each week overwrites the last, so there’s no history.

Raw pipeline also overstates things. A deal that just entered Qualification at 10% likely isn’t worth the same as one in Negotiation at 90%. Weighted value is amount × stage probability.

Raw versus weighted, by stage

StageProbabilityDealsRaw ₱Weighted ₱
Qualification10%342,3004,230
Needs Analysis20%330,9506,190
Value Proposition40%346,45018,580
Identify Decision Makers60%355,65033,390
Proposal / Price Quote75%366,40049,800
Negotiation / Review90%370,95063,855
Total56%18312,700176,045
Zoho stage probability mapping
The percentages come from Zoho’s stage-probability mapping.

Seven steps, one loop

1ScheduleMon 9 AMThe report arrives on its own before the weekly sales meeting.
2Zoho API Request (Beta)GET /DealsReturns up to 200 deals in one call; the native search returns one.
3Code by ZapierJavaScriptGroups by stage, drops closed deals, computes count, raw and weighted totals, outputs parallel arrays.
4Looping7 passesTurns the arrays into one pass per row.
5Google SheetsInside loopAppends one dated row per pass, building week-by-week history.
6FilterLast pass only“Loop Iteration Is Last” lets exactly one pass through.
7Slack#pipeline-reportOne summary message, not seven.

Step 3 · Code by Zapier · JavaScript

const deals = JSON.parse(inputData.dealsJson).data || [];
const openStages = ["Qualification", "Needs Analysis", "Value Proposition",
  "Identify Decision Makers", "Proposal/Price Quote", "Negotiation/Review"];
const today = new Date().toISOString().split('T')[0];
const summary = {};
openStages.forEach(s => summary[s] = { count: 0, total: 0, weighted: 0 });
deals.forEach(d => {
  if (!summary[d.Stage]) return;            // skips Closed Won / Closed Lost
  const amt = parseFloat(d.Amount) || 0;
  const prob = parseFloat(d.Probability) || 0;
  summary[d.Stage].count += 1;
  summary[d.Stage].total += amt;
  summary[d.Stage].weighted += amt * (prob / 100);
});
// parallel arrays: index i of every array = one sheet row
const dates = [], stages = [], counts = [], totals = [], weighted = [];
let gC = 0, gT = 0, gW = 0;
openStages.forEach(s => {
  const v = summary[s];
  dates.push(today); stages.push(s); counts.push(v.count);
  totals.push(v.total.toFixed(2)); weighted.push(v.weighted.toFixed(2));
  gC += v.count; gT += v.total; gW += v.weighted;
});
dates.push(today); stages.push("TOTAL"); counts.push(gC);
totals.push(gT.toFixed(2)); weighted.push(gW.toFixed(2));
return { dates, stages, deal_counts: counts, total_values: totals,
  weighted_values: weighted, grand_count: gC,
  grand_total: gT.toFixed(2), grand_weighted: gW.toFixed(2) };
Built Zap canvas
The built Zap. Steps 4–7 sit inside the loop.

7 rows written, 1 message sent

Eighteen open deals, three per stage, with uneven amounts, plus two Closed Won deals the report had to ignore. Every number was checked by hand before the run.

Google Sheet report log
Sheets: seven dated rows per run.
Slack summary message
Slack: one summary, fired on the last pass.
Zap history
Zap History: passes 1–6 filtered, pass 7 continued to Slack.

The first build didn’t work

Seven rows came out as one

The Code step returned seven objects to “Create Multiple Rows”, but Code hands off one bundle per run, so only Qualification landed. Parallel arrays into Looping, with a single-row action inside the loop, fixed it.

The Code step plates seven dishes on one tray; Looping serves them one at a time.

Row count 1
Before: row count 1.
Sheet with one row
Before: one row instead of seven.

Two fields with almost the same name

Inside the loop I first mapped “Preview Loop Values”, which holds the whole list. The plainly named field holds the current pass’s value.

Mapped to whole list
Before: mapped to the whole list.
Mapped to per-pass value
After: one value per pass.

“Last” versus “Is Last”

The first filter used Loop Iteration Last, which is the count (7). Any non-zero number reads as true, so all seven passes would have posted. The test output showed 7, not true or false.

A green test result can still be the wrong result.

Wrong field passes
Before: “would have continued” with value 7.
Right field blocks
After: Is Last = false, correctly blocked.

Old test data would have wrecked the totals

Zoho showed 26 deals instead of 20. Leftovers from an earlier project carried amounts up to ₱1,075,000, more than the whole real pipeline. Identified by naming pattern and deleted before building.

No “run now” for a weekly schedule

To prove the full seven-pass run, I moved the schedule a few minutes ahead, watched it fire, then set it back to Monday.

What I’d change for production

Task usage

Each loop pass counts as tasks, so a run costs about 7× a single pass.

200-deal ceiling

One API call returns 200 deals. A bigger pipeline needs pagination.

Hard-coded stages

A new Zoho stage must also be added to the code, or its deals are skipped.

Cost

₱0 to build on trials and free tiers; roughly $49/month on a paid Zapier plan to run.

TaskFlow and every company and person shown are fictional mock data. Built on Zoho CRM Free, Slack and a Zapier Professional trial; the Zaps are switched off now the trial has ended, and every screenshot comes from the real Zapier, Zoho and Slack accounts.

All work

Hiring, or have a process to automate?

Let’s talk.

Send a few lines about the role, or what keeps slipping through.

Write to me

ralphmatthewmarty@gmail.com

Or message me

WhatsApp +63 919 947 0840

Or connect

LinkedIn

Replies within one business day · UTC+8

Good to know

Based in

Central Luzon, PH (UTC+8)

Looking for

Full-time remote roles; contract too

Works

Remote; hybrid or on-site in the Philippines